From f595fe3b207f4877833754e9798ff3715d887ce3 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Sun, 20 Oct 2019 21:05:50 +0200 Subject: Import opensta_0~20191010git56851ed+dfsg-1.debian.tar.xz [dgit import tarball opensta 0~20191010git56851ed+dfsg-1 opensta_0~20191010git56851ed+dfsg-1.debian.tar.xz] --- changelog | 5 + control | 43 ++++ copyright | 33 +++ gbp.conf | 2 + gitlab-ci.yml | 9 + man/genmanpages.sh | 4 + man/sta.txt | 23 ++ opensta-dev.install | 2 + opensta.examples | 1 + opensta.install | 1 + opensta.manpages | 1 + ...e-for-interactive-input.-This-adds-comman.patch | 283 +++++++++++++++++++++ patches/series | 1 + rules | 51 ++++ source/format | 1 + watch | 6 + 16 files changed, 466 insertions(+) create mode 100644 changelog create mode 100644 control create mode 100644 copyright create mode 100644 gbp.conf create mode 100644 gitlab-ci.yml create mode 100755 man/genmanpages.sh create mode 100644 man/sta.txt create mode 100644 opensta-dev.install create mode 100644 opensta.examples create mode 100644 opensta.install create mode 100644 opensta.manpages create mode 100644 patches/0003-Use-readline-for-interactive-input.-This-adds-comman.patch create mode 100644 patches/series create mode 100755 rules create mode 100644 source/format create mode 100644 watch diff --git a/changelog b/changelog new file mode 100644 index 0000000..573a47b --- /dev/null +++ b/changelog @@ -0,0 +1,5 @@ +opensta (0~20191010git56851ed+dfsg-1) unstable; urgency=low + + * Initial release (Closes: #926099) + + -- Ruben Undheim Sun, 20 Oct 2019 21:05:50 +0200 diff --git a/control b/control new file mode 100644 index 0000000..0f510c1 --- /dev/null +++ b/control @@ -0,0 +1,43 @@ +Source: opensta +Section: electronics +Priority: optional +Maintainer: Debian Electronics Team +Uploaders: Ruben Undheim +Build-Depends: debhelper-compat (= 12), + cmake, + tcl-dev, + swig, + bison, + flex, + libreadline-dev, + zlib1g-dev, + txt2man +Standards-Version: 4.4.1 +Vcs-Browser: https://salsa.debian.org/electronics-team/opensta +Vcs-Git: https://salsa.debian.org/electronics-team/opensta.git +Homepage: https://github.com/The-OpenROAD-Project/OpenSTA + +Package: opensta +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: Gate-level Static Timing Analyzer + After synthesis, place and route of a digital circuit, it is necessary to + verify the timing of the design. OpenSTA is a tool for doing exactly that. It + has a Tcl interface for entering commands for analysing designs. + . + It typically takes as input a verilog netlist, a liberty file, and other + parasitics information from the placed and routed design. + +Package: opensta-dev +Architecture: any +Section: libdevel +Depends: ${misc:Depends} +Description: Gate-level Static Timing Analyzer - development files + After synthesis, place and route of a digital circuit, it is necessary to + verify the timing of the design. OpenSTA is a tool for doing exactly that. It + has a Tcl interface for entering commands for analysing designs. + . + It typically takes as input a verilog netlist, a liberty file, and other + parasitics information from the placed and routed design. + . + This package contains the header files and some libraries for development. diff --git a/copyright b/copyright new file mode 100644 index 0000000..78e2f24 --- /dev/null +++ b/copyright @@ -0,0 +1,33 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: OpenSTA +Source: https://github.com/The-OpenROAD-Project/OpenSTA +Files-Excluded: examples/example1_fast.lib + examples/example1_typ.lib + examples/example1_slow.lib + +Files: * +Copyright: 2018-2019 Parallax Software, Inc. + 2018 Nefelus, Inc. +License: GPL-3+ + +Files: debian/* +Copyright: 2019 Ruben Undheim +License: GPL-3+ + + +License: GPL-3+ + This package 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 package 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 + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". diff --git a/gbp.conf b/gbp.conf new file mode 100644 index 0000000..cec628c --- /dev/null +++ b/gbp.conf @@ -0,0 +1,2 @@ +[DEFAULT] +pristine-tar = True diff --git a/gitlab-ci.yml b/gitlab-ci.yml new file mode 100644 index 0000000..d9acd28 --- /dev/null +++ b/gitlab-ci.yml @@ -0,0 +1,9 @@ +image: registry.salsa.debian.org/salsa-ci-team/ci-image-git-buildpackage:latest + +build: + artifacts: + paths: + - "*.deb" + expire_in: 1 day + script: + - gitlab-ci-git-buildpackage-all diff --git a/man/genmanpages.sh b/man/genmanpages.sh new file mode 100755 index 0000000..d8970d0 --- /dev/null +++ b/man/genmanpages.sh @@ -0,0 +1,4 @@ +#!/bin/bash + + +txt2man -d "${CHANGELOG_DATE}" -t STA -s 1 sta.txt > sta.1 diff --git a/man/sta.txt b/man/sta.txt new file mode 100644 index 0000000..30f1d0e --- /dev/null +++ b/man/sta.txt @@ -0,0 +1,23 @@ +NAME + sta - OpenSTA for static timing analysis of digital circuits + +SYNOPSIS + sta [-help] [-version] [-no_init] [-exit] cmd_file + +DESCRIPTION + OpenSTA can be used to do timing analysis of digital circuits. + +OPTIONS + -help show help and exit + -version show version and exit + -no_init do not read .sta init file + -threads max use count threads + -no_splash do not show the license splash at startup + -exit exit after reading cmd_file + cmd_file source cmd_file + +AUTHOR + This manual page was written by Ruben Undheim for the Debian project (and may be used by others). + + + diff --git a/opensta-dev.install b/opensta-dev.install new file mode 100644 index 0000000..09bcbbd --- /dev/null +++ b/opensta-dev.install @@ -0,0 +1,2 @@ +usr/include/* usr/include/OpenSTA/ +usr/lib/lib*.a diff --git a/opensta.examples b/opensta.examples new file mode 100644 index 0000000..e39721e --- /dev/null +++ b/opensta.examples @@ -0,0 +1 @@ +examples/* diff --git a/opensta.install b/opensta.install new file mode 100644 index 0000000..e772481 --- /dev/null +++ b/opensta.install @@ -0,0 +1 @@ +usr/bin diff --git a/opensta.manpages b/opensta.manpages new file mode 100644 index 0000000..d01d0ee --- /dev/null +++ b/opensta.manpages @@ -0,0 +1 @@ +debian/man/sta.1 diff --git a/patches/0003-Use-readline-for-interactive-input.-This-adds-comman.patch b/patches/0003-Use-readline-for-interactive-input.-This-adds-comman.patch new file mode 100644 index 0000000..406b0d0 --- /dev/null +++ b/patches/0003-Use-readline-for-interactive-input.-This-adds-comman.patch @@ -0,0 +1,283 @@ +From: Ruben Undheim +Date: Sun, 31 Mar 2019 21:34:17 +0200 +Subject: Use readline for interactive input. This adds 'command line + completion', search functionality etc + +--- + CMakeLists.txt | 5 ++ + app/StaMain.cc | 142 ++++++++++++++++++++++++++++++++++++++- + cmake/modules/FindReadline.cmake | 47 +++++++++++++ + 3 files changed, 191 insertions(+), 3 deletions(-) + create mode 100644 cmake/modules/FindReadline.cmake + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 716d486..8b5f237 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -18,6 +18,8 @@ cmake_minimum_required (VERSION 3.9) + + project(STA VERSION 2.0.16) + ++set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules CACHE STRING "CMake module path") ++ + set(CMAKE_VERBOSE_MAKEFILE ON) + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +@@ -562,6 +564,7 @@ get_filename_component(TCL_HEADER_DIR "${TCL_HEADER}" PATH) + + find_package(FLEX) + find_package(BISON) ++find_package(Readline REQUIRED) + + # LibertyExpr scan/parse. + bison_target(LibertyExprParser liberty/LibertyExprParse.yy ${STA_HOME}/liberty/LibertyExprParse.cc +@@ -706,6 +709,8 @@ if (ZLIB_FOUND) + target_link_libraries(sta ${ZLIB_LIBRARIES}) + endif() + ++target_link_libraries(sta ${Readline_LIBRARY}) ++ + message(STATUS "STA executable: ${STA_HOME}/app/sta") + + # g++ std::thread apparently still needs -pthreads. +diff --git a/app/StaMain.cc b/app/StaMain.cc +index 0ee8fde..81eb933 100644 +--- a/app/StaMain.cc ++++ b/app/StaMain.cc +@@ -16,6 +16,8 @@ + + #include + #include ++#include ++#include + #include "Machine.hh" + #include "StringUtil.hh" + #include "Vector.hh" +@@ -26,6 +28,12 @@ namespace sta { + + typedef sta::Vector SwigInitFuncSeq; + ++char **extra_command_completion (const char *, int, int); ++char *extra_command_generator (const char *, int); ++int command_exit (ClientData, Tcl_Interp *, int, Tcl_Obj *const []); ++void save_history(); ++void load_history(); ++ + // "Arguments" passed to staTclAppInit. + static int sta_argc; + static char **sta_argv; +@@ -34,6 +42,8 @@ static SwigInitFunc sta_swig_init; + + static const char *init_filename = "[file join $env(HOME) .sta]"; + ++static bool ended = 0; ++ + void + staMain(Sta *sta, + int argc, +@@ -41,6 +51,10 @@ staMain(Sta *sta, + SwigInitFunc swig_init, + const char *tcl_inits[]) + { ++ char *buffer; ++ Tcl_Interp *myInterp; ++ int status; ++ + initSta(); + + Sta::setSta(sta); +@@ -50,9 +64,31 @@ staMain(Sta *sta, + sta->setThreadCount(thread_count); + + staSetupAppInit(argc, argv, swig_init, tcl_inits); +- // Set argc to 1 so Tcl_Main doesn't source any files. +- // Tcl_Main never returns. +- Tcl_Main(1, argv, staTclAppInit); ++ ++ myInterp = Tcl_CreateInterp(); ++ Tcl_CreateObjCommand(myInterp, "exit", (Tcl_ObjCmdProc*)command_exit, 0, 0); ++ ++ rl_attempted_completion_function = extra_command_completion; ++ ++ staTclAppInit(myInterp); ++ ++ load_history(); ++ ++ while((!ended) && (buffer = readline("OpenSTA> ")) != NULL) { ++ status = Tcl_Eval(myInterp, buffer); ++ if(status != TCL_OK) { ++ fprintf(stderr, "%s\n", Tcl_GetStringResult(myInterp)); ++ } ++ if (buffer[0] != 0) ++ add_history(buffer); ++ free(buffer); ++ if(ended) break; ++ } ++ ++ save_history(); ++ ++ Tcl_DeleteInterp(myInterp); ++ Tcl_Finalize(); + } + + int +@@ -215,6 +251,106 @@ evalTclInit(Tcl_Interp *interp, + delete [] unencoded; + } + ++int command_exit(ClientData, Tcl_Interp *, int, Tcl_Obj *const []) ++{ ++ ended = 1; ++ return 0; ++} ++ ++char const *extra_commands[] = { ++ "all_clocks", ++ "all_inputs", ++ "all_outputs", ++ "all_registers", ++ "check_setup", ++ "create_clock", ++ "create_generated_clock", ++ "create_voltage_area", ++ "current_design", ++ "current_instance", ++ "define_corners", ++ ++ "get_clocks", ++ "get_fanin", ++ "get_fanout", ++ ++ "get_nets", ++ "get_pins", ++ "get_ports", ++ "read_liberty", ++ "read_parasitics", ++ "read_sdc", ++ "read_sdf", ++ "read_spef", ++ "read_verilog", ++ "report_annotated_delay", ++ "report_cell", ++ "report_checks", ++ "report_path", ++ "report_slack", ++ "set_input_delay", ++ "write_sdc", ++ "write_sdf", ++ NULL ++}; ++ ++char **extra_command_completion(const char *text, int, int) ++{ ++ rl_attempted_completion_over = 0; ++ return rl_completion_matches(text, extra_command_generator); ++} ++ ++char *extra_command_generator(const char *text, int state) ++{ ++ static int list_index, len; ++ const char *name; ++ ++ if (!state) { ++ list_index = 0; ++ len = strlen(text); ++ } ++ ++ while ((name = extra_commands[list_index++])) { ++ if(strncmp(name, text, len) == 0) { ++ return strdup(name); ++ } ++ } ++ ++ return NULL; ++} ++ ++void load_history() ++{ ++ FILE *histin = fopen(".history_sta", "r"); ++ if(histin != NULL) { ++ char *line = NULL; ++ size_t len = 0; ++ ssize_t read; ++ while((read = getline(&line, &len, histin)) != -1) { ++ line[strlen(line)-1] = 0; ++ if (line[0] != 0) { ++ add_history(line); ++ } ++ } ++ fclose(histin); ++ } ++} ++ ++void save_history() ++{ ++ printf("Saving command history\n"); ++ HIST_ENTRY **the_list; ++ the_list = history_list(); ++ if(the_list){ ++ FILE *histout = fopen(".history_sta", "w"); ++ for(int i=0; the_list[i] ; i++) { ++ fprintf(histout, "%s\n", the_list[i]->line); ++ } ++ fclose(histout); ++ } ++} ++ ++ + void + showUsage(const char * prog) + { +diff --git a/cmake/modules/FindReadline.cmake b/cmake/modules/FindReadline.cmake +new file mode 100644 +index 0000000..4dcb649 +--- /dev/null ++++ b/cmake/modules/FindReadline.cmake +@@ -0,0 +1,47 @@ ++# - Try to find readline include dirs and libraries ++# ++# Usage of this module as follows: ++# ++# find_package(Readline) ++# ++# Variables used by this module, they can change the default behaviour and need ++# to be set before calling find_package: ++# ++# Readline_ROOT_DIR Set this variable to the root installation of ++# readline if the module has problems finding the ++# proper installation path. ++# ++# Variables defined by this module: ++# ++# READLINE_FOUND System has readline, include and lib dirs found ++# Readline_INCLUDE_PATH The readline include directories. ++# Readline_LIBRARY The readline library. ++ ++find_path(Readline_ROOT_DIR ++ NAMES include/readline/readline.h ++) ++ ++find_path(Readline_INCLUDE_PATH ++ NAMES readline/readline.h ++ HINTS ${Readline_ROOT_DIR}/include ++) ++ ++find_library(Readline_LIBRARY ++ NAMES readline ++ HINTS ${Readline_ROOT_DIR}/lib ++) ++ ++if(Readline_INCLUDE_PATH AND Readline_LIBRARY AND Ncurses_LIBRARY) ++ set(READLINE_FOUND TRUE) ++else(Readline_INCLUDE_PATH AND Readline_LIBRARY AND Ncurses_LIBRARY) ++ FIND_LIBRARY(Readline_LIBRARY NAMES readline) ++ include(FindPackageHandleStandardArgs) ++ FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_PATH Readline_LIBRARY ) ++ MARK_AS_ADVANCED(Readline_INCLUDE_PATH Readline_LIBRARY) ++endif(Readline_INCLUDE_PATH AND Readline_LIBRARY AND Ncurses_LIBRARY) ++ ++mark_as_advanced( ++ Readline_ROOT_DIR ++ Readline_INCLUDE_PATH ++ Readline_LIBRARY ++) diff --git a/patches/series b/patches/series new file mode 100644 index 0000000..ecbd185 --- /dev/null +++ b/patches/series @@ -0,0 +1 @@ +0003-Use-readline-for-interactive-input.-This-adds-comman.patch diff --git a/rules b/rules new file mode 100755 index 0000000..51bbca7 --- /dev/null +++ b/rules @@ -0,0 +1,51 @@ +#!/usr/bin/make -f + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +include /usr/share/dpkg/pkg-info.mk + +export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +CHANGELOG_DATE ?= $(shell LC_ALL=C date -u -d @$(SOURCE_DATE_EPOCH) +"%d %B %Y") + +%: + dh $@ + + +override_dh_auto_install: + dh_auto_install + +override_dh_auto_clean: + dh_auto_clean + $(RM) app/StaApp_wrap.cc + $(RM) app/TclInitVar.cc + $(RM) app/libOpenSTA.a + $(RM) app/sta + $(RM) liberty/LibertyExprLex.cc + $(RM) liberty/LibertyExprLex.hh + $(RM) liberty/LibertyExprParse.cc + $(RM) liberty/LibertyExprParse.hh + $(RM) liberty/LibertyLex.cc + $(RM) liberty/LibertyLex.hh + $(RM) liberty/LibertyParse.cc + $(RM) liberty/LibertyParse.hh + $(RM) parasitics/SpefLex.cc + $(RM) parasitics/SpefLex.hh + $(RM) parasitics/SpefParse.cc + $(RM) parasitics/SpefParse.hh + $(RM) sdf/SdfLex.cc + $(RM) sdf/SdfLex.hh + $(RM) sdf/SdfParse.cc + $(RM) sdf/SdfParse.hh + $(RM) util/StaConfig.hh + $(RM) verilog/VerilogLex.cc + $(RM) verilog/VerilogLex.hh + $(RM) verilog/VerilogParse.cc + $(RM) verilog/VerilogParse.hh + +override_dh_installman: + cd debian/man ; CHANGELOG_DATE="$(CHANGELOG_DATE)" ./genmanpages.sh + dh_installman diff --git a/source/format b/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/watch b/watch new file mode 100644 index 0000000..f6f3a25 --- /dev/null +++ b/watch @@ -0,0 +1,6 @@ +version=4 +opts=mode=git,\ +dversionmangle=s/0~(\d{8})(git|hg)([a-f0-9]{7})\+dfsg.*/0\.0~git$1\.$3/,\ +oversionmangle=s/0\.0~git(\d{8})\.([a-f0-9]{7})$/0~$1git$2+dfsg/ \ +https://github.com/The-OpenROAD-Project/OpenSTA.git \ +HEAD debian uupdate -- cgit v1.2.3 From cc7cc978ae35494f8a937aad3cb63a1d6771cf4c Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Sun, 20 Oct 2019 21:05:50 +0200 Subject: Import opensta_0~20191010git56851ed+dfsg.orig.tar.xz [dgit import orig opensta_0~20191010git56851ed+dfsg.orig.tar.xz] --- .dockerignore | 4 + .gitignore | 82 + CMakeLists.txt | 736 ++++ Dockerfile | 32 + LICENSE | 674 +++ README.md | 230 + app/Main.cc | 55 + app/Makefile.am | 62 + app/StaApp.i | 25 + app/StaMain.cc | 231 + app/StaMain.hh | 76 + cmake/GetGitRevisionDescription.cmake | 168 + cmake/GetGitRevisionDescription.cmake.in | 41 + dcalc/ArcDelayCalc.cc | 54 + dcalc/ArcDelayCalc.hh | 146 + dcalc/Arnoldi.hh | 83 + dcalc/Arnoldi.txt | 57 + dcalc/ArnoldiDelayCalc.cc | 1490 +++++++ dcalc/ArnoldiDelayCalc.hh | 26 + dcalc/ArnoldiReduce.cc | 646 +++ dcalc/ArnoldiReduce.hh | 116 + dcalc/DcalcAnalysisPt.cc | 61 + dcalc/DcalcAnalysisPt.hh | 78 + dcalc/DelayCalc.cc | 91 + dcalc/DelayCalc.hh | 50 + dcalc/DelayCalc.i | 51 + dcalc/DelayCalc.tcl | 125 + dcalc/DmpCeff.cc | 1780 ++++++++ dcalc/DmpCeff.hh | 104 + dcalc/DmpDelayCalc.cc | 414 ++ dcalc/DmpDelayCalc.hh | 28 + dcalc/GraphDelayCalc.cc | 175 + dcalc/GraphDelayCalc.hh | 143 + dcalc/GraphDelayCalc1.cc | 1783 ++++++++ dcalc/GraphDelayCalc1.hh | 246 ++ dcalc/LumpedCapDelayCalc.cc | 299 ++ dcalc/LumpedCapDelayCalc.hh | 117 + dcalc/Makefile.am | 62 + dcalc/NetCaps.cc | 49 + dcalc/NetCaps.hh | 52 + dcalc/RCDelayCalc.cc | 79 + dcalc/RCDelayCalc.hh | 48 + dcalc/SimpleRCDelayCalc.cc | 113 + dcalc/SimpleRCDelayCalc.hh | 65 + dcalc/UnitDelayCalc.cc | 144 + dcalc/UnitDelayCalc.hh | 98 + doc/ApiChanges.txt | 26 + doc/BugLog | 19 + doc/ChangeLog.txt | 142 + doc/CodingGuidelines.txt | 156 + doc/Makefile.am | 27 + doc/OpenSTA.odt | Bin 0 -> 76181 bytes doc/OpenSTA.pdf | Bin 0 -> 153190 bytes doc/StaApi.txt | 593 +++ etc/Makefile.am | 23 + etc/SwigCleanup.tcl | 77 + etc/TclEncode.tcl | 96 + examples/example1.dspef | 135 + examples/example1.sdf | 93 + examples/example1.tcl | 8 + examples/example1.v | 11 + examples/example2.tcl | 7 + examples/example3.tcl | 8 + examples/example4.tcl | 8 + examples/example5.tcl | 15 + graph/Delay.cc | 32 + graph/Delay.hh | 42 + graph/DelayFloat.cc | 143 + graph/DelayFloat.hh | 104 + graph/DelayNormal1.cc | 422 ++ graph/DelayNormal1.hh | 157 + graph/DelayNormal2.cc | 456 ++ graph/DelayNormal2.hh | 164 + graph/Graph.cc | 1629 ++++++++ graph/Graph.hh | 511 +++ graph/GraphClass.hh | 58 + graph/GraphCmp.cc | 68 + graph/GraphCmp.hh | 56 + graph/Makefile.am | 37 + liberty/EquivCells.cc | 320 ++ liberty/EquivCells.hh | 78 + liberty/FuncExpr.cc | 418 ++ liberty/FuncExpr.hh | 112 + liberty/InternalPower.cc | 233 ++ liberty/InternalPower.hh | 117 + liberty/LeakagePower.cc | 54 + liberty/LeakagePower.hh | 64 + liberty/Liberty.cc | 2782 ++++++++++++ liberty/Liberty.hh | 1069 +++++ liberty/LibertyBuilder.cc | 648 +++ liberty/LibertyBuilder.hh | 143 + liberty/LibertyClass.hh | 172 + liberty/LibertyExpr.cc | 192 + liberty/LibertyExpr.hh | 35 + liberty/LibertyExprLex.ll | 86 + liberty/LibertyExprParse.yy | 86 + liberty/LibertyExprPvt.hh | 79 + liberty/LibertyExt.cc | 268 ++ liberty/LibertyLex.ll | 209 + liberty/LibertyParse.yy | 179 + liberty/LibertyParser.cc | 576 +++ liberty/LibertyParser.hh | 353 ++ liberty/LibertyReader.cc | 5276 +++++++++++++++++++++++ liberty/LibertyReader.hh | 31 + liberty/LibertyReaderPvt.hh | 829 ++++ liberty/LinearModel.cc | 124 + liberty/LinearModel.hh | 91 + liberty/Makefile.am | 103 + liberty/Sequential.cc | 56 + liberty/Sequential.hh | 75 + liberty/TableModel.cc | 1409 +++++++ liberty/TableModel.hh | 461 ++ liberty/TimingArc.cc | 767 ++++ liberty/TimingArc.hh | 286 ++ liberty/TimingModel.hh | 91 + liberty/TimingRole.cc | 252 ++ liberty/TimingRole.hh | 138 + liberty/Transition.cc | 277 ++ liberty/Transition.hh | 224 + liberty/Units.cc | 149 + liberty/Units.hh | 95 + liberty/Wireload.cc | 303 ++ liberty/Wireload.hh | 100 + network/ConcreteLibrary.cc | 635 +++ network/ConcreteLibrary.hh | 255 ++ network/ConcreteNetwork.cc | 1979 +++++++++ network/ConcreteNetwork.hh | 405 ++ network/HpinDrvrLoad.cc | 303 ++ network/HpinDrvrLoad.hh | 79 + network/MakeConcreteNetwork.hh | 28 + network/Makefile.am | 46 + network/Network.cc | 1840 ++++++++ network/Network.hh | 670 +++ network/NetworkClass.hh | 81 + network/NetworkCmp.cc | 122 + network/NetworkCmp.hh | 89 + network/ParseBus.cc | 200 + network/ParseBus.hh | 86 + network/PortDirection.cc | 101 + network/PortDirection.hh | 77 + network/SdcNetwork.cc | 1123 +++++ network/SdcNetwork.hh | 262 ++ network/VerilogNamespace.cc | 154 + network/VerilogNamespace.hh | 38 + parasitics/ConcreteParasitics.cc | 1707 ++++++++ parasitics/ConcreteParasitics.hh | 218 + parasitics/ConcreteParasiticsPvt.hh | 448 ++ parasitics/EstimateParasitics.cc | 237 ++ parasitics/EstimateParasitics.hh | 94 + parasitics/MakeConcreteParasitics.hh | 26 + parasitics/Makefile.am | 64 + parasitics/NullParasitics.cc | 457 ++ parasitics/NullParasitics.hh | 193 + parasitics/Parasitics.cc | 212 + parasitics/Parasitics.hh | 332 ++ parasitics/Parasitics.i | 107 + parasitics/Parasitics.tcl | 122 + parasitics/ParasiticsClass.hh | 31 + parasitics/ReduceParasitics.cc | 729 ++++ parasitics/ReduceParasitics.hh | 50 + parasitics/SpefLex.ll | 216 + parasitics/SpefNamespace.cc | 109 + parasitics/SpefNamespace.hh | 35 + parasitics/SpefParse.yy | 825 ++++ parasitics/SpefReader.cc | 728 ++++ parasitics/SpefReader.hh | 54 + parasitics/SpefReaderPvt.hh | 210 + sdc/Clock.cc | 713 ++++ sdc/Clock.hh | 316 ++ sdc/ClockGatingCheck.cc | 33 + sdc/ClockGatingCheck.hh | 42 + sdc/ClockGroups.cc | 85 + sdc/ClockGroups.hh | 71 + sdc/ClockInsertion.cc | 89 + sdc/ClockInsertion.hh | 57 + sdc/ClockLatency.cc | 83 + sdc/ClockLatency.hh | 61 + sdc/CycleAccting.cc | 340 ++ sdc/CycleAccting.hh | 105 + sdc/DataCheck.cc | 115 + sdc/DataCheck.hh | 81 + sdc/DeratingFactors.cc | 196 + sdc/DeratingFactors.hh | 118 + sdc/DisabledPorts.cc | 265 ++ sdc/DisabledPorts.hh | 112 + sdc/ExceptionPath.cc | 2534 +++++++++++ sdc/ExceptionPath.hh | 684 +++ sdc/InputDrive.cc | 225 + sdc/InputDrive.hh | 117 + sdc/Makefile.am | 67 + sdc/MinMaxValues.hh | 169 + sdc/PinPair.cc | 48 + sdc/PinPair.hh | 51 + sdc/PortDelay.cc | 196 + sdc/PortDelay.hh | 194 + sdc/PortExtCap.cc | 80 + sdc/PortExtCap.hh | 71 + sdc/RiseFallMinMax.cc | 267 ++ sdc/RiseFallMinMax.hh | 76 + sdc/RiseFallValues.cc | 98 + sdc/RiseFallValues.hh | 49 + sdc/Sdc.cc | 6731 ++++++++++++++++++++++++++++++ sdc/Sdc.hh | 1529 +++++++ sdc/SdcClass.hh | 105 + sdc/SdcCmdComment.cc | 49 + sdc/SdcCmdComment.hh | 39 + sdc/WriteSdc.cc | 2974 +++++++++++++ sdc/WriteSdc.hh | 37 + sdc/WriteSdcPvt.hh | 255 ++ sdf/Makefile.am | 59 + sdf/ReportAnnotation.cc | 579 +++ sdf/ReportAnnotation.hh | 48 + sdf/Sdf.hh | 225 + sdf/Sdf.i | 161 + sdf/Sdf.tcl | 230 + sdf/SdfLex.ll | 197 + sdf/SdfParse.yy | 316 ++ sdf/SdfReader.cc | 1108 +++++ sdf/SdfReader.hh | 80 + sdf/SdfWriter.cc | 792 ++++ sdf/SdfWriter.hh | 35 + search/Bfs.cc | 461 ++ search/Bfs.hh | 168 + search/CheckMaxSkews.cc | 296 ++ search/CheckMaxSkews.hh | 92 + search/CheckMinPeriods.cc | 243 ++ search/CheckMinPeriods.hh | 84 + search/CheckMinPulseWidths.cc | 510 +++ search/CheckMinPulseWidths.hh | 110 + search/CheckSlewLimits.cc | 380 ++ search/CheckSlewLimits.hh | 112 + search/CheckTiming.cc | 438 ++ search/CheckTiming.hh | 76 + search/ClkInfo.cc | 340 ++ search/ClkInfo.hh | 124 + search/ClkSkew.cc | 342 ++ search/ClkSkew.hh | 69 + search/Corner.cc | 498 +++ search/Corner.hh | 188 + search/Crpr.cc | 398 ++ search/Crpr.hh | 97 + search/FindRegister.cc | 806 ++++ search/FindRegister.hh | 47 + search/GatedClk.cc | 257 ++ search/GatedClk.hh | 56 + search/Genclks.cc | 1310 ++++++ search/Genclks.hh | 149 + search/Latches.cc | 525 +++ search/Latches.hh | 107 + search/Levelize.cc | 493 +++ search/Levelize.hh | 125 + search/Makefile.am | 107 + search/Path.cc | 367 ++ search/Path.hh | 130 + search/PathAnalysisPt.cc | 56 + search/PathAnalysisPt.hh | 63 + search/PathEnd.cc | 2071 +++++++++ search/PathEnd.hh | 663 +++ search/PathEnum.cc | 614 +++ search/PathEnum.hh | 107 + search/PathEnumed.cc | 183 + search/PathEnumed.hh | 78 + search/PathExpanded.cc | 239 ++ search/PathExpanded.hh | 80 + search/PathGroup.cc | 909 ++++ search/PathGroup.hh | 204 + search/PathRef.cc | 253 ++ search/PathRef.hh | 87 + search/PathVertex.cc | 606 +++ search/PathVertex.hh | 147 + search/PathVertexRep.cc | 230 + search/PathVertexRep.hh | 77 + search/Power.cc | 998 +++++ search/Power.hh | 186 + search/Property.cc | 979 +++++ search/Property.hh | 194 + search/ReportPath.cc | 3347 +++++++++++++++ search/ReportPath.hh | 578 +++ search/Search.cc | 4011 ++++++++++++++++++ search/Search.hh | 822 ++++ search/SearchClass.hh | 131 + search/SearchPred.cc | 271 ++ search/SearchPred.hh | 178 + search/Sim.cc | 1308 ++++++ search/Sim.hh | 221 + search/Sta.cc | 4986 ++++++++++++++++++++++ search/Sta.hh | 1331 ++++++ search/StaState.cc | 116 + search/StaState.hh | 123 + search/Tag.cc | 693 +++ search/Tag.hh | 168 + search/TagGroup.cc | 383 ++ search/TagGroup.hh | 167 + search/VertexVisitor.cc | 42 + search/VertexVisitor.hh | 55 + search/VisitPathEnds.cc | 639 +++ search/VisitPathEnds.hh | 153 + search/VisitPathGroupVertices.cc | 321 ++ search/VisitPathGroupVertices.hh | 34 + search/WorstSlack.cc | 346 ++ search/WorstSlack.hh | 116 + search/WritePathSpice.cc | 1626 ++++++++ search/WritePathSpice.hh | 39 + search/WriteSpice.cc | 946 +++++ search/WriteSpice.hh | 37 + tcl/Cmds.tcl | 2032 +++++++++ tcl/Graph.tcl | 402 ++ tcl/Liberty.tcl | 37 + tcl/Link.tcl | 38 + tcl/Makefile.am | 29 + tcl/Network.tcl | 530 +++ tcl/NetworkEdit.i | 96 + tcl/NetworkEdit.tcl | 267 ++ tcl/Power.tcl | 248 ++ tcl/Sdc.tcl | 3200 ++++++++++++++ tcl/Search.tcl | 420 ++ tcl/Splash.tcl | 642 +++ tcl/Sta.tcl | 1080 +++++ tcl/StaException.i | 36 + tcl/StaTcl.i | 6150 +++++++++++++++++++++++++++ tcl/Util.tcl | 446 ++ tcl/Variables.tcl | 193 + test/example1.ok | 29 + test/example2.ok | 29 + test/example3.ok | 56 + test/example4.ok | 29 + test/example5.ok | 88 + test/find_parent_dir.tcl | 34 + test/regression | 34 + test/regression.tcl | 508 +++ test/regression_vars.tcl | 115 + test/save_ok | 32 + test/valgrind.suppress | 155 + util/Condition.cc | 58 + util/Condition.hh | 41 + util/Debug.cc | 115 + util/Debug.hh | 127 + util/DisallowCopyAssign.hh | 26 + util/EnumNameMap.hh | 97 + util/Error.cc | 75 + util/Error.hh | 90 + util/Fuzzy.cc | 77 + util/Fuzzy.hh | 45 + util/Hash.cc | 33 + util/Hash.hh | 56 + util/HashMap.hh | 570 +++ util/HashSet.hh | 455 ++ util/Iterator.hh | 37 + util/Machine.cc | 178 + util/Machine.hh | 114 + util/Makefile.am | 83 + util/Map.hh | 176 + util/MinMax.cc | 177 + util/MinMax.hh | 151 + util/Mutex.cc | 40 + util/Mutex.hh | 27 + util/ObjectIndex.hh | 25 + util/PatternMatch.cc | 198 + util/PatternMatch.hh | 94 + util/Pool.hh | 340 ++ util/Pthread.cc | 149 + util/Pthread.hh | 92 + util/ReadWriteLock.cc | 33 + util/ReadWriteLock.hh | 66 + util/Report.cc | 295 ++ util/Report.hh | 122 + util/ReportStd.cc | 63 + util/ReportStd.hh | 30 + util/ReportTcl.cc | 458 ++ util/ReportTcl.hh | 69 + util/Set.hh | 230 + util/StaConfig.hh.cmake | 9 + util/Stats.cc | 52 + util/Stats.hh | 45 + util/StringSeq.cc | 32 + util/StringSeq.hh | 31 + util/StringSet.cc | 42 + util/StringSet.hh | 34 + util/StringUtil.cc | 251 ++ util/StringUtil.hh | 189 + util/Thread.cc | 51 + util/Thread.hh | 48 + util/ThreadException.cc | 38 + util/ThreadException.hh | 41 + util/ThreadForEach.hh | 112 + util/ThreadPool.cc | 51 + util/ThreadPool.hh | 45 + util/ThreadWorker.cc | 134 + util/ThreadWorker.hh | 64 + util/TokenParser.cc | 80 + util/TokenParser.hh | 50 + util/UnorderedMap.hh | 179 + util/UnorderedSet.hh | 128 + util/Vector.hh | 140 + util/Zlib.hh | 42 + verilog/Makefile.am | 52 + verilog/Verilog.i | 63 + verilog/Verilog.tcl | 34 + verilog/VerilogLex.ll | 186 + verilog/VerilogParse.yy | 465 +++ verilog/VerilogReader.cc | 2257 ++++++++++ verilog/VerilogReader.hh | 34 + verilog/VerilogReaderPvt.hh | 728 ++++ verilog/VerilogWriter.cc | 297 ++ verilog/VerilogWriter.hh | 31 + 405 files changed, 144975 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 app/Main.cc create mode 100644 app/Makefile.am create mode 100644 app/StaApp.i create mode 100644 app/StaMain.cc create mode 100644 app/StaMain.hh create mode 100644 cmake/GetGitRevisionDescription.cmake create mode 100644 cmake/GetGitRevisionDescription.cmake.in create mode 100644 dcalc/ArcDelayCalc.cc create mode 100644 dcalc/ArcDelayCalc.hh create mode 100644 dcalc/Arnoldi.hh create mode 100644 dcalc/Arnoldi.txt create mode 100644 dcalc/ArnoldiDelayCalc.cc create mode 100644 dcalc/ArnoldiDelayCalc.hh create mode 100644 dcalc/ArnoldiReduce.cc create mode 100644 dcalc/ArnoldiReduce.hh create mode 100644 dcalc/DcalcAnalysisPt.cc create mode 100644 dcalc/DcalcAnalysisPt.hh create mode 100644 dcalc/DelayCalc.cc create mode 100644 dcalc/DelayCalc.hh create mode 100644 dcalc/DelayCalc.i create mode 100644 dcalc/DelayCalc.tcl create mode 100644 dcalc/DmpCeff.cc create mode 100644 dcalc/DmpCeff.hh create mode 100644 dcalc/DmpDelayCalc.cc create mode 100644 dcalc/DmpDelayCalc.hh create mode 100644 dcalc/GraphDelayCalc.cc create mode 100644 dcalc/GraphDelayCalc.hh create mode 100644 dcalc/GraphDelayCalc1.cc create mode 100644 dcalc/GraphDelayCalc1.hh create mode 100644 dcalc/LumpedCapDelayCalc.cc create mode 100644 dcalc/LumpedCapDelayCalc.hh create mode 100644 dcalc/Makefile.am create mode 100644 dcalc/NetCaps.cc create mode 100644 dcalc/NetCaps.hh create mode 100644 dcalc/RCDelayCalc.cc create mode 100644 dcalc/RCDelayCalc.hh create mode 100644 dcalc/SimpleRCDelayCalc.cc create mode 100644 dcalc/SimpleRCDelayCalc.hh create mode 100644 dcalc/UnitDelayCalc.cc create mode 100644 dcalc/UnitDelayCalc.hh create mode 100644 doc/ApiChanges.txt create mode 100644 doc/BugLog create mode 100644 doc/ChangeLog.txt create mode 100644 doc/CodingGuidelines.txt create mode 100644 doc/Makefile.am create mode 100644 doc/OpenSTA.odt create mode 100644 doc/OpenSTA.pdf create mode 100644 doc/StaApi.txt create mode 100644 etc/Makefile.am create mode 100755 etc/SwigCleanup.tcl create mode 100755 etc/TclEncode.tcl create mode 100644 examples/example1.dspef create mode 100644 examples/example1.sdf create mode 100644 examples/example1.tcl create mode 100644 examples/example1.v create mode 100644 examples/example2.tcl create mode 100644 examples/example3.tcl create mode 100644 examples/example4.tcl create mode 100644 examples/example5.tcl create mode 100644 graph/Delay.cc create mode 100644 graph/Delay.hh create mode 100644 graph/DelayFloat.cc create mode 100644 graph/DelayFloat.hh create mode 100644 graph/DelayNormal1.cc create mode 100644 graph/DelayNormal1.hh create mode 100644 graph/DelayNormal2.cc create mode 100644 graph/DelayNormal2.hh create mode 100644 graph/Graph.cc create mode 100644 graph/Graph.hh create mode 100644 graph/GraphClass.hh create mode 100644 graph/GraphCmp.cc create mode 100644 graph/GraphCmp.hh create mode 100644 graph/Makefile.am create mode 100644 liberty/EquivCells.cc create mode 100644 liberty/EquivCells.hh create mode 100644 liberty/FuncExpr.cc create mode 100644 liberty/FuncExpr.hh create mode 100644 liberty/InternalPower.cc create mode 100644 liberty/InternalPower.hh create mode 100644 liberty/LeakagePower.cc create mode 100644 liberty/LeakagePower.hh create mode 100644 liberty/Liberty.cc create mode 100644 liberty/Liberty.hh create mode 100644 liberty/LibertyBuilder.cc create mode 100644 liberty/LibertyBuilder.hh create mode 100644 liberty/LibertyClass.hh create mode 100644 liberty/LibertyExpr.cc create mode 100644 liberty/LibertyExpr.hh create mode 100644 liberty/LibertyExprLex.ll create mode 100644 liberty/LibertyExprParse.yy create mode 100644 liberty/LibertyExprPvt.hh create mode 100644 liberty/LibertyExt.cc create mode 100644 liberty/LibertyLex.ll create mode 100644 liberty/LibertyParse.yy create mode 100644 liberty/LibertyParser.cc create mode 100644 liberty/LibertyParser.hh create mode 100644 liberty/LibertyReader.cc create mode 100644 liberty/LibertyReader.hh create mode 100644 liberty/LibertyReaderPvt.hh create mode 100644 liberty/LinearModel.cc create mode 100644 liberty/LinearModel.hh create mode 100644 liberty/Makefile.am create mode 100644 liberty/Sequential.cc create mode 100644 liberty/Sequential.hh create mode 100644 liberty/TableModel.cc create mode 100644 liberty/TableModel.hh create mode 100644 liberty/TimingArc.cc create mode 100644 liberty/TimingArc.hh create mode 100644 liberty/TimingModel.hh create mode 100644 liberty/TimingRole.cc create mode 100644 liberty/TimingRole.hh create mode 100644 liberty/Transition.cc create mode 100644 liberty/Transition.hh create mode 100644 liberty/Units.cc create mode 100644 liberty/Units.hh create mode 100644 liberty/Wireload.cc create mode 100644 liberty/Wireload.hh create mode 100644 network/ConcreteLibrary.cc create mode 100644 network/ConcreteLibrary.hh create mode 100644 network/ConcreteNetwork.cc create mode 100644 network/ConcreteNetwork.hh create mode 100644 network/HpinDrvrLoad.cc create mode 100644 network/HpinDrvrLoad.hh create mode 100644 network/MakeConcreteNetwork.hh create mode 100644 network/Makefile.am create mode 100644 network/Network.cc create mode 100644 network/Network.hh create mode 100644 network/NetworkClass.hh create mode 100644 network/NetworkCmp.cc create mode 100644 network/NetworkCmp.hh create mode 100644 network/ParseBus.cc create mode 100644 network/ParseBus.hh create mode 100644 network/PortDirection.cc create mode 100644 network/PortDirection.hh create mode 100644 network/SdcNetwork.cc create mode 100644 network/SdcNetwork.hh create mode 100644 network/VerilogNamespace.cc create mode 100644 network/VerilogNamespace.hh create mode 100644 parasitics/ConcreteParasitics.cc create mode 100644 parasitics/ConcreteParasitics.hh create mode 100644 parasitics/ConcreteParasiticsPvt.hh create mode 100644 parasitics/EstimateParasitics.cc create mode 100644 parasitics/EstimateParasitics.hh create mode 100644 parasitics/MakeConcreteParasitics.hh create mode 100644 parasitics/Makefile.am create mode 100644 parasitics/NullParasitics.cc create mode 100644 parasitics/NullParasitics.hh create mode 100644 parasitics/Parasitics.cc create mode 100644 parasitics/Parasitics.hh create mode 100644 parasitics/Parasitics.i create mode 100644 parasitics/Parasitics.tcl create mode 100644 parasitics/ParasiticsClass.hh create mode 100644 parasitics/ReduceParasitics.cc create mode 100644 parasitics/ReduceParasitics.hh create mode 100644 parasitics/SpefLex.ll create mode 100644 parasitics/SpefNamespace.cc create mode 100644 parasitics/SpefNamespace.hh create mode 100755 parasitics/SpefParse.yy create mode 100644 parasitics/SpefReader.cc create mode 100644 parasitics/SpefReader.hh create mode 100644 parasitics/SpefReaderPvt.hh create mode 100644 sdc/Clock.cc create mode 100644 sdc/Clock.hh create mode 100644 sdc/ClockGatingCheck.cc create mode 100644 sdc/ClockGatingCheck.hh create mode 100644 sdc/ClockGroups.cc create mode 100644 sdc/ClockGroups.hh create mode 100644 sdc/ClockInsertion.cc create mode 100644 sdc/ClockInsertion.hh create mode 100644 sdc/ClockLatency.cc create mode 100644 sdc/ClockLatency.hh create mode 100644 sdc/CycleAccting.cc create mode 100644 sdc/CycleAccting.hh create mode 100644 sdc/DataCheck.cc create mode 100644 sdc/DataCheck.hh create mode 100644 sdc/DeratingFactors.cc create mode 100644 sdc/DeratingFactors.hh create mode 100644 sdc/DisabledPorts.cc create mode 100644 sdc/DisabledPorts.hh create mode 100644 sdc/ExceptionPath.cc create mode 100644 sdc/ExceptionPath.hh create mode 100644 sdc/InputDrive.cc create mode 100644 sdc/InputDrive.hh create mode 100644 sdc/Makefile.am create mode 100644 sdc/MinMaxValues.hh create mode 100644 sdc/PinPair.cc create mode 100644 sdc/PinPair.hh create mode 100644 sdc/PortDelay.cc create mode 100644 sdc/PortDelay.hh create mode 100644 sdc/PortExtCap.cc create mode 100644 sdc/PortExtCap.hh create mode 100644 sdc/RiseFallMinMax.cc create mode 100644 sdc/RiseFallMinMax.hh create mode 100644 sdc/RiseFallValues.cc create mode 100644 sdc/RiseFallValues.hh create mode 100644 sdc/Sdc.cc create mode 100644 sdc/Sdc.hh create mode 100644 sdc/SdcClass.hh create mode 100644 sdc/SdcCmdComment.cc create mode 100644 sdc/SdcCmdComment.hh create mode 100644 sdc/WriteSdc.cc create mode 100644 sdc/WriteSdc.hh create mode 100644 sdc/WriteSdcPvt.hh create mode 100644 sdf/Makefile.am create mode 100644 sdf/ReportAnnotation.cc create mode 100644 sdf/ReportAnnotation.hh create mode 100644 sdf/Sdf.hh create mode 100644 sdf/Sdf.i create mode 100644 sdf/Sdf.tcl create mode 100644 sdf/SdfLex.ll create mode 100644 sdf/SdfParse.yy create mode 100644 sdf/SdfReader.cc create mode 100644 sdf/SdfReader.hh create mode 100644 sdf/SdfWriter.cc create mode 100644 sdf/SdfWriter.hh create mode 100644 search/Bfs.cc create mode 100644 search/Bfs.hh create mode 100644 search/CheckMaxSkews.cc create mode 100644 search/CheckMaxSkews.hh create mode 100644 search/CheckMinPeriods.cc create mode 100644 search/CheckMinPeriods.hh create mode 100644 search/CheckMinPulseWidths.cc create mode 100644 search/CheckMinPulseWidths.hh create mode 100644 search/CheckSlewLimits.cc create mode 100644 search/CheckSlewLimits.hh create mode 100644 search/CheckTiming.cc create mode 100644 search/CheckTiming.hh create mode 100644 search/ClkInfo.cc create mode 100644 search/ClkInfo.hh create mode 100644 search/ClkSkew.cc create mode 100644 search/ClkSkew.hh create mode 100644 search/Corner.cc create mode 100644 search/Corner.hh create mode 100644 search/Crpr.cc create mode 100644 search/Crpr.hh create mode 100644 search/FindRegister.cc create mode 100644 search/FindRegister.hh create mode 100644 search/GatedClk.cc create mode 100644 search/GatedClk.hh create mode 100644 search/Genclks.cc create mode 100644 search/Genclks.hh create mode 100644 search/Latches.cc create mode 100644 search/Latches.hh create mode 100644 search/Levelize.cc create mode 100644 search/Levelize.hh create mode 100644 search/Makefile.am create mode 100644 search/Path.cc create mode 100644 search/Path.hh create mode 100644 search/PathAnalysisPt.cc create mode 100644 search/PathAnalysisPt.hh create mode 100644 search/PathEnd.cc create mode 100644 search/PathEnd.hh create mode 100644 search/PathEnum.cc create mode 100644 search/PathEnum.hh create mode 100644 search/PathEnumed.cc create mode 100644 search/PathEnumed.hh create mode 100644 search/PathExpanded.cc create mode 100644 search/PathExpanded.hh create mode 100644 search/PathGroup.cc create mode 100644 search/PathGroup.hh create mode 100644 search/PathRef.cc create mode 100644 search/PathRef.hh create mode 100644 search/PathVertex.cc create mode 100644 search/PathVertex.hh create mode 100644 search/PathVertexRep.cc create mode 100644 search/PathVertexRep.hh create mode 100644 search/Power.cc create mode 100644 search/Power.hh create mode 100644 search/Property.cc create mode 100644 search/Property.hh create mode 100644 search/ReportPath.cc create mode 100644 search/ReportPath.hh create mode 100644 search/Search.cc create mode 100644 search/Search.hh create mode 100644 search/SearchClass.hh create mode 100644 search/SearchPred.cc create mode 100644 search/SearchPred.hh create mode 100644 search/Sim.cc create mode 100644 search/Sim.hh create mode 100644 search/Sta.cc create mode 100644 search/Sta.hh create mode 100644 search/StaState.cc create mode 100644 search/StaState.hh create mode 100644 search/Tag.cc create mode 100644 search/Tag.hh create mode 100644 search/TagGroup.cc create mode 100644 search/TagGroup.hh create mode 100644 search/VertexVisitor.cc create mode 100644 search/VertexVisitor.hh create mode 100644 search/VisitPathEnds.cc create mode 100644 search/VisitPathEnds.hh create mode 100644 search/VisitPathGroupVertices.cc create mode 100644 search/VisitPathGroupVertices.hh create mode 100644 search/WorstSlack.cc create mode 100644 search/WorstSlack.hh create mode 100644 search/WritePathSpice.cc create mode 100644 search/WritePathSpice.hh create mode 100644 search/WriteSpice.cc create mode 100644 search/WriteSpice.hh create mode 100644 tcl/Cmds.tcl create mode 100644 tcl/Graph.tcl create mode 100644 tcl/Liberty.tcl create mode 100644 tcl/Link.tcl create mode 100644 tcl/Makefile.am create mode 100644 tcl/Network.tcl create mode 100644 tcl/NetworkEdit.i create mode 100644 tcl/NetworkEdit.tcl create mode 100644 tcl/Power.tcl create mode 100644 tcl/Sdc.tcl create mode 100644 tcl/Search.tcl create mode 100644 tcl/Splash.tcl create mode 100644 tcl/Sta.tcl create mode 100644 tcl/StaException.i create mode 100644 tcl/StaTcl.i create mode 100644 tcl/Util.tcl create mode 100644 tcl/Variables.tcl create mode 100644 test/example1.ok create mode 100644 test/example2.ok create mode 100644 test/example3.ok create mode 100644 test/example4.ok create mode 100644 test/example5.ok create mode 100644 test/find_parent_dir.tcl create mode 100755 test/regression create mode 100755 test/regression.tcl create mode 100644 test/regression_vars.tcl create mode 100755 test/save_ok create mode 100644 test/valgrind.suppress create mode 100644 util/Condition.cc create mode 100644 util/Condition.hh create mode 100644 util/Debug.cc create mode 100644 util/Debug.hh create mode 100644 util/DisallowCopyAssign.hh create mode 100644 util/EnumNameMap.hh create mode 100644 util/Error.cc create mode 100644 util/Error.hh create mode 100644 util/Fuzzy.cc create mode 100644 util/Fuzzy.hh create mode 100644 util/Hash.cc create mode 100644 util/Hash.hh create mode 100644 util/HashMap.hh create mode 100644 util/HashSet.hh create mode 100644 util/Iterator.hh create mode 100644 util/Machine.cc create mode 100644 util/Machine.hh create mode 100644 util/Makefile.am create mode 100644 util/Map.hh create mode 100644 util/MinMax.cc create mode 100644 util/MinMax.hh create mode 100644 util/Mutex.cc create mode 100644 util/Mutex.hh create mode 100644 util/ObjectIndex.hh create mode 100644 util/PatternMatch.cc create mode 100644 util/PatternMatch.hh create mode 100644 util/Pool.hh create mode 100644 util/Pthread.cc create mode 100644 util/Pthread.hh create mode 100644 util/ReadWriteLock.cc create mode 100644 util/ReadWriteLock.hh create mode 100644 util/Report.cc create mode 100644 util/Report.hh create mode 100644 util/ReportStd.cc create mode 100644 util/ReportStd.hh create mode 100644 util/ReportTcl.cc create mode 100644 util/ReportTcl.hh create mode 100644 util/Set.hh create mode 100644 util/StaConfig.hh.cmake create mode 100644 util/Stats.cc create mode 100644 util/Stats.hh create mode 100644 util/StringSeq.cc create mode 100644 util/StringSeq.hh create mode 100644 util/StringSet.cc create mode 100644 util/StringSet.hh create mode 100644 util/StringUtil.cc create mode 100644 util/StringUtil.hh create mode 100644 util/Thread.cc create mode 100644 util/Thread.hh create mode 100644 util/ThreadException.cc create mode 100644 util/ThreadException.hh create mode 100644 util/ThreadForEach.hh create mode 100644 util/ThreadPool.cc create mode 100644 util/ThreadPool.hh create mode 100644 util/ThreadWorker.cc create mode 100644 util/ThreadWorker.hh create mode 100644 util/TokenParser.cc create mode 100644 util/TokenParser.hh create mode 100644 util/UnorderedMap.hh create mode 100644 util/UnorderedSet.hh create mode 100644 util/Vector.hh create mode 100644 util/Zlib.hh create mode 100644 verilog/Makefile.am create mode 100644 verilog/Verilog.i create mode 100644 verilog/Verilog.tcl create mode 100644 verilog/VerilogLex.ll create mode 100644 verilog/VerilogParse.yy create mode 100644 verilog/VerilogReader.cc create mode 100644 verilog/VerilogReader.hh create mode 100644 verilog/VerilogReaderPvt.hh create mode 100644 verilog/VerilogWriter.cc create mode 100644 verilog/VerilogWriter.hh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5cb51cf --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.gitignore +Dockerfile +README +INSTALL diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..16d9c30 --- /dev/null +++ b/.gitignore @@ -0,0 +1,82 @@ +*.o +*.a +*.lo +*.la +*.gcov +*.gcda +*.gcno +*.rej +*.orig +TAGS +*~ +\#*# +.~lock.*# +.DS_Store +Makefile + +/build +/debug +/rel + +# /util/ +/util/StaConfig.hh + +# /app/ +/app/StaApp_wrap.cc +/app/TclInitVar.cc +/app/sta +/app/sta.exe +/app/sta.dSYM + +# /doc/ +/doc/._Sta.docx +/doc/.~lock.Sta.doc# +/doc/.~lock.Sta.odt# + +# /liberty/ +/liberty/LibertyExprLex.cc +/liberty/LibertyExprLex.hh +/liberty/LibertyExprParse.cc +/liberty/LibertyExprParse.h +/liberty/LibertyLex.cc +/liberty/LibertyLex.hh +/liberty/LibertyParse.cc +/liberty/LibertyParse.h +/liberty/LibertyExprParse.hh +/liberty/LibertyParse.hh + +# /parasitics/ +/parasitics/SpefLex.cc +/parasitics/SpefLex.hh +/parasitics/SpefParse.cc +/parasitics/SpefParse.hh +/parasitics/SpfLex.cc +/parasitics/SpfLex.hh +/parasitics/SpfParse.cc +/parasitics/SpfParse.hh + +# /sdf/ +/sdf/SdfLex.cc +/sdf/SdfLex.hh +/sdf/SdfParse.cc +/sdf/SdfParse.hh + +# /tcl/ +/tcl/history.tcl +/tcl/init.tcl + +# /test/ +/test/gmon.out +/test/results +# ngspice turd +/test/b3v3_1check.log + +/test_native + +# /verilog/ +/verilog/VerilogLex.hh +/verilog/VerilogLex.cc +/verilog/VerilogParse.cc +/verilog/VerilogParse.h +/verilog/VerilogParse.output +/verilog/VerilogParse.hh diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..716d486 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,736 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +cmake_minimum_required (VERSION 3.9) + +project(STA VERSION 2.0.16) + +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +# Disable compiler specific extensions like gnu++11. +set(CMAKE_CXX_EXTENSIONS OFF) + +set(STA_HOME ${PROJECT_SOURCE_DIR}) +message(STATUS "STA version: ${PROJECT_VERSION}") + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +include(GetGitRevisionDescription) +get_git_head_revision(GIT_REFSPEC STA_GIT_SHA1) +message(STATUS "STA git sha: ${STA_GIT_SHA1}") + +# Default to bulding optimnized/release executable. +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE RELEASE) +endif() + +# Compiler specific options. +# Note -Wno-deprecated-register is to suppress bison errors. +if (CMAKE_CXX_COMPILER_ID MATCHES AppleClang|Clang) + set(STA_COMPILE_OPTIONS -Wall -Wextra -pedantic -Wcast-qual -Wredundant-decls -Wformat-security -Wno-deprecated-register) +endif() + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(STA_COMPILE_OPTIONS -Wall -Wextra -pedantic -Wcast-qual -Wredundant-decls -Wformat-security) +endif() + +message(STATUS "System name: ${CMAKE_SYSTEM_NAME}") +message(STATUS "Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}") +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") +message(STATUS "Build CXX_FLAGS: ${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}") +message(STATUS "STA CXX_FLAGS: ${STA_COMPILE_OPTIONS}") +message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}") + +################################################################ +# +# Source files. +# +################################################################ + +set(STA_SOURCE + app/StaMain.cc + app/TclInitVar.cc + app/StaApp_wrap.cc + + dcalc/ArcDelayCalc.cc + dcalc/ArnoldiDelayCalc.cc + dcalc/ArnoldiReduce.cc + dcalc/DcalcAnalysisPt.cc + dcalc/DelayCalc.cc + dcalc/DmpCeff.cc + dcalc/DmpDelayCalc.cc + dcalc/GraphDelayCalc.cc + dcalc/GraphDelayCalc1.cc + dcalc/LumpedCapDelayCalc.cc + dcalc/NetCaps.cc + dcalc/RCDelayCalc.cc + dcalc/SimpleRCDelayCalc.cc + dcalc/UnitDelayCalc.cc + + graph/DelayFloat.cc + graph/DelayNormal1.cc + graph/DelayNormal2.cc + graph/Graph.cc + graph/GraphCmp.cc + + liberty/EquivCells.cc + liberty/FuncExpr.cc + liberty/InternalPower.cc + liberty/LeakagePower.cc + liberty/Liberty.cc + liberty/LibertyBuilder.cc + liberty/LibertyExpr.cc + liberty/LibertyExpr.hh + liberty/LibertyExprLex.cc + liberty/LibertyExprParse.cc + liberty/LibertyExprPvt.hh + liberty/LibertyLex.cc + liberty/LibertyParse.cc + liberty/LibertyParser.cc + liberty/LibertyReader.cc + liberty/LinearModel.cc + liberty/Sequential.cc + liberty/TableModel.cc + liberty/TimingArc.cc + liberty/TimingRole.cc + liberty/Transition.cc + liberty/Units.cc + liberty/Wireload.cc + + network/ConcreteLibrary.cc + network/ConcreteNetwork.cc + network/HpinDrvrLoad.cc + network/Network.cc + network/NetworkCmp.cc + network/ParseBus.cc + network/PortDirection.cc + network/SdcNetwork.cc + network/VerilogNamespace.cc + + parasitics/ConcreteParasitics.cc + parasitics/EstimateParasitics.cc + parasitics/NullParasitics.cc + parasitics/Parasitics.cc + parasitics/ReduceParasitics.cc + parasitics/SpefLex.cc + parasitics/SpefNamespace.cc + parasitics/SpefParse.cc + parasitics/SpefReader.cc + parasitics/SpefReaderPvt.hh + + sdc/Clock.cc + sdc/ClockGatingCheck.cc + sdc/ClockGroups.cc + sdc/ClockInsertion.cc + sdc/ClockLatency.cc + sdc/CycleAccting.cc + sdc/DataCheck.cc + sdc/DeratingFactors.cc + sdc/DisabledPorts.cc + sdc/ExceptionPath.cc + sdc/InputDrive.cc + sdc/PinPair.cc + sdc/PortDelay.cc + sdc/PortExtCap.cc + sdc/RiseFallMinMax.cc + sdc/RiseFallValues.cc + sdc/Sdc.cc + sdc/SdcCmdComment.cc + sdc/WriteSdc.cc + + sdf/ReportAnnotation.cc + sdf/SdfReader.cc + sdf/SdfParse.cc + sdf/SdfLex.cc + sdf/SdfWriter.cc + + search/Bfs.cc + search/CheckMaxSkews.cc + search/CheckMinPeriods.cc + search/CheckMinPulseWidths.cc + search/CheckSlewLimits.cc + search/CheckTiming.cc + search/ClkInfo.cc + search/ClkSkew.cc + search/Corner.cc + search/Crpr.cc + search/FindRegister.cc + search/GatedClk.cc + search/Genclks.cc + search/Latches.cc + search/Levelize.cc + search/Path.cc + search/PathAnalysisPt.cc + search/PathEnd.cc + search/PathEnum.cc + search/PathEnumed.cc + search/PathExpanded.cc + search/PathGroup.cc + search/PathRef.cc + search/PathVertex.cc + search/PathVertexRep.cc + search/Power.cc + search/Property.cc + search/ReportPath.cc + search/Search.cc + search/SearchPred.cc + search/Sim.cc + search/Sta.cc + search/StaState.cc + search/Tag.cc + search/TagGroup.cc + search/VertexVisitor.cc + search/VisitPathEnds.cc + search/VisitPathGroupVertices.cc + search/WorstSlack.cc + search/WritePathSpice.cc + + util/Debug.cc + 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/Stats.cc + util/StringSeq.cc + util/StringSet.cc + util/StringUtil.cc + util/TokenParser.cc + + verilog/VerilogReader.cc + verilog/VerilogLex.cc + verilog/VerilogParse.cc + verilog/VerilogWriter.cc + ) + +set(STA_HEADERS + app/StaMain.hh + + dcalc/ArcDelayCalc.hh + dcalc/Arnoldi.hh + dcalc/ArnoldiDelayCalc.hh + dcalc/ArnoldiReduce.hh + dcalc/DelayCalc.hh + dcalc/DcalcAnalysisPt.hh + dcalc/DmpCeff.hh + dcalc/DmpDelayCalc.hh + dcalc/GraphDelayCalc.hh + dcalc/GraphDelayCalc1.hh + dcalc/LumpedCapDelayCalc.hh + dcalc/NetCaps.hh + dcalc/RCDelayCalc.hh + dcalc/SimpleRCDelayCalc.hh + dcalc/UnitDelayCalc.hh + + graph/Delay.hh + graph/DelayFloat.hh + graph/DelayNormal1.hh + graph/DelayNormal2.hh + graph/Graph.hh + graph/GraphClass.hh + graph/GraphCmp.hh + + liberty/EquivCells.hh + liberty/FuncExpr.hh + liberty/InternalPower.hh + liberty/LeakagePower.hh + liberty/Liberty.hh + liberty/LibertyBuilder.hh + liberty/LibertyClass.hh + liberty/LibertyParser.hh + liberty/LibertyReader.hh + liberty/LibertyReaderPvt.hh + liberty/LinearModel.hh + liberty/Sequential.hh + liberty/TableModel.hh + liberty/TimingArc.hh + liberty/TimingModel.hh + liberty/TimingRole.hh + liberty/Transition.hh + liberty/Units.hh + liberty/Wireload.hh + + network/ConcreteLibrary.hh + network/ConcreteNetwork.hh + network/HpinDrvrLoad.hh + network/MakeConcreteNetwork.hh + network/Network.hh + network/NetworkClass.hh + network/NetworkCmp.hh + network/ParseBus.hh + network/PortDirection.hh + network/SdcNetwork.hh + network/VerilogNamespace.hh + + parasitics/ConcreteParasitics.hh + parasitics/ConcreteParasiticsPvt.hh + parasitics/EstimateParasitics.hh + parasitics/MakeConcreteParasitics.hh + parasitics/NullParasitics.hh + parasitics/Parasitics.hh + parasitics/ParasiticsClass.hh + parasitics/ReduceParasitics.hh + parasitics/SpefNamespace.hh + parasitics/SpefReader.hh + + sdc/Clock.hh + sdc/ClockGatingCheck.hh + sdc/ClockGroups.hh + sdc/ClockInsertion.hh + sdc/ClockLatency.hh + sdc/CycleAccting.hh + sdc/DataCheck.hh + sdc/DeratingFactors.hh + sdc/DisabledPorts.hh + sdc/ExceptionPath.hh + sdc/InputDrive.hh + sdc/MinMaxValues.hh + sdc/PinPair.hh + sdc/PortDelay.hh + sdc/PortExtCap.hh + sdc/RiseFallMinMax.hh + sdc/RiseFallValues.hh + sdc/Sdc.hh + sdc/SdcClass.hh + sdc/SdcCmdComment.hh + sdc/WriteSdc.hh + sdc/WriteSdcPvt.hh + + sdf/ReportAnnotation.hh + sdf/Sdf.hh + sdf/SdfReader.hh + sdf/SdfWriter.hh + + search/Bfs.hh + search/CheckMaxSkews.hh + search/CheckMinPeriods.hh + search/CheckMinPulseWidths.hh + search/CheckSlewLimits.hh + search/CheckTiming.hh + search/ClkInfo.hh + search/ClkSkew.hh + search/Corner.hh + search/Crpr.hh + search/FindRegister.hh + search/GatedClk.hh + search/Genclks.hh + search/Latches.hh + search/Levelize.hh + search/Path.hh + search/PathAnalysisPt.hh + search/PathEnd.hh + search/PathEnum.hh + search/PathEnumed.hh + search/PathExpanded.hh + search/PathRef.hh + search/PathGroup.hh + search/PathVertex.hh + search/PathVertexRep.hh + search/Power.hh + search/Property.hh + search/ReportPath.hh + search/Search.hh + search/SearchClass.hh + search/SearchPred.hh + search/Sim.hh + search/Sta.hh + search/StaState.hh + search/Tag.hh + search/TagGroup.hh + search/VertexVisitor.hh + search/VisitPathEnds.hh + search/VisitPathGroupVertices.hh + search/WorstSlack.hh + search/WritePathSpice.hh + + util/Debug.hh + util/DisallowCopyAssign.hh + util/EnumNameMap.hh + util/Error.hh + util/Fuzzy.hh + util/Hash.hh + util/HashSet.hh + util/Iterator.hh + util/Machine.hh + util/Map.hh + util/MinMax.hh + util/Mutex.hh + util/ObjectIndex.hh + util/PatternMatch.hh + util/Pool.hh + util/Report.hh + util/ReportStd.hh + util/ReportTcl.hh + util/Set.hh + util/StaConfig.hh + util/Stats.hh + util/StringSeq.hh + util/StringSet.hh + util/StringUtil.hh + util/ThreadForEach.hh + util/TokenParser.hh + util/UnorderedMap.hh + util/UnorderedSet.hh + util/Vector.hh + util/Zlib.hh + + verilog/VerilogReaderPvt.hh + verilog/VerilogReader.hh + verilog/VerilogWriter.hh + ) + +# Source files. +set(STA_TCL_FILES + tcl/Util.tcl + tcl/Graph.tcl + tcl/Liberty.tcl + tcl/Link.tcl + tcl/Network.tcl + tcl/NetworkEdit.tcl + tcl/Sdc.tcl + tcl/Search.tcl + tcl/Cmds.tcl + tcl/Variables.tcl + tcl/Sta.tcl + tcl/Power.tcl + tcl/Splash.tcl + dcalc/DelayCalc.tcl + parasitics/Parasitics.tcl + sdf/Sdf.tcl + verilog/Verilog.tcl + ) + +set(STA_SWIG_FILES + tcl/NetworkEdit.i + tcl/StaException.i + sdf/Sdf.i + dcalc/DelayCalc.i + parasitics/Parasitics.i + verilog/Verilog.i + tcl/StaTcl.i + app/StaApp.i + ) + +################################################################ +# +# Library dependencies +# +################################################################ + +# Zlib +include(FindZLIB) +# Translate cmake bool to StaConfig.h ifdef bool +if (ZLIB_FOUND) + set(ZLIB 1) +else() + set(ZLIB 0) +endif() + +################################################################ +# +# Locate CUDD bdd packagte +# -DCUDD=0 to not use CUDD. +# Look for library in CUDD/lib, CUDD/cudd/lib +# Look for header in CUDD/include, CUDD/cudd/include +# +if("${CUDD}" STREQUAL "" OR "${CUDD}" STREQUAL "0") + set(CUDD_INCLUDE "") + set(CUDD_LIB "") + set(CUDD_FOUND FALSE) + set(CUDD 0) + message(STATUS "CUDD library: not found") +else() + find_library(CUDD_LIB NAMES cudd PATHS ${CUDD}/lib ${CUDD}/lib/cudd) + if (CUDD_LIB) + message(STATUS "CUDD library: ${CUDD_LIB}") + get_filename_component(CUDD_LIB_DIR "${CUDD_LIB}" PATH) + get_filename_component(CUDD_LIB_PARENT1 "${CUDD_LIB_DIR}" PATH) + get_filename_component(CUDD_LIB_PARENT2 "${CUDD_LIB_PARENT1}" PATH) + find_file(CUDD_HEADER cudd.h + PATHS ${CUDD}/include ${CUDD_LIB_PARENT2}/include/cudd) + if (CUDD_HEADER) + get_filename_component(CUDD_INCLUDE "${CUDD_HEADER}" PATH) + message(STATUS "CUDD header: ${CUDD_HEADER}") + # CUDD referenced by StaConfig.hh.cmake + set(CUDD 1) + else() + message(STATUS "CUDD header: not found") + endif() + else() + set(CUDD_INCLUDE "") + set(CUDD_LIB "") + set(CUDD_FOUND FALSE) + set(CUDD 0) + message(STATUS "CUDD library: not found") + endif() +endif() + +if("${SSTA}" STREQUAL "") + set(SSTA 0) +endif() +message(STATUS "SSTA: ${SSTA}") + +# configure a header file to pass some of the CMake settins +configure_file(${STA_HOME}/util/StaConfig.hh.cmake + ${STA_HOME}/util/StaConfig.hh + ) + + +################################################################ +# +# Locate TCL library. +# +# Note that the cmake findTcl module is hopeless for OSX +# because there doesn't appear to be a way to override +# searching OSX system directories before unix directories. + +set(TCL_POSSIBLE_NAMES tcl87 tcl8.7 + tcl86 tcl8.6 + tcl85 tcl8.5 + tcl84 tcl8.4 + tcl83 tcl8.3 + tcl82 tcl8.2 + ) + +# tcl lib path guesses. +if (NOT TCL_LIB_PATHS) + if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(TCL_LIB_PATHS /usr/local/lib) + set(TCL_NO_DEFAULT_PATH TRUE) + endif() +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(TCL_LIB_PATHS /usr/lib + /usr/local/lib + ) + set(TCL_NO_DEFAULT_PATH FALSE) +endif() + +if (NOT TCL_LIB) + # bagbiter cmake doesn't have a way to pass NO_DEFAULT_PATH as a parameter. + if (TCL_NO_DEFAULT_PATH) + find_library(TCL_LIB + NAMES tcl ${TCL_POSSIBLE_NAMES} + PATHS ${TCL_LIB_PATHS} + NO_DEFAULT_PATH + ) + else() + find_library(TCL_LIB + NAMES tcl ${TCL_POSSIBLE_NAMES} + PATHS ${TCL_LIB_PATHS} + ) + endif() +endif() +message(STATUS "TCL lib: ${TCL_LIB}") + +get_filename_component(TCL_LIB_DIR "${TCL_LIB}" PATH) +get_filename_component(TCL_LIB_PARENT1 "${TCL_LIB_DIR}" PATH) +get_filename_component(TCL_LIB_PARENT2 "${TCL_LIB_PARENT1}" PATH) + +# Locate tcl.h +if (NOT TCL_HEADER) + find_file(TCL_HEADER tcl.h + PATHS ${TCL_LIB_PARENT1} ${TCL_LIB_PARENT2} + PATH_SUFFIXES include include/tcl + NO_DEFAULT_PATH + ) +endif() +message(STATUS "TCL header: ${TCL_HEADER}") +get_filename_component(TCL_HEADER_DIR "${TCL_HEADER}" PATH) + +################################################################ +# +# Flex/bison scanner/parsers +# +################################################################ + +find_package(FLEX) +find_package(BISON) + +# LibertyExpr scan/parse. +bison_target(LibertyExprParser liberty/LibertyExprParse.yy ${STA_HOME}/liberty/LibertyExprParse.cc + DEFINES_FILE ${STA_HOME}/liberty/LibertyExprParse.hh + COMPILE_FLAGS --name-prefix=LibertyExprParse_ + ) + +flex_target(LibertyExprLex liberty/LibertyExprLex.ll ${STA_HOME}/liberty/LibertyExprLex.cc + DEFINES_FILE ${STA_HOME}/liberty/LibertyExprLex.hh + COMPILE_FLAGS --prefix=LibertyExprLex_ + ) + +add_flex_bison_dependency(LibertyExprLex LibertyExprParser) + +# Liberty scan/parse. +bison_target(LibertyParser liberty/LibertyParse.yy ${STA_HOME}/liberty/LibertyParse.cc + DEFINES_FILE ${STA_HOME}/liberty/LibertyParse.hh + COMPILE_FLAGS --name-prefix=LibertyParse_ + ) + +flex_target(LibertyLex liberty/LibertyLex.ll ${STA_HOME}/liberty/LibertyLex.cc + DEFINES_FILE ${STA_HOME}/liberty/LibertyLex.hh + COMPILE_FLAGS --prefix=LibertyLex_ + ) + +add_flex_bison_dependency(LibertyLex LibertyParser) + +# Spef scan/parse. +bison_target(SpefParser parasitics/SpefParse.yy ${STA_HOME}/parasitics/SpefParse.cc + DEFINES_FILE ${STA_HOME}/parasitics/SpefParse.hh + COMPILE_FLAGS --name-prefix=SpefParse_ + ) + +flex_target(SpefLex parasitics/SpefLex.ll ${STA_HOME}/parasitics/SpefLex.cc + DEFINES_FILE ${STA_HOME}/parasitics/SpefLex.hh + COMPILE_FLAGS --prefix=SpefLex_ + ) + +add_flex_bison_dependency(SpefLex SpefParser) + +# Verilog scan/parse. +bison_target(VerilogParser verilog/VerilogParse.yy ${STA_HOME}/verilog/VerilogParse.cc + DEFINES_FILE ${STA_HOME}/verilog/VerilogParse.hh + COMPILE_FLAGS --name-prefix=VerilogParse_ + ) + +flex_target(VerilogLex verilog/VerilogLex.ll ${STA_HOME}/verilog/VerilogLex.cc + DEFINES_FILE ${STA_HOME}/verilog/VerilogLex.hh + COMPILE_FLAGS --prefix=VerilogLex_ + ) + +add_flex_bison_dependency(VerilogLex VerilogParser) + +# Sdf scan/parse. +bison_target(SdfParser sdf/SdfParse.yy ${STA_HOME}/sdf/SdfParse.cc + DEFINES_FILE ${STA_HOME}/sdf/SdfParse.hh + COMPILE_FLAGS --name-prefix=SdfParse_ + ) + +flex_target(SdfLex sdf/SdfLex.ll ${STA_HOME}/sdf/SdfLex.cc + DEFINES_FILE ${STA_HOME}/sdf/SdfLex.hh + COMPILE_FLAGS --prefix=SdfLex_ + ) + +add_flex_bison_dependency(SdfLex SdfParser) + + +################################################################ + +include(FindSWIG) + +add_custom_command(OUTPUT ${STA_HOME}/app/StaApp_wrap.cc + COMMAND ${SWIG_EXECUTABLE} -tcl8 -c++ -namespace -prefix sta -I${STA_HOME}/tcl -I${STA_HOME}/sdf -I${STA_HOME}/dcalc -I${STA_HOME}/parasitics -I${STA_HOME}/verilog -o ${STA_HOME}/app/StaApp_wrap.cc ${STA_HOME}/app/StaApp.i + COMMAND ${STA_HOME}/etc/SwigCleanup.tcl ${STA_HOME}/app/StaApp_wrap.cc + WORKING_DIRECTORY ${STA_HOME} + DEPENDS ${STA_SWIG_FILES} + ) + +################################################################ + +# TCL files included as part of the executable are shoved into TclInitVar.cc. +# These files are encoded and shipped as part of the executable +# so that they do not have to be installed on the client host. +add_custom_command(OUTPUT ${STA_HOME}/app/TclInitVar.cc + COMMAND etc/TclEncode.tcl app/TclInitVar.cc tcl_inits ${STA_TCL_FILES} + WORKING_DIRECTORY ${STA_HOME} + DEPENDS ${STA_TCL_FILES} etc/TclEncode.tcl + ) + +################################################################ + +set(STA_INCLUDE_DIRS + app + dcalc + graph + liberty + network + parasitics + sdc + sdf + search + util + verilog + ${TCL_HEADER_DIR} + ${CUDD_INCLUDE} + ) + +########################################################### +# Library +########################################################### + +# compatibility with configure +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${STA_HOME}/app) + +add_library(OpenSTA ${STA_SOURCE}) + +target_include_directories(OpenSTA PUBLIC ${STA_INCLUDE_DIRS}) + +target_compile_features(OpenSTA + PUBLIC cxx_auto_type + ) + +target_compile_options(OpenSTA PUBLIC ${STA_COMPILE_OPTIONS}) + +########################################################### +# Executable +########################################################### + +# compatibility with configure +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${STA_HOME}/app) + +# Note executable and lib name cannot be the same because +# on osx something is case insensitive. Using STA for the +# lib name results in "No rule to make target ../depend. +add_executable(sta app/Main.cc) +target_link_libraries(sta + OpenSTA + ${TCL_LIB} + ${CUDD_LIB} + ) +if (ZLIB_FOUND) + target_link_libraries(sta ${ZLIB_LIBRARIES}) +endif() + +message(STATUS "STA executable: ${STA_HOME}/app/sta") + +# g++ std::thread apparently still needs -pthreads. +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_link_libraries(sta -pthread) +endif() + +target_compile_options(sta PUBLIC ${STA_COMPILE_OPTIONS}) + +################################################################ +# Install +# cmake .. -DCMAKE_INSTALL_PREFIX= + +# executable +install(TARGETS sta DESTINATION bin) + +# library +install(TARGETS OpenSTA DESTINATION lib) + +# include +install(FILES ${STA_HEADERS} DESTINATION include) + +################################################################ + +add_custom_target(tags etags -o TAGS ${STA_SOURCE} ${STA_HEADERS} ${STA_TCL_FILES} ${SWIG_TCL_FILES} + WORKING_DIRECTORY ${STA_HOME} + DEPENDS ${STA_SOURCE} ${STA_HEADERS} ${STA_TCL_FILES} ${SWIG_TCL_FILES} + ) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..295a1e2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +FROM ubuntu:18.04 +LABEL author="James Cherry" +LABEL maintainer="Abdelrahman Hosny " + +# install basics +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && \ + apt-get install -y wget apt-utils git cmake gcc tcl-dev swig bison flex + +# download CUDD +RUN wget https://www.davidkebo.com/source/cudd_versions/cudd-3.0.0.tar.gz && \ + tar -xvf cudd-3.0.0.tar.gz && \ + rm cudd-3.0.0.tar.gz + +# install CUDD +RUN cd cudd-3.0.0 && \ + mkdir ../cudd && \ + ./configure --prefix=$HOME/cudd && \ + make && \ + make install + +# copy files and install OpenSTA +RUN mkdir OpenSTA +COPY . OpenSTA +RUN cd OpenSTA && \ + mkdir build && \ + cd build && \ + cmake .. -DCUDD=$HOME/cudd && \ + make + +# Run sta on entry +ENTRYPOINT ["OpenSTA/app/sta"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f7be2eb --- /dev/null +++ b/README.md @@ -0,0 +1,230 @@ +# Parallax Static Timing Analyzer + +OpenSTA is a gate level static timing verifier. As a stand-alone +executable it can be used to verify the timing of a design using +standard file formats. + +* Verilog netlist +* Liberty library +* SDC timing constraints +* SDF delay annotation +* SPEF parasitics + +OpenSTA uses a TCL command interpreter to read the design, specify +timing constraints and print timing reports. + +##### Clocks +* Generated +* Latency +* Source latency (insertion delay) +* Uncertainty +* Propagated/Ideal +* Gated clock checks +* Multiple frequency clocks + +##### Exception paths +* False path +* Multicycle path +* Min/Max path delay +* Exception points +* -from clock/pin/instance -through pin/net -to clock/pin/instance +* Edge specific exception points +* -rise_from/-fall_from, -rise_through/-fall_through, -rise_to/-fall_to + +##### Delay calculation +* Integrated Dartu/Menezes/Pileggi RC effective capacitance algorithm +* External delay calculator API + +##### Analysis +* Report timing checks -from, -through, -to, multiple paths to endpoint +* Report delay calculation +* Check timing setup + +##### Timing Engine +OpenSTA is architected to be easily bolted on to other tools as a +timing engine. By using a network adapter, OpenSTA can access the host +netlist data structures without duplicating them. + +* Query based incremental update of delays, arrival and required times +* Simulator to propagate constants from constraints and netlist tie high/low + +See doc/OpenSTA.pdf for complete documentiaton. + +## Getting Started + +OpenSTA can be run as a [Docker](https://www.docker.com/) container +or built as local executable with CMake. + +### Run using Docker +* Install Docker on [Windows](https://docs.docker.com/docker-for-windows/), [Mac](https://docs.docker.com/docker-for-mac/) or [Linux](https://docs.docker.com/install/). +* Navigate to the directory where you have the input files. +* Run OpenSTA as a binary using +```` +docker run -it -v $(pwd):/data openroad/opensta +```` + +4. From the interactive terminal, use OpenSTA commands. You can read input files from `/data` directory inside the docker container (e.g. `read_liberty /data/liberty.lib`). You can use OpenSTA in non-interactive mode by passing a command file using the `-f` flag as follows. +``` +docker run -it -v $(pwd):/data openroad/opensta -f /data/cmd_file +``` +Note that the path after `-f` is the path inside container, not on the guest machine. + +### Prerequisites + +The build dependency versions are show below. Other versions may +work, but these are the versions used for development. + +``` + from Ubuntu Xcode + 18.04.1 10.1 +cmake 3.9 +clang 9.1.0 10.0.0 +gcc 3.3.2 7.3.0 +tcl 8.2 8.6 8.6.6 +swig 1.3.28 3.0.12 3.0.12 +bison 1.35 3.0.4 2.3 +flex 2.5.4 2.6.4 2.5.35 +``` + +These packages are **optional**: + +``` +libz 1.1.4 1.2.5 1.2.8 +cudd 2.4.1 3.0.0 +``` + +CUDD is a binary decision diageram (BDD) package that is used to improve conditional timing arc handling. OpenSTA does not require it to be installed. It is available [here](https://www.davidkebo.com/source/cudd_versions/cudd-3.0.0.tar.gz) or [here](https://sourceforge.net/projects/cudd-mirror/). + +Note that the file hierarchy of the CUDD installation changed with version 3.0. +Some changes to CMakeLists.txt are required to support older versions. + +When building CUDD you may use the `--prefix ` option to `configure` to +install in a location other than the default (`/usr/local/lib`). +``` +cd $HOME/cudd-3.0.0 +mkdir $HOME/cudd +./configure --prefix $HOME/cudd +make +make install +``` +To not use CUDD specify `CUDD=0`. +Cmake looks for the CUDD library in `CUDD/lib, CUDD/cudd/lib` +and for the header in `CUDD/include, CUDD/cudd/include`. +``` +# equivalent to -DCUDD=0 +cmake .. +or +cmake .. -DCUDD=0 +or +# look in ~/cudd/lib, ~/cudd/include +cmake .. -DCUDD=$HOME/cudd +or +# look in /usr/local/lib/cudd, /usr/local/include/cudd +cmake .. -DCUDD=/usr/local +``` + +The Zlib library is an optional. If CMake finds libz, OpenSTA can +read Verilog, SDF, SPF, and SPEF files compressed with gzip. + +### Installing with CMake + +Use the following commands to checkout the git repository and build the +OpenSTA library and excutable. + +``` +git clone https://github.com/The-OpenROAD-Project/OpenSTA.git +cd OpenSTA +mkdir build +cd build +cmake .. +make +``` +The default build type is release to compile optimized code. +The resulting executable is in `app/sta`. +The library without a `main()` procedure is `app/libSTA.a`. + +Optional CMake variables passed as -D= arguments to CMake are show below. + +``` +CMAKE_BUILD_TYPE DEBUG|RELEASE +CMAKE_CXX_FLAGS - additional compiler flags +TCL_LIB - path to tcl library +TCL_HEADER - path to tcl.h +CUDD - path to cudd installation +ZLIB_ROOT - path to zlib +CMAKE_INSTALL_PREFIX +``` + +If `TCL_LIB` is specified the CMake script will attempt to locate the +header from the library path. + +The default install directory is `/usr/local`. +To install in a different directory with CMake use: + +``` +cmake .. -DCMAKE_INSTALL_PREFIX= +``` + +Alternatively, you can use the `DESTDIR` variable with make. + +``` +make DESTDIR= install +``` + +If you make changes to `CMakeLists.txt` you may need to clean out +existing CMake cached variable values by deleting all of the +files in the build directory. + +## Authors + +* James Cherry + +* William Scott authored the arnoldi delay calculator at Blaze, Inc which was subsequently licensed to Nefelus, Inc that has graciously contributed it to OpenSTA. + +## Bug Reports + +Use the Issues tab on the github repository to report bugs. + +Each issue/bug should be a separate issue. The subject of the issue +should be a short description of the problem. Attach a test case to +reproduce the issue as described below. Issues without test cases are +unlikely to get a response. + +The files in the test case should be collected into a directory named +YYYYMMDD where YYYY is the year, MM is the month, and DD is the +day (this format allows "ls" to report them in chronological order). +The contents of the directory should be collected into a compressed +tarfile named YYYYMMDD.tgz. + +The test case should have a tcl command file recreates the issue named +run.tcl. If there are more than one command file using the same data +files, there should be separate command files, run1.tcl, run2.tcl +etc. The bug report can refer to these command files by name. + +Command files should not have absolute filenames like +"/home/cho/OpenSTA_Request/write_path_spice/dump_spice" in them. +These obviously are not portable. Use filenames relative to the test +case directory. + +## License + +OpenSTA is dual licensed. It is released under GPL v3 as OpenSTA and +is also licensed for commerical applications by Parallax Software without +the GPL's requirements. + +OpenSTA, Static Timing Analyzer +Copyright (c) 2019, Parallax Software, Inc. + +This 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 . + diff --git a/app/Main.cc b/app/Main.cc new file mode 100644 index 0000000..6d1ff7f --- /dev/null +++ b/app/Main.cc @@ -0,0 +1,55 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "StaConfig.hh" // STA_VERSION +#include "StringUtil.hh" +#include "Sta.hh" +#include "StaMain.hh" + +using sta::stringEq; +using sta::Sta; +using sta::staMain; +using sta::showUsage; + +// Swig uses C linkage for init functions. +extern "C" { +extern int Sta_Init(Tcl_Interp *interp); +} + +namespace sta { +extern const char *tcl_inits[]; +} + +int +main(int argc, + char *argv[]) +{ + if (argc == 2 && stringEq(argv[1], "-help")) { + showUsage(argv[0]); + return 0; + } + else if (argc == 2 && stringEq(argv[1], "-version")) { + printf("%s\n", STA_VERSION); + return 0; + } + else { + Sta *sta = new Sta; + staMain(sta, argc, argv, Sta_Init, sta::tcl_inits); + return 0; + } +} diff --git a/app/Makefile.am b/app/Makefile.am new file mode 100644 index 0000000..fb074b0 --- /dev/null +++ b/app/Makefile.am @@ -0,0 +1,62 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +bin_PROGRAMS = sta + +include_HEADERS = \ + StaMain.hh + +sta_SOURCES = \ + Main.cc \ + StaMain.cc \ + StaApp_wrap.cc \ + TclInitVar.cc + +NETWORK_LIBS = \ + ../verilog/libverilog.la + +sta_DEPENDENCIES = $(NETWORK_LIBS) $(STA_LIBS) $(CUDD_LIBS) + +sta_LDADD = $(NETWORK_LIBS) $(STA_LIBS) $(CUDD_LIBS) + +StaApp_wrap.cc: $(SWIG_DEPEND) StaApp.i ../verilog/Verilog.i + $(SWIG) $(SWIG_FLAGS) -namespace -prefix sta \ + -o StaApp_wrap.cc StaApp.i + ../etc/SwigCleanup.tcl StaApp_wrap.cc + +# TCL files included as part of the executable. +# These files are encoded and shipped as part of the executable +# so that they do not have to be installed on the client host. + +TclInitVar.cc: ../etc/TclEncode.tcl $(TCL_INIT_FILES) + ../etc/TclEncode.tcl TclInitVar.cc "tcl_inits" \ + $(TCL_INIT_FILES) ../verilog/Verilog.tcl + +EXTRA_DIST = \ + StaApp.i + +# TclInitVar.cc is derived and TCL version specific, so don't dist it. +dist-hook: + rm -rf $(distdir)/TclInitVar.cc + +MAINTAINERCLEANFILES = \ + StaApp_wrap.cc \ + TclInitVar.cc + +libs: $(sta_OBJECTS) + +xtags: $(SOURCES) $(HEADERS) + etags -a -o ../TAGS $(SOURCES) $(HEADERS) diff --git a/app/StaApp.i b/app/StaApp.i new file mode 100644 index 0000000..f38c469 --- /dev/null +++ b/app/StaApp.i @@ -0,0 +1,25 @@ +%module sta + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +%include "StaException.i" +%include "StaTcl.i" +%include "Verilog.i" +%include "NetworkEdit.i" +%include "Sdf.i" +%include "DelayCalc.i" +%include "Parasitics.i" diff --git a/app/StaMain.cc b/app/StaMain.cc new file mode 100644 index 0000000..0ee8fde --- /dev/null +++ b/app/StaMain.cc @@ -0,0 +1,231 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include "Machine.hh" +#include "StringUtil.hh" +#include "Vector.hh" +#include "Sta.hh" +#include "StaMain.hh" + +namespace sta { + +typedef sta::Vector SwigInitFuncSeq; + +// "Arguments" passed to staTclAppInit. +static int sta_argc; +static char **sta_argv; +static const char **sta_tcl_inits; +static SwigInitFunc sta_swig_init; + +static const char *init_filename = "[file join $env(HOME) .sta]"; + +void +staMain(Sta *sta, + int argc, + char *argv[], + SwigInitFunc swig_init, + const char *tcl_inits[]) +{ + initSta(); + + Sta::setSta(sta); + sta->makeComponents(); + + int thread_count = parseThreadsArg(argc, argv); + sta->setThreadCount(thread_count); + + staSetupAppInit(argc, argv, swig_init, tcl_inits); + // Set argc to 1 so Tcl_Main doesn't source any files. + // Tcl_Main never returns. + Tcl_Main(1, argv, staTclAppInit); +} + +int +parseThreadsArg(int &argc, + 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); + else + fprintf(stderr,"Warning: -threads must be max or a positive integer.\n"); + } + return 1; +} + +// Set globals to pass to staTclAppInit. +void +staSetupAppInit(int argc, + char *argv[], + SwigInitFunc swig_init, + const char *tcl_inits[]) +{ + sta_argc = argc; + sta_argv = argv; + sta_tcl_inits = tcl_inits; + sta_swig_init = swig_init; +} + +// Tcl init executed inside Tcl_Main. +int +staTclAppInit(Tcl_Interp *interp) +{ + int argc = sta_argc; + char **argv = sta_argv; + + // source init.tcl + Tcl_Init(interp); + + // Define swig commands. + sta_swig_init(interp); + + Sta *sta = Sta::sta(); + sta->setTclInterp(interp); + + // Eval encoded sta TCL sources. + evalTclInit(interp, sta_tcl_inits); + + if (!findCmdLineFlag(argc, argv, "-no_splash")) + Tcl_Eval(interp, "sta::show_splash"); + + // Import exported commands from sta namespace to global namespace. + Tcl_Eval(interp, "sta::define_sta_cmds"); + Tcl_Eval(interp, "namespace import sta::*"); + + if (!findCmdLineFlag(argc, argv, "-no_init")) + sourceTclFile(init_filename, true, true, interp); + + bool exit_after_cmd_file = findCmdLineFlag(argc, argv, "-exit"); + + if (argc > 2 || + (argc > 1 && argv[1][0] == '-')) + showUsage(argv[0]); + else { + if (argc == 2) { + char *cmd_file = argv[1]; + if (cmd_file) { + sourceTclFile(cmd_file, false, false, interp); + if (exit_after_cmd_file) + exit(EXIT_SUCCESS); + } + } + } + return TCL_OK; +} + +bool +findCmdLineFlag(int &argc, + char *argv[], + const char *flag) +{ + for (int i = 1; i < argc; i++) { + char *arg = argv[i]; + if (stringEq(arg, flag)) { + // remove flag from argv. + for (int j = i + 1; j < argc; j++, i++) + argv[i] = argv[j]; + argc--; + return true; + } + } + return false; +} + +char * +findCmdLineKey(int &argc, + char *argv[], + const char *key) +{ + for (int i = 1; i < argc; i++) { + char *arg = argv[i]; + if (stringEq(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]; + argc -= 2; + return value; + } + } + return nullptr; +} + +// Use overridden version of source to echo cmds and results. +void +sourceTclFile(const char *filename, + bool echo, + bool verbose, + Tcl_Interp *interp) +{ + string cmd; + stringPrint(cmd, "source %s%s%s", + echo ? "-echo " : "", + verbose ? "-verbose " : "", + filename); + Tcl_Eval(interp, cmd.c_str()); +} + +void +evalTclInit(Tcl_Interp *interp, + const char *inits[]) +{ + size_t length = 0; + for (const char **e = inits; *e; e++) { + const char *init = *e; + length += strlen(init); + } + char *unencoded = new char[length / 3 + 1]; + char *u = unencoded; + for (const char **e = inits; *e; e++) { + const char *init = *e; + size_t init_length = strlen(init); + for (const char *s = init; s < &init[init_length]; s += 3) { + char code[4] = {s[0], s[1], s[2], '\0'}; + char ch = atoi(code); + *u++ = ch; + } + } + *u = '\0'; + if (Tcl_Eval(interp, unencoded) != TCL_OK) { + // Get a backtrace for the error. + Tcl_Eval(interp, "$errorInfo"); + const char *tcl_err = Tcl_GetStringResult(interp); + fprintf(stderr, "Error: TCL init script: %s.\n", tcl_err); + fprintf(stderr, " Try deleting app/TclInitVar.cc and rebuilding.\n"); + exit(0); + } + delete [] unencoded; +} + +void +showUsage(const char * prog) +{ + 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 .sta init file\n"); + 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"); +} + +} // namespace diff --git a/app/StaMain.hh b/app/StaMain.hh new file mode 100644 index 0000000..c85bbd3 --- /dev/null +++ b/app/StaMain.hh @@ -0,0 +1,76 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_APP_H +#define STA_APP_H + +struct Tcl_Interp; + +namespace sta { + +class Sta; +typedef int (*SwigInitFunc)(Tcl_Interp *); + +// The swig_init function is called to define the swig interface +// functions to the tcl interpreter. +void +staMain(Sta *sta, + int argc, + char *argv[], + SwigInitFunc swig_init, + const char *tcl_inits[]); + +// Set arguments passed to staTclAppInit inside the tcl interpreter. +void +staSetupAppInit(int argc, + char *argv[], + SwigInitFunc swig_init, + const char *tcl_inits[]); + +int +staTclAppInit(Tcl_Interp *interp); + +// TCL init files are encoded into the string init using the three +// digit decimal equivalent for each ascii character. This function +// unencodes the string and evals it. This packages the TCL init +// files as part of the executable so they don't have to be shipped as +// separate files that have to be located and loaded at run time. +void +evalTclInit(Tcl_Interp *interp, + const char *inits[]); + +bool +findCmdLineFlag(int &argc, + char *argv[], + const char *flag); +char * +findCmdLineKey(int &argc, + char *argv[], + const char *key); + +void +showUsage(const char *prog); +int +parseThreadsArg(int &argc, + char *argv[]); +void +sourceTclFile(const char *filename, + bool echo, + bool verbose, + Tcl_Interp *interp); + +} // namespace +#endif diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake new file mode 100644 index 0000000..8ab03bc --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake @@ -0,0 +1,168 @@ +# - Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can +# trust the values of the variables in your build system. +# +# get_git_head_revision( [ ...]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting +# the output so that it tests false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# git_local_changes() +# +# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. +# Uses the return code of "git diff-index --quiet HEAD --". +# Does not regard untracked files. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +function(get_git_head_revision _refspecvar _hashvar) + set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories + set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") + get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) + if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) + # We have reached the root directory, we are not in git + set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + return() + endif() + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + endwhile() + # check if this is a submodule + if(NOT IS_DIRECTORY ${GIT_DIR}) + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${GIT_DIR}/HEAD") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" + @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) + set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + return() + endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + #message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + describe + ${hash} + ${ARGN} + WORKING_DIRECTORY + "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(git_local_changes _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + return() + endif() + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + diff-index --quiet HEAD -- + WORKING_DIRECTORY + "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(res EQUAL 0) + set(${_var} "CLEAN" PARENT_SCOPE) + else() + set(${_var} "DIRTY" PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake/GetGitRevisionDescription.cmake.in b/cmake/GetGitRevisionDescription.cmake.in new file mode 100644 index 0000000..6d8b708 --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake.in @@ -0,0 +1,41 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + else() + configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) + file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) + if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") + set(HEAD_HASH "${CMAKE_MATCH_1}") + endif() + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() diff --git a/dcalc/ArcDelayCalc.cc b/dcalc/ArcDelayCalc.cc new file mode 100644 index 0000000..0b403a7 --- /dev/null +++ b/dcalc/ArcDelayCalc.cc @@ -0,0 +1,54 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "TimingModel.hh" +#include "TimingArc.hh" +#include "GraphDelayCalc.hh" +#include "ArcDelayCalc.hh" + +namespace sta { + +ArcDelayCalc::ArcDelayCalc(StaState *sta): + StaState(sta) +{ +} + +TimingModel * +ArcDelayCalc::model(TimingArc *arc, + const DcalcAnalysisPt *dcalc_ap) const +{ + const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); + TimingArc *corner_arc = arc->cornerArc(dcalc_ap->libertyIndex()); + return corner_arc->model(op_cond); + +} + +GateTimingModel * +ArcDelayCalc::gateModel(TimingArc *arc, + const DcalcAnalysisPt *dcalc_ap) const +{ + return dynamic_cast(model(arc, dcalc_ap)); +} + +CheckTimingModel * +ArcDelayCalc::checkModel(TimingArc *arc, + const DcalcAnalysisPt *dcalc_ap) const +{ + return dynamic_cast(model(arc, dcalc_ap)); +} + +} // namespace diff --git a/dcalc/ArcDelayCalc.hh b/dcalc/ArcDelayCalc.hh new file mode 100644 index 0000000..e9997a0 --- /dev/null +++ b/dcalc/ArcDelayCalc.hh @@ -0,0 +1,146 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_ARC_DELAY_CALC_H +#define STA_ARC_DELAY_CALC_H + +#include +#include "DisallowCopyAssign.hh" +#include "MinMax.hh" +#include "Delay.hh" +#include "StaState.hh" +#include "LibertyClass.hh" +#include "NetworkClass.hh" + +namespace sta { + +using std::string; + +class Parasitic; +class DcalcAnalysisPt; + +// Delay calculator class hierarchy. +// ArcDelayCalc +// UnitDelayCalc +// LumpedCapDelayCalc +// RCDelayCalc +// SimpleRCDelayCalc +// DmpCeffDelayCalc +// DmpCeffElmoreDelayCalc +// DmpCeffTwoPoleDelayCalc + +// Abstract class to interface to a delay calculator primitive. +class ArcDelayCalc : public StaState +{ +public: + explicit ArcDelayCalc(StaState *sta); + virtual ~ArcDelayCalc() {} + virtual ArcDelayCalc *copy() = 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 TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap) = 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 void inputPortDelay(const Pin *port_pin, + float in_slew, + const TransRiseFall *tr, + Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap) = 0; + + // Find the delay and slew for arc driving drvr_pin. + virtual void gateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + // Pass in load_cap or drvr_parasitic. + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) = 0; + // Find the wire delay and load slew of a load pin. + // Called after inputPortDelay or gateDelay. + virtual void loadDelay(const Pin *load_pin, + // Return values. + ArcDelay &wire_delay, + Slew &load_slew) = 0; + virtual void setMultiDrvrSlewFactor(float factor) = 0; + // Ceff for parasitics with pi models. + virtual float ceff(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap) = 0; + + // Find the delay for a timing check arc given the arc's + // from/clock, to/data slews and related output pin parasitic. + virtual void checkDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &from_slew, + const Slew &to_slew, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &margin) = 0; + // Report delay and slew calculation. + virtual void reportGateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + // Pass in load_cap or drvr_parasitic. + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + int digits, + string *result) = 0; + // Report timing check delay calculation. + virtual void reportCheckDelay(const LibertyCell *cell, + TimingArc *arc, + const Slew &from_slew, + const char *from_slew_annotation, + const Slew &to_slew, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + int digits, + string *result) = 0; + virtual void finishDrvrPin() = 0; + +protected: + GateTimingModel *gateModel(TimingArc *arc, + const DcalcAnalysisPt *dcalc_ap) const; + CheckTimingModel *checkModel(TimingArc *arc, + const DcalcAnalysisPt *dcalc_ap) const; + TimingModel *model(TimingArc *arc, + const DcalcAnalysisPt *dcalc_ap) const; + +private: + DISALLOW_COPY_AND_ASSIGN(ArcDelayCalc); +}; + +} // namespace +#endif diff --git a/dcalc/Arnoldi.hh b/dcalc/Arnoldi.hh new file mode 100644 index 0000000..af29794 --- /dev/null +++ b/dcalc/Arnoldi.hh @@ -0,0 +1,83 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +// (c) 2018 Nefelus, Inc. +// +// Author: W. Scott + +#ifndef ARNOLDI_H +#define ARNOLDI_H + +#include "ConcreteParasiticsPvt.hh" + +namespace sta { + +struct delay_work; +class rcmodel; + +class GateTableModel; +class Pin; + +// +// single-driver arnoldi model +// +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); + + // + // calculate poles/residues for given rdrive + // + void calculate_poles_res(delay_work *D,double rdrive); + +public: + int order; + int n; // number of terms, including driver + double *d; // [order] + double *e; // [order-1] + double **U; // [order][n] + double ctot; + double sqc; +}; + +// This is the rcmodel, without Rd. +// n is the number of terms +// The vectors U[j] are of size n +class rcmodel : public ConcreteParasitic, + public arnoldi1 +{ +public: + rcmodel(); + virtual ~rcmodel(); + virtual float capacitance() const; + + const Pin **pinV; // [n] +}; + +struct timing_table +{ + GateTableModel *table; + const LibertyCell *cell; + const Pvt *pvt; + float in_slew; + float relcap; +}; + +} // namespace +#endif diff --git a/dcalc/Arnoldi.txt b/dcalc/Arnoldi.txt new file mode 100644 index 0000000..63ebad5 --- /dev/null +++ b/dcalc/Arnoldi.txt @@ -0,0 +1,57 @@ +The method is used for simulation with a time-and-voltage-dependent +current source. But it is simpler to describe with a linear driver. +Suppose we are given: + voltage nodes 1,..n initialized to V[j]=1.0 + a resistor network described by a conductance matrix G[j,k] + a drive resistance from node 1 to ground, Rdrv + capacitance of the nodes, c[j], also written as a + diagonal matrix C[j,k] +The node voltages will fall to zero with time. +Matrix equation: + GV+CdV/dt = -(V[0]/Rdrv)e0 +where e0 is the unit vector (1,0,0,..0). +Let G' be the matrix formed by taking G and adding 1/Rdrv in the +[0,0] position. Then we are solving G'V + CdV/dt = 0. +Let R be the inverse of G'. (In implementation, R may not actually +be formed as a matrix, instead some method of producing RV given V). +The exact solution would diagonalize sqrt(C) R sqrt(C). + +The Arnoldi method takes a matrix M and a vector of interest V, and +considers the subspace of vectors near V in terms of the action of M, +that is, the space spanned by V, MV, MMV, etc, for a small number of +powers. We do this here, but instead of finding the part of MV orthogonal +to V, we find the part of RCV that is C-orthogonal of V. We use C +as the metric, so the basis vectors U0, U1, U2 that we generate satisfy + Ui.CUj = (i==j?1:0) + +U0 = (1,1,..1)/sqrt(sum C) + representing the initial value of V, V[j] = sqrt(n)U0[j] at t=0. + sum(C) = C[0]+..C[n-1], so U0.C U0 = 1. +Let: + W = R C U0 + d0 = U0.C W + W' = W - d0 U0 + e0 = sqrt(W'.C W') + U1 = W'/e0 +Then: U0.C U0 = U1.C U1 = 1, U0.C U1 = 0, and + R C U0 = d0 U0 + e0 U1 +Next step: + W = R C U1 + d1 = U1.C W + W' = W - d1 U1 - e0 U0 + e1 = sqrt(W'.C W') + U2 = W'/e1 +and we have U2.C U2 = 1, U2.C U1 = U2.C U0 = 0, and + RC U1 = e0 U0 + d1 U1 + e1 U2 +In this way, RC, which in nonsymmetric in the original basis, +becomes a symmetric tridiagonal matrix in the U0,U1,.. basis. +We stop at, say, U3. The resulting 4x4 tridiagonal matrix is +positive definite, because it is the projection of sqrt(C)R sqrt(C) +to a subspace. So the eigenvalues of this small tridiagonal matrix +are guaranteed to be positive. This is the advantage over AWE. + +In the actual implementation, I remember there was some way of +isolating the node 0 where drive resistance is attached, so that the +tridiagonal matrix (d,e) can be recalculated without knowing Rdrv, +and then just the first d0,e0 updated when the Rdrv is known, or +when Rdrv changes in a simulation. diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc new file mode 100644 index 0000000..a8ab186 --- /dev/null +++ b/dcalc/ArnoldiDelayCalc.cc @@ -0,0 +1,1490 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +// (c) 2018 Nefelus, Inc. +// +// Author: W. Scott + +#include +#include // abs +#include "Machine.hh" +#include "Report.hh" +#include "Debug.hh" +#include "Units.hh" +#include "Liberty.hh" +#include "TimingModel.hh" +#include "TimingArc.hh" +#include "TableModel.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Parasitics.hh" +#include "Sdc.hh" +#include "DcalcAnalysisPt.hh" +#include "DelayCalc.hh" +#include "ArcDelayCalc.hh" +#include "RCDelayCalc.hh" +#include "GraphDelayCalc.hh" +#include "Arnoldi.hh" +#include "ArnoldiReduce.hh" +#include "ArnoldiDelayCalc.hh" + +namespace sta { + +// wireload8 is n^2 +// do not delete arnoldi parasitics +// handle rspf parasitics? + +// mv static functions to ArnoldiDelayCalc +// need slew only lookup for +// ra_delay +// ra_get_r +// ra_get_s + +using std::abs; + +struct delay_work; +struct delay_c; + +//////////////////////////////////////////////////////////////// + +static delay_work *delay_work_create(); +static void +delay_work_destroy(delay_work *D); +static double * +delay_work_get_residues(delay_work *D, + int term_index); + +static bool +tridiagEV(int n,double *d,double *e,double *p,double **v); + +////////////////////////////////////////////////////////////// + +struct delay_c +{ + double slew_derate; + double vlo; + double vhi; + double vlg; + double smin; + double x1; + double y1; + double vmid; // falling convention, should be >= 0.5 +}; + +// workspace for pole-residue -> delay calculations +// delay_work +// max order is 32 +struct delay_work +{ + double slew_derate; + double slew_factor; // (0,1.0] table_slew = slew_factor * full_slew + delay_c cV[2]; + delay_c *c; + + double lo_thresh; + double hi_thresh; + + int nmax; + double poles[32]; // 1/tau + double **resi; // resi[jrec][h] h=0,..order + double *v[32]; + double *w[32]; + double aa[32]; +}; + +//////////////////////////////////////////////////////////////// + +class ArnoldiDelayCalc : public RCDelayCalc +{ +public: + ArnoldiDelayCalc(StaState *sta); + virtual ~ArnoldiDelayCalc(); + virtual ArcDelayCalc *copy(); + virtual Parasitic *findParasitic(const Pin *drvr_pin, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap); + virtual void gateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + // Pass in load_cap or drvr_parasitic. + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew); + virtual void loadDelay(const Pin *load_pin, + // Return values. + ArcDelay &wire_delay, + Slew &load_slew); + virtual void inputPortDelay(const Pin *port_pin, + float in_slew, + const TransRiseFall *tr, + Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap); + virtual void reportGateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + int digits, + string *result); + void delay_work_set_thresholds(delay_work *D, + double lo, + double hi, + bool rising, + double derate); + +private: + void gateDelaySlew(const LibertyCell *drvr_cell, + GateTableModel *table_model, + const Slew &in_slew, + float related_out_cap, + const Pvt *pvt, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew); + void ar1_ceff_delay(delay_work *D,timing_table *tab, arnoldi1 *mod, + double *delays, double *slews); + double ra_rdelay_1(timing_table *tab, + double ctot); + double ra_get_r(delay_work *D, + timing_table *tab, + double rdelay, + double ctot); + double ra_get_s(delay_work *D, + timing_table *tab, + double r, + double c); + void ra_solve_for_s(delay_work *D, + 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); + void pr_solve3(double s, + 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 ra_solve_for_t(double p, + double s, + double v); + void ra_solve_for_pt(double ps, + double v, + double *pt, + double *d); + void ra_calc_c(double lo, + double hi, + double *c_smin, + double *c_x1, + double *c_y1); + + rcmodel *rcmodel_; + int _pinNmax; + double *_delayV; + double *_slewV; + int pin_n_; + bool input_port_; + ArnoldiReduce *reduce_; + delay_work *delay_work_; +}; + +ArcDelayCalc * +makeArnoldiDelayCalc(StaState *sta) +{ + return new ArnoldiDelayCalc(sta); +} + +ArnoldiDelayCalc::ArnoldiDelayCalc(StaState *sta) : + RCDelayCalc(sta), + reduce_(new ArnoldiReduce(sta)), + delay_work_(delay_work_create()) +{ + _pinNmax = 1024; + _delayV = (double*)malloc(_pinNmax * sizeof(double)); + _slewV = (double*)malloc(_pinNmax * sizeof(double)); +} + +ArcDelayCalc * +ArnoldiDelayCalc::copy() +{ + return new ArnoldiDelayCalc(this); +} + +ArnoldiDelayCalc::~ArnoldiDelayCalc() +{ + delay_work_destroy(delay_work_); + free(_delayV); + free(_slewV); + delete reduce_; +} + +Parasitic * +ArnoldiDelayCalc::findParasitic(const Pin *drvr_pin, + const TransRiseFall *drvr_tr, + const DcalcAnalysisPt *dcalc_ap) +{ + // set_load has precidence over parasitics. + if (!sdc_->drvrPinHasWireCap(drvr_pin)) { + const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + Parasitic *parasitic_network = + parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + bool delete_parasitic_network = false; + + const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); + const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); + const Corner *corner = dcalc_ap->corner(); + if (parasitic_network == nullptr) { + Wireload *wireload = sdc_->wireloadDefaulted(cnst_min_max); + if (wireload) { + float pin_cap, wire_cap, fanout; + bool has_wire_cap; + graph_delay_calc_->netCaps(drvr_pin, drvr_tr, dcalc_ap, + pin_cap, wire_cap, fanout, has_wire_cap); + parasitic_network = parasitics_->makeWireloadNetwork(drvr_pin, wireload, + fanout, op_cond, + parasitic_ap); + delete_parasitic_network = true; + } + } + + if (parasitic_network) { + Parasitic *parasitic = + reduce_->reduceToArnoldi(parasitic_network, + drvr_pin, + parasitic_ap->couplingCapFactor(), + drvr_tr, op_cond, corner, + cnst_min_max, parasitic_ap); + if (delete_parasitic_network) { + Net *net = network_->net(drvr_pin); + parasitics_->deleteParasiticNetwork(net, parasitic_ap); + } + reduced_parasitic_drvrs_.push_back(drvr_pin); + return parasitic; + } + } + return nullptr; +} + +void +ArnoldiDelayCalc::inputPortDelay(const Pin *drvr_pin, + float in_slew, + const TransRiseFall *tr, + Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap) +{ + RCDelayCalc::inputPortDelay(drvr_pin, in_slew, tr, parasitic, dcalc_ap); + rcmodel_ = nullptr; + _delayV[0] = 0.0; + _slewV[0] = in_slew; + + int j; + if (parasitic) { + rcmodel_ = reinterpret_cast(parasitic); + pin_n_ = rcmodel_->n; + if (pin_n_ >= _pinNmax) { + _pinNmax *= 2; + if (pin_n_ >= _pinNmax) _pinNmax += pin_n_; + _pinNmax *= 2; + _delayV = (double*)realloc(_delayV,_pinNmax * sizeof(double)); + _slewV = (double*)realloc(_slewV,_pinNmax * sizeof(double)); + } + pin_n_ = 1; + + pin_n_ = rcmodel_->n; + double slew_derate = drvr_library_->slewDerateFromLibrary(); + double lo_thresh = drvr_library_->slewLowerThreshold(drvr_tr_); + double hi_thresh = drvr_library_->slewUpperThreshold(drvr_tr_); + bool rising = (drvr_tr_ == TransRiseFall::rise()); + delay_work_set_thresholds(delay_work_, lo_thresh, hi_thresh, rising, + slew_derate); + delay_c *c = delay_work_->c; + double c_log = c->vlg; + + for (j=1;jelmore(j); + _delayV[j] = 0.6931472*elmore; + _slewV[j] = in_slew + c_log*elmore/slew_derate; + } + } +} + +void +ArnoldiDelayCalc::gateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) +{ + input_port_ = false; + drvr_tr_ = arc->toTrans()->asRiseFall(); + drvr_library_ = drvr_cell->libertyLibrary(); + drvr_parasitic_ = drvr_parasitic; + ConcreteParasitic *drvr_cparasitic = + reinterpret_cast(drvr_parasitic); + rcmodel_ = dynamic_cast(drvr_cparasitic); + GateTimingModel *model = gateModel(arc, dcalc_ap); + GateTableModel *table_model = dynamic_cast(model); + if (table_model && rcmodel_) + gateDelaySlew(drvr_cell, table_model, in_slew, + related_out_cap, pvt, + gate_delay, drvr_slew); + else + LumpedCapDelayCalc::gateDelay(drvr_cell, arc, in_slew, load_cap, + drvr_parasitic, related_out_cap, pvt, + dcalc_ap, gate_delay, drvr_slew); + drvr_slew_ = drvr_slew; + multi_drvr_slew_factor_ = 1.0F; +} + +void +ArnoldiDelayCalc::gateDelaySlew(const LibertyCell *drvr_cell, + GateTableModel *table_model, + const Slew &in_slew, + float related_out_cap, + const Pvt *pvt, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) +{ + pin_n_ = rcmodel_->n; + if (pin_n_ >= _pinNmax) { + _pinNmax *= 2; + if (pin_n_ >= _pinNmax) _pinNmax += pin_n_; + _delayV = (double*)realloc(_delayV,_pinNmax * sizeof(double)); + _slewV = (double*)realloc(_slewV,_pinNmax * sizeof(double)); + } + + pin_n_ = rcmodel_->n; + if (table_model) { + double slew_derate = drvr_library_->slewDerateFromLibrary(); + double lo_thresh = drvr_library_->slewLowerThreshold(drvr_tr_); + double hi_thresh = drvr_library_->slewUpperThreshold(drvr_tr_); + bool rising = (drvr_tr_ == TransRiseFall::rise()); + delay_work_set_thresholds(delay_work_, lo_thresh, hi_thresh, rising, + slew_derate); + if (rcmodel_->order > 0) { + timing_table tab; + tab.table = table_model; + tab.cell = drvr_cell; + tab.pvt = pvt; + tab.in_slew = delayAsFloat(in_slew); + tab.relcap = related_out_cap; + ar1_ceff_delay(delay_work_, &tab, rcmodel_, + _delayV, _slewV); + } + gate_delay = _delayV[0]; + drvr_slew = _slewV[0]; + } +} + +void +ArnoldiDelayCalc::loadDelay(const Pin *load_pin, + // Return values. + ArcDelay &wire_delay, + Slew &load_slew) +{ + wire_delay = 0.0; + load_slew = drvr_slew_ * multi_drvr_slew_factor_; + if (rcmodel_) { + // HACK + for (int i = 0; i < rcmodel_->n; i++) { + if (rcmodel_->pinV[i] == load_pin) { + wire_delay = _delayV[i] - _delayV[0]; + load_slew = _slewV[i] * multi_drvr_slew_factor_; + break; + } + } + } + thresholdAdjust(load_pin, wire_delay, load_slew); +} + +void +ArnoldiDelayCalc::reportGateDelay(const LibertyCell *, + TimingArc *, + const Slew &, + float, + Parasitic *, + float, + const Pvt *, + const DcalcAnalysisPt *, + int, + string *) +{ +} + +//////////////////////////////////////////////////////////////// + +// +// arnoldi1.cpp +// + +arnoldi1::~arnoldi1() +{ + free(d); + free(U); +} + +double +arnoldi1::elmore(int k) +{ + if (order==0) return 0.0; + if (order==1) return d[0]; + double sqctot = 1.0/U[0][0]; + double tau = d[0] + e[0]*U[1][k]*sqctot; + return tau; +} + +delay_work * +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->lo_thresh = 0.0; + D->hi_thresh = 0.0; + D->slew_derate = 0.0; + D->slew_factor = 0.0; + for (j=0;j<2;j++) { + D->cV[j].vlo = 0.0; + D->cV[j].vhi = 0.0; + D->cV[j].vlg = 0.0; + D->cV[j].smin = 0.0; + D->cV[j].x1 = 0.0; + D->cV[j].y1 = 0.0; + D->cV[j].vmid = 0.0; + } + D->c = D->cV; + return D; +} + +static void +delay_work_destroy(delay_work *D) +{ + free(D->resi[0]); + free(D->resi); + free(D->v[0]); + free(D->w[0]); + free(D); +} + +static void +delay_work_alloc(delay_work *D,int n) +{ + if (n<=D->nmax) return; + free(D->w[0]); + free(D->resi[0]); + free(D->resi); + D->nmax *= 2; + if (n > D->nmax) D->nmax = n; + 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; +} + +void +ArnoldiDelayCalc::delay_work_set_thresholds(delay_work *D, + double lo, + double hi, + bool rising, + double derate) +{ + double mid = 0.5; // 0.0:1.0 + int i = rising?1:0; + D->c = D->cV+ i; + // WRONG + bool changed = (lo != D->c->vlo || hi != D->c->vhi); + if (changed) { + if (!(lo>0.01 && hi<0.99)) { + lo = 0.1; + hi = 0.9; + derate = 0.8; + } + D->c->slew_derate = derate; + D->c->vlo = lo; + D->c->vhi = hi; + 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->lo_thresh = D->c->vlo; + D->hi_thresh = D->c->vhi; + D->slew_derate = derate; + double measured_swing = D->c->vhi - D->c->vlo; + double reported_swing = measured_swing/D->slew_derate; + D->slew_factor = reported_swing; +} + +static double * +delay_work_get_residues(delay_work *D,int term_index) +{ + return D->resi[term_index]; +} + +////////////////////////////////////////////////////////////// +// +// calculate_poles_res +// + +void arnoldi1::calculate_poles_res(delay_work *D,double rdrive) +{ + if (n > D->nmax) delay_work_alloc(D,n); + double *p = D->poles; + double **v = D->v; + double **w = D->w; + double *aa = D->aa; + double **resi = D->resi; + int h,j,k; + double sum, dsave; + + dsave = d[0]; + d[0] += rdrive*ctot; + if (!tridiagEV(order,d,e,p,v)) + internalError("arnoldi delay calc failed.\n"); + d[0] = dsave; + + for (h=0;h32) + return false; + + for (i=0;i=1;h--) { + iter = 0; + while (abs(e[h])>1e-18) { // 1e-6ps + m=0; + if (m != h) { + if (iter++ == 20) + return false; + g = (d[h-1]-d[h])/(2.0*e[h]); + r = sqrt(1.0+g*g); // watch overflow + g = d[m]-d[h]+e[h]/(g + (g<0?-r:r)); + s = c = 1.0; + p = 0.0; + for (i=m+1;i<=h;i++) { + f = s*e[i]; + b = c*e[i]; + e[i-1] = r = sqrt(f*f+g*g); // watch + if (r == 0.0) { + d[i-1] -= p; + e[m] = 0.0; + break; + } + s = f/r; + c = g/r; + g = d[i-1]-p; + r = (d[i]-g)*s+2.0*c*b; + d[i-1] = g + (p=s*r); + g = c*r-b; + for (k=0;k p) { k=j; p=d[k]; } + if (k != i) { + d[k] = d[i]; + d[i] = p; + for (j=0;j= 0.0) + || (abs(2.0*f) > abs(dxold*df))) { + dxold = dx; + dx = 0.5*(xh-xl); + 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); + } + flast = f; + rts = xl+dx; + if (xl == rts) { + return rts; + } + } else { + dxold = dx; + dx = f/df; + flast = 0.0; + temp = rts; + rts -= dx; + if (temp == rts) { + return rts; + } + } + if (abs(dx) < xacc) { + return rts; + } + get_dv(rts,s,order,p,rr,&f,&df); f -= val; + if (f<0.0) + xl = rts; + else + xh = rts; + } + if (abs(f)<1e-6) // 1uV + return rts; + return 0.5*(xl+xh); +} + +void +ArnoldiDelayCalc::pr_solve1(double s, + 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) + order--; + if (rr[0]<0.5) { + for (h=1;h0.3 && rr[h]>rr[0]) { h0 = h; break; } + } + double p0 = p[h0]; + double ps,vs,ta,va; + vs = 0.0; + for (h=0;h1.0 && p[order-1]>500.0 && va>v1-0.002)) + debugPrint0(debug_, "arnoldi", 1, "err, pr_solve1, vav1) { + tmin = ta; vmin = va; + ta *= 2.0; + pr_get_v(ta,s,order,p,rr,&va); + } + if (va>v1) + debugPrint0(debug_, "arnoldi", 1, "err, pr_solve1, va>v1\n"); + tmax = ta; vmax = va; + } + } else { + // s is irrelevant + ta = s; va = vs; + while (va >= v1) { + tmin = ta; + vmin = va; + ta += 1.0/p0; + pr_get_v(ta,s,order,p,rr,&va); + } + tmax = ta; vmax = va; + } + *t1 = solve_t_bracketed(s,order,p,rr,v1,tmin,tmax,vmin,vmax); +} + +void +ArnoldiDelayCalc::pr_solve3(double s, + 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) + 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; + double ps,vs,ta,va; + vs = 0.0; + for (h=0;hvhi) { + tmin2 = tmin5 = ta; + vmin2 = vmin5 = va; + tmin8 = ta; vmin8 = va; + if (va vhi) { + tmin2 = tmin5; + vmin2 = vmin5; + tmax2 = tmax5; + vmax2 = tmax5; + } else { + tmax2 = tmin5; + vmax2 = vmin5; + ta = vlo*s; + pr_get_v(ta,s,order,p,rr,&va); + tmin2 = ta; vmin2 = va; + } + } + } else if (vsvlo) { + tmin8 = ta; vmin8 = va; + ta += 1.0/p0; + pr_get_v(ta,s,order,p,rr,&va); + } + tmax8 = ta; vmax8 = va; + ta = vmid*s; + pr_get_v(ta,s,order,p,rr,&va); + tmin5 = ta; vmin5 = va; + if (va>vhi) { + tmin2 = ta; vmin2 = va; + } else { + tmax2 = ta; vmax2 = va; + ta = vlo*s; + pr_get_v(ta,s,order,p,rr,&va); + tmin2 = ta; vmin2 = va; + } + } else if (vsvmid) { + tmin5 = tmin8 = ta; vmin5 = tmin8 = va; + ta += 0.7/p0; + pr_get_v(ta,s,order,p,rr,&va); + } + tmax5 = ta; vmax5 = va; + if (va < vlo) { + tmax8 = ta; vmax8 = va; + } else { + tmin8 = ta; vmin8 = va; + ta += 1.0/p0; + pr_get_v(ta,s,order,p,rr,&va); + while (va>vlo) { + tmin8 = ta; vmin8 = va; + ta += 1.0/p0; + pr_get_v(ta,s,order,p,rr,&va); + } + tmax8 = ta; vmax8 = va; + } + } else { + // s is irrelevant + ta = s; va = vs; + tmin2 = tmin5 = tmin8 = ta; + vmin2 = vmin5 = vmin8 = va; + while (va > vhi) { + tmin2 = tmin5 = tmin8 = ta; + vmin2 = vmin5 = vmin8 = va; + ta += 1.0/p0; + pr_get_v(ta,s,order,p,rr,&va); + } + tmax2 = ta; vmax2 = va; + if (va < vmid) { + tmax5 = ta; vmax5 = va; + } else 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) { + tmin8 = ta; + vmin8 = va; + ta += 1.0/p0; + pr_get_v(ta,s,order,p,rr,&va); + } + tmax8 = ta; vmax8 = va; + } + + *thi = solve_t_bracketed(s,order,p,rr,vhi,tmin2,tmax2,vmin2,vmax2); + *tmid= solve_t_bracketed(s,order,p,rr,vmid,tmin5,tmax5,vmin5,vmax5); + *tlo= solve_t_bracketed(s,order,p,rr,vlo,tmin8,tmax8,vmin8,vmax8); +} + +static double +calc_integ(double p,double s,double t) +{ + // integral of f(t)-vin(t) + double ps = p*s; + double pt = p*t; + double y,ept,eps; + if (t<=s) { + ept = (pt>40.0)?0.0:exp(-pt); + y = ept-1.0+pt; + } else { + pt = pt-ps; + ept = (pt>40.0)?0.0:exp(-pt); + eps = (ps>40.0)?0.0:exp(-ps); + y = ps - (1.0-eps)*ept; + } + y /= ps*p; + return y; +} + + +double +ArnoldiDelayCalc::pr_ceff(double s, + double rdrive, + int order, + double *p, + double *rr, + double ceff_time) +{ + double integi = 0.0; + double ceff, v0; + int j; + for (j=0;j1e-8) + debugPrint2(debug, "arnoldi", 1, "y f %g %g\n",y,f); + return x; +} + +double +ArnoldiDelayCalc::ra_solve_for_t(double p, + double s, + double v) +{ + double t; + double ps = p*s; + if (ps>30.0) { + t = (1.0+ps*(1.0-v)) / p; + return t; + } + double eps = exp(ps); + if ((1-ps*v)*eps >= 1.0) { + t = log((eps-1.0)/(ps*v)) / p; + } else { + t = ra_hinv((1-v)*ps, debug_)/p; + } + return t; +} + +void +ArnoldiDelayCalc::ra_solve_for_pt(double ps, + double v, + double *pt, + double *d) +{ + if (ps>30.0) { + *pt = 1.0+ps*(1.0-v); + *d = 1.0-v; + return; + } + double eps = exp(ps); + if ((1-ps*v)*eps >= 1.0) { + *pt = log((eps-1.0)/(ps*v)); + *d = eps/(eps-1.0) - 1.0/ps; + } else { + *pt = ra_hinv((1-v)*ps, debug_); + *d = (1.0-v)/(*pt - (1-v)*ps); + } +} + +void +ArnoldiDelayCalc::ra_calc_c(double vlo, + 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_); + double b = log(1.0/vlo); + double c_s1 = b + ra_hinv((1.0-vlo)/vlo - b, debug_); + double a1 = (exp(c_s1)-1.0)/c_s1; + double den = log(a1/vlo) - ra_hinv((1.0-vhi)*c_s1, debug_); + *c_x1 = (vhi-vlo)/den; + *c_y1 = c_s1*(*c_x1); +} + +//////////////////////////////////////////////////////////////// + +// +// ceff.cpp +// + +void +ArnoldiDelayCalc::ra_solve_for_s(delay_work *D, + double p, + double tlohi, + double &s) +{ + delay_c *c = D->c; + double vhi = c->vhi; + double vlo = c->vlo; + // s is 0-100 + // solve f(x,y)=0 with f = x*(ptlo(y/x)-pthi(y/x))-(vhi-vlo) + // (x=0,y=1) + // (x=x1,y=y1) c->x1,y1 + // (x=x2,y=y2) x2=(vhi-vlo)/log(vhi/vlo) y2=(c->smin)*x2 + double x1 = c->x1; + double y1 = c->y1; + double x2 = (vhi-vlo)/c->vlg; + double y2 = (c->smin)*x2; + double ptlo,dlo; + double pthi,dhi; + double f,df,x,y; + x = c->vlg/(p*tlohi); + + if (x <= x1) { + y = y1 - 0.5*(x-x1); + if (y>1.0) y=1.0; + } else { + y = y1 - (x-x1)*(0.5 + 8*(x-x1)); + if (y.5e-12) // .5ps + debugPrint3(debug_, "arnoldi", 1, + "ra_solve_for_s p %g tlohi %s err %s\n", + p, + units_->timeUnit()->asString(tlohi), + units_->timeUnit()->asString(f)); +} + +///////////////////////////////////////////////////////////////////// +// method 0: +// r = a match to slew to (ctot, limited by cmin,cmax) +// if r>rdelay, lower r +// Now at any ceff (limited) +// If slew(r,0,ceff) is too big +// s = s_start(r,ceff), not smaller than Smin +// accept the pessimistic output slew +// Else +// solve for s + +// 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) +{ + // find the maximum r that allows a solution for s of + // (s,r,ctot)-> output_slew + // If this maximum is greater than rdelay, use rdelay. + delay_c *c = D->c; + double slew_derate = c->slew_derate; + double c_log = c->vlg; + float c1; + double tlohi,r; + c1 = ctot; + ArcDelay d1; + Slew s1; + tab->table->gateDelay(tab->cell, tab->pvt, tab->in_slew, + c1, tab->relcap, pocv_enabled_, + d1, s1); + tlohi = slew_derate*delayAsFloat(s1); + r = tlohi/(c_log*c1); + if (rdelay>0.0 && r > rdelay) + r = rdelay; + // else printf("from rdelay %g to r %g\n",rdelay,r); + return r; +} + +double +ArnoldiDelayCalc::ra_get_s(delay_work *D, + 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->cell, tab->pvt, tab->in_slew, + c, tab->relcap, pocv_enabled_, 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) { + // printf("hit smin\n"); + s = smin; + } else { + s = smin+0.3*tlohi; + ra_solve_for_s(D,1.0/(r*c),tlohi,s); + } + return s; +} + +///////////////////////////////////////////////////////////////////// +// method 1: +// determine the drive resistance from change in delay versus ctot +// find the maximum r that allows a solution for s of +// (s,r,ctot)-> output_slew +// If this maximum is greater than rdelay, use rdelay. +// calculate s,r,mod -> t50_srmod, +// then t50_srmod+t50_sy-t50_sr + +double +ArnoldiDelayCalc::ra_rdelay_1(timing_table *tab, + 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->cell, tab->pvt, tab->in_slew, + c1, tab->relcap, pocv_enabled_, d1, s1); + tab->table->gateDelay(tab->cell, tab->pvt, tab->in_slew, + c2, tab->relcap, pocv_enabled_, d2, s2); + double dt50 = delayAsFloat(d1)-delayAsFloat(d2); + if (dt50 <= 0.0) + return 0.0; + double rdelay = dt50/(c1-c2); + return rdelay; +} + +void +ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D, + timing_table *tab, + arnoldi1 *mod, + double *delays, + double *slews) +{ + delay_c *con = D->c; + double slew_derate = con->slew_derate; + double vhi = con->vhi; + double vlo = con->vlo; + double ctot = mod->ctot; + double ceff,tlohi,t50_sy,r,s,t50_sr,rdelay; + ArcDelay df; + Slew sf; + + debugPrint1(debug_, "arnoldi", 1, "\nctot=%s\n", + 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 + rdelay = 1e+3; // 1kohm + + bool bad = (r0.0 + && s<100e-9)) // 100ns + s = 0.5e-9; // .5ns + + if (debug_->check("arnoldi", 1)) { + double p = 1.0/(r*ctot); + double thix,tlox; + if (bad) printf("bad\n"); + debugPrint2(debug_, "arnoldi", 1, "at r=%s s=%s\n", + 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->cell, tab->pvt,tab->in_slew, + ctot, tab->relcap, pocv_enabled_, df, sf); + debugPrint3(debug_, "arnoldi", 1, + "table slew (in_slew %s ctot %s) = %s\n", + units_->timeUnit()->asString(tab->in_slew), + units_->capacitanceUnit()->asString(ctot), + delayAsString(sf, this)); + tlohi = slew_derate*delayAsFloat(sf); + debugPrint2(debug_, "arnoldi", 1, "tlohi %s %s\n", + units_->timeUnit()->asString(tlohi), + units_->timeUnit()->asString(tlox-thix)); + } + ceff = ctot; + tab->table->gateDelay(tab->cell, tab->pvt, tab->in_slew, + ceff, tab->relcap, 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 + + mod->calculate_poles_res(D,r); + double *p = D->poles; + double *rr = delay_work_get_residues(D,0); + double thi,tlo,t50_srmod; + pr_solve1(s,mod->order,p,rr,0.5,&t50_srmod); + + int ceff_it,j; + double ceff_time=0.0; + + if (!bad) { + for (ceff_it=0;ceff_it<3;ceff_it++) { + + // calculate ceff + ceff_time = s; + ceff = pr_ceff(s,r,mod->order,p,rr,ceff_time); + + if((ceff-1e-20) < 0.0) { // 1e-8pf + debugPrint0(debug_, "arnoldi", 1, + "Invalid effective capacitance, using total capacitance\n"); + ceff = ctot; + } + + // new mvs at ceff + s = ra_get_s(D,tab,r,ceff); + debugPrint1(debug_, "arnoldi", 1, "new mvs s = %s\n", + units_->timeUnit()->asString(s)); + } + } + debugPrint4(debug_, "arnoldi", 1, "r %s s %s ceff_time %s ceff %s\n", + units_->resistanceUnit()->asString(r), + units_->timeUnit()->asString(s), + units_->timeUnit()->asString(ceff_time), + units_->capacitanceUnit()->asString(ceff)); + + tab->table->gateDelay(tab->cell, tab->pvt, tab->in_slew, ceff, + tab->relcap, pocv_enabled_, df, sf); + t50_sy = delayAsFloat(df); + t50_sr = ra_solve_for_t(1.0/(r*ceff),s,0.5); + for (j=0;jn;j++) { + rr = delay_work_get_residues(D,j); + pr_solve3(s,mod->order,p,rr,vhi,&thi,0.5,&t50_srmod,vlo,&tlo); + delays[j] = t50_srmod + t50_sy - t50_sr; + slews[j] = (tlo-thi)/slew_derate; + } + debugPrint3(debug_, "arnoldi", 1, "slews[0] %s thi %s tlo %s\n", + units_->timeUnit()->asString(slews[0]), + units_->timeUnit()->asString(tlo), + units_->timeUnit()->asString(thi)); +} + +} // namespace diff --git a/dcalc/ArnoldiDelayCalc.hh b/dcalc/ArnoldiDelayCalc.hh new file mode 100644 index 0000000..25e3d2d --- /dev/null +++ b/dcalc/ArnoldiDelayCalc.hh @@ -0,0 +1,26 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef ARNOLDIDELAYCALC_H +#define ARNOLDIDELAYCALC_H + +namespace sta { + +ArcDelayCalc * +makeArnoldiDelayCalc(StaState *sta); + +} // namespace +#endif diff --git a/dcalc/ArnoldiReduce.cc b/dcalc/ArnoldiReduce.cc new file mode 100644 index 0000000..d018afc --- /dev/null +++ b/dcalc/ArnoldiReduce.cc @@ -0,0 +1,646 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +// (c) 2018 Nefelus, Inc. +// +// Author: W. Scott + +#include +#include +#include +#include +#include "Machine.hh" +#include "Debug.hh" +#include "Units.hh" +#include "MinMax.hh" +#include "Sdc.hh" +#include "Network.hh" +#include "ArnoldiReduce.hh" +#include "Arnoldi.hh" +#include "ConcreteParasiticsPvt.hh" + +namespace sta { + +rcmodel::rcmodel() : + pinV(nullptr) +{ +} + +rcmodel::~rcmodel() +{ + free(pinV); +} + +float +rcmodel::capacitance() const +{ + return ctot; +} + +struct ts_point +{ + ParasiticNode *node_; + int eN; + bool is_term; + int tindex; // index into termV of corresponding term + ts_edge **eV; + bool visited; + ts_edge *in_edge; + int ts; + double c; + double r; +}; + +struct ts_edge +{ + ConcreteParasiticResistor *resistor_; + ts_point *from; + ts_point *to; +}; + +//////////////////////////////////////////////////////////////// + + +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) +{ + 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; + tr_ = tr; + op_cond_ = op_cond; + corner_ = corner; + cnst_min_max_ = cnst_min_max; + ap_ = ap; + loadWork(); + return makeRcmodelDrv(); +} + +void +ArnoldiReduce::loadWork() +{ + pt_map_.clear(); + + int resistor_count = 0; + ConcreteParasiticDeviceSet devices; + parasitic_network_->devices(&devices); + ConcreteParasiticDeviceSet::Iterator device_iter(devices); + while (device_iter.hasNext()) { + ParasiticDevice *device = device_iter.next(); + if (parasitics_->isResistor(device)) + resistor_count++; + } + + termN = parasitic_network_->pinNodes()->size(); + int subnode_count = parasitic_network_->subNodes()->size(); + ts_pointN = subnode_count + 1 + termN; + ts_edgeN = resistor_count; + allocPoints(); + allocTerms(termN); + ts_point *p0 = ts_pointV; + pterm0 = p0 + subnode_count + 1; + ts_point *pend = p0 + ts_pointN; + ts_point *p; + ts_edge *e0 = ts_edgeV; + ts_edge *eend = e0 + ts_edgeN; + + ts_edge *e; + int tindex; + for (p = p0; p!=pend; p++) { + p->node_ = nullptr; + p->eN = 0; + p->is_term = false; + } + pend = pterm0; + e = e0; + int index = 0; + ConcreteParasiticSubNodeMap::Iterator + sub_node_iter(parasitic_network_->subNodes()); + while (sub_node_iter.hasNext()) { + ConcreteParasiticSubNode *node = sub_node_iter.next(); + pt_map_[node] = index; + p = p0 + index; + p->node_ = node; + p->eN = 0; + p->is_term = false; + index++; + } + + ConcreteParasiticPinNodeMap::Iterator + pin_node_iter(parasitic_network_->pinNodes()); + while (pin_node_iter.hasNext()) { + ConcreteParasiticPinNode *node = pin_node_iter.next(); + p = pend++; + pt_map_[node] = p - p0; + p->node_ = node; + p->eN = 0; + p->is_term = true; + tindex = p - pterm0; + p->tindex = tindex; + const Pin *pin = parasitics_->connectionPin(node); + pinV[tindex] = pin; + } + + ts_edge **eV = ts_eV; + ConcreteParasiticDeviceSet::Iterator device_iter2(devices); + while (device_iter2.hasNext()) { + ParasiticDevice *device = device_iter2.next(); + if (parasitics_->isResistor(device)) { + ConcreteParasiticResistor *resistor = + reinterpret_cast(device); + ts_point *pt1 = findPt(resistor->node1()); + ts_point *pt2 = findPt(resistor->node2()); + e->from = pt1; + e->to = pt2; + e->resistor_ = resistor; + pt1->eN++; + if (e->from != e->to) + pt2->eN++; + e++; + } + } + + for (p=p0;p!=pend;p++) { + if (p->node_) { + p->eV = eV; + eV += p->eN; + p->eN = 0; + } + } + 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; + } +} + +void +ArnoldiReduce::allocPoints() +{ + if (ts_pointN > ts_pointNmax) { + free(par); + free(c); + free(r); + 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)); + } + 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*)); + } +} + +void +ArnoldiReduce::allocTerms(int nterms) +{ + if (nterms > termNmax) { + free(U0); + 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)); + + U0 = (double*)malloc(dNmax*termNmax*sizeof(double)); + int h; + for (h=0;h(node)]]; +} + +rcmodel * +ArnoldiReduce::makeRcmodelDrv() +{ + ParasiticNode *drv_node = parasitics_->findNode(parasitic_network_, + drvr_pin_); + ts_point *pdrv = findPt(drv_node); + makeRcmodelDfs(pdrv); + getRC(); + if (ctot_ < 1e-22) // 1e-10ps + return nullptr; + setTerms(pdrv); + makeRcmodelFromTs(); + rcmodel *mod = makeRcmodelFromW(); + return mod; +} + +#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 *p0 = ts_pointV; + ts_point *pend = p0 + ts_pointN; + for (p=p0;p!=pend;p++) + p->visited = 0; + ts_edge *e; + + ts_edge **stackV = ts_stackV; + int stackN = 1; + stackV[0] = e = pdrv->eV[0]; + ts_orient(pdrv,e); + pdrv->visited = 1; + pdrv->in_edge = nullptr; + pdrv->ts = 0; + ts_ordV[0] = pdrv-p0; + ts_pordV[0] = pdrv; + ts_ordN = 1; + while (stackN>0) { + e = stackV[stackN-1]; + q = e->to; + + if (q->visited) { + // if it is a one-rseg self-loop, + // ignore, and do not even set *loop + if (e->to != e->from) + loop = true; + } else { + // try to descend + q->visited = 1; + q->ts = ts_ordN++; + ts_pordV[q->ts] = q; + 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; + e = q->eV[k]; + ts_orient(q,e); + stackV[stackN++] = e; + continue; // descent + } + } + // try to ascend + 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; + // if (k==p->eN) notice(0,"ERROR, e not found!\n"); + ++k; + 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]; + } + ts_orient(p,e); + stackV[stackN++] = e; + break; + } + + } // while (stackN) + + if (loop) + debugPrint1(debug_, "arnoldi", 1, + "net %s loop\n", + network_->pathName(drvr_pin_)); +} + +// makeRcmodelGetRC +void +ArnoldiReduce::getRC() +{ + ts_point *p, *p0 = ts_pointV; + ts_point *pend = p0 + ts_pointN; + ctot_ = 0.0; + 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, ap_) + + pinCapacitance(node); + if (cap > 0.0) { + p->c = cap; + ctot_ += cap; + } + else + p->c = 0.0; + if (p->in_edge && p->in_edge->resistor_) + p->r = parasitics_->value(p->in_edge->resistor_, ap_); + if (!(p->r>=0.0 && p->r<100e+3)) { // 0 < r < 100kohm + debugPrint2(debug_, "arnoldi", 1, + "R value %g out of range, drvr pin %s\n", + p->r, + network_->pathName(drvr_pin_)); + } + } + } +} + +float +ArnoldiReduce::pinCapacitance(ParasiticNode *node) +{ + const Pin *pin = parasitics_->connectionPin(node); + float pin_cap = 0.0; + if (pin) { + Port *port = network_->port(pin); + LibertyPort *lib_port = network_->libertyPort(port); + if (lib_port) + pin_cap = sdc_->pinCapacitance(pin,tr_, op_cond_, corner_, cnst_min_max_); + else if (network_->isTopLevelPort(pin)) + pin_cap = sdc_->portExtCap(port, tr_, cnst_min_max_); + } + return pin_cap; +} + +void +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; + termV[0] = k0 = pdrv->tindex; + for (k=1;kts; + } +} + +// The guts of the arnoldi reducer. +void +ArnoldiReduce::makeRcmodelFromTs() +{ + ts_point *p, *p0 = ts_pointV; + int n = ts_ordN; + int nterms = termN; + int i,j,k,h; + if (debug_->check("arnoldi", 1)) { + for (k=0;kts,p-p0, + units_->capacitanceUnit()->asString(p->c)); + if (p->is_term) + debug_->print(" term%d",p->tindex); + if (p->in_edge) + debug_->print(" from T%d,P%ld r=%s", + p->in_edge->from->ts, + p->in_edge->from-p0, + units_->resistanceUnit()->asString(p->r)); + debug_->print("\n"); + } + for (i=0;ic; + for (j=1;jc; + r[j] = p->r; + par[j] = p->in_edge->from->ts; + } + + 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]; + iv[par[j]] += iv[j]; + } + iv[0] += c[0]*u0[0]; + y[0] = 0.0; + for (j=1;jcheck("arnoldi", 1)) { + debugPrint1(debug_, "arnoldi", 1, + "tridiagonal reduced matrix, drvr pin %s\n", + network_->pathName(drvr_pin_)); + debugPrint2(debug_, "arnoldi", 1, "order %d n %d\n",order,n); + for (h=0;hprint("d[%d] %s", + h, + units_->timeUnit()->asString(d[h])); + if (hprint(" e[%d] %s", + h, + units_->timeUnit()->asString(e[h])); + debug_->print("\n"); + debug_->print("U[%d]",h); + for (i=0;iprint(" %6.2e",U[h][i]); + debug_->print("\n"); + } + } +} + +rcmodel * +ArnoldiReduce::makeRcmodelFromW() +{ + 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*)); + mod->U[0] = mod->d + order + order - 1; + for (h=1;hU[h]=mod->U[0] + h*n; + for (h=0;hd[h] = d[h]; + if (he[h] = e[h]; + for (j=0;jU[h][j] = U[h][j]; + } + } + + mod->pinV = (const Pin **)malloc(n*sizeof(const Pin*)); + for (j=0;jpinV[j] = pinV[k]; + } + + mod->ctot = ctot_; + mod->sqc = sqc_; + return mod; +} + +} // namespace diff --git a/dcalc/ArnoldiReduce.hh b/dcalc/ArnoldiReduce.hh new file mode 100644 index 0000000..8b28671 --- /dev/null +++ b/dcalc/ArnoldiReduce.hh @@ -0,0 +1,116 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +// (c) 2018 Nefelus, Inc. +// +// Author: W. Scott + +#ifndef STA_ARNOLDI_REDUCE_H +#define STA_ARNOLDI_REDUCE_H + +#include "Map.hh" +#include "NetworkClass.hh" +#include "ParasiticsClass.hh" + +namespace sta { + +class ConcreteParasiticNetwork; +class ConcreteParasiticNode; + +class rcmodel; +struct ts_edge; +struct ts_point; + +typedef Map ArnolidPtMap; + +class ArnoldiReduce : public StaState +{ +public: + ArnoldiReduce(StaState *sta); + ~ArnoldiReduce(); + Parasitic *reduceToArnoldi(Parasitic *parasitic, + const Pin *drvr_pin, + float coupling_cap_factor, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap); + +protected: + void loadWork(); + rcmodel *makeRcmodelDrv(); + void allocPoints(); + void allocTerms(int nterms); + ts_point *findPt(ParasiticNode *node); + void makeRcmodelDfs(ts_point *pdrv); + void getRC(); + float pinCapacitance(ParasiticNode *node); + void setTerms(ts_point *pdrv); + void makeRcmodelFromTs(); + rcmodel *makeRcmodelFromW(); + + ConcreteParasiticNetwork *parasitic_network_; + const Pin *drvr_pin_; + float coupling_cap_factor_; + const TransRiseFall *tr_; + const OperatingConditions *op_cond_; + const Corner *corner_; + const MinMax *cnst_min_max_; + const ParasiticAnalysisPt *ap_; + // ParasiticNode -> ts_point index. + ArnolidPtMap pt_map_; + + // rcWork + ts_point *ts_pointV; + int ts_pointN; + int ts_pointNmax; + static const int ts_point_count_incr_; + ts_edge *ts_edgeV; + int ts_edgeN; + int ts_edgeNmax; + static const int ts_edge_count_incr_; + ts_edge **ts_eV; + ts_edge **ts_stackV; + int *ts_ordV; + ts_point **ts_pordV; + int ts_ordN; + + int termNmax; + 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; + double *d; + double *e; + double *U0; + double **U; + + double ctot_; + double sqc_; + double *_u0, *_u1; + double *y, *iv; + double *c, *r; + int *par; + int order; +}; + +} // namespace +#endif + diff --git a/dcalc/DcalcAnalysisPt.cc b/dcalc/DcalcAnalysisPt.cc new file mode 100644 index 0000000..ab6e7f4 --- /dev/null +++ b/dcalc/DcalcAnalysisPt.cc @@ -0,0 +1,61 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "StringUtil.hh" +#include "Corner.hh" +#include "DcalcAnalysisPt.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 index_; // same for now +} + +} // namespace diff --git a/dcalc/DcalcAnalysisPt.hh b/dcalc/DcalcAnalysisPt.hh new file mode 100644 index 0000000..90046dd --- /dev/null +++ b/dcalc/DcalcAnalysisPt.hh @@ -0,0 +1,78 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_DCALC_ANALYSIS_PT_H +#define STA_DCALC_ANALYSIS_PT_H + +#include "DisallowCopyAssign.hh" +#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: + DISALLOW_COPY_AND_ASSIGN(DcalcAnalysisPt); + + 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 +#endif diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc new file mode 100644 index 0000000..bd38961 --- /dev/null +++ b/dcalc/DelayCalc.cc @@ -0,0 +1,91 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Map.hh" +#include "StringUtil.hh" +#include "UnitDelayCalc.hh" +#include "LumpedCapDelayCalc.hh" +#include "SimpleRCDelayCalc.hh" +#include "DmpDelayCalc.hh" +#include "ArnoldiDelayCalc.hh" +#include "DelayCalc.hh" + +namespace sta { + +typedef Map DelayCalcMap; + +static DelayCalcMap *delay_calcs = nullptr; + +void +registerDelayCalcs() +{ + registerDelayCalc("unit", makeUnitDelayCalc); + registerDelayCalc("lumped_cap", makeLumpedCapDelayCalc); + registerDelayCalc("simple_rc", makeSimpleRCDelayCalc); + registerDelayCalc("dmp_ceff_elmore", makeDmpCeffElmoreDelayCalc); + registerDelayCalc("dmp_ceff_two_pole", makeDmpCeffTwoPoleDelayCalc); + registerDelayCalc("arnoldi", makeArnoldiDelayCalc); +} + +void +registerDelayCalc(const char *name, + MakeArcDelayCalc maker) +{ + if (delay_calcs == nullptr) + delay_calcs = new DelayCalcMap; + (*delay_calcs)[name] = maker; +} + +void +deleteDelayCalcs() +{ + delete delay_calcs; + delay_calcs = nullptr; +} + +ArcDelayCalc * +makeDelayCalc(const char *name, + StaState *sta) +{ + MakeArcDelayCalc maker = delay_calcs->findKey(name); + if (maker) + return maker(sta); + else + return nullptr; +} + +bool +isDelayCalcName(const char *name) +{ + return delay_calcs->hasKey(name); +} + +StringSeq * +delayCalcNames() +{ + StringSeq *names = new StringSeq; + DelayCalcMap::Iterator dcalc_iter(delay_calcs); + while (dcalc_iter.hasNext()) { + MakeArcDelayCalc maker; + const char *name; + dcalc_iter.next(name, maker); + names->push_back(name); + } + return names; +} + +} // namespace diff --git a/dcalc/DelayCalc.hh b/dcalc/DelayCalc.hh new file mode 100644 index 0000000..160f079 --- /dev/null +++ b/dcalc/DelayCalc.hh @@ -0,0 +1,50 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_DELAY_CALC_H +#define STA_DELAY_CALC_H + +#include "StringSeq.hh" + +namespace sta { + +class ArcDelayCalc; +class StaState; + +typedef ArcDelayCalc *(*MakeArcDelayCalc)(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); +bool +isDelayCalcName(const char *name); +// Caller owns return value. +StringSeq * +delayCalcNames(); +void +deleteDelayCalcs(); + +// Make a registered delay calculator by name. +ArcDelayCalc * +makeDelayCalc(const char *name, + StaState *sta); + +} // namespace +#endif diff --git a/dcalc/DelayCalc.i b/dcalc/DelayCalc.i new file mode 100644 index 0000000..61a8336 --- /dev/null +++ b/dcalc/DelayCalc.i @@ -0,0 +1,51 @@ +%module dcalc + +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Sta.hh" + +%} + +%inline %{ + +TmpStringSeq * +delay_calc_names() +{ + return sta::delayCalcNames(); +} + +bool +is_delay_calc_name(const char *alg) +{ + return sta::isDelayCalcName(alg); +} + +void +set_delay_calculator_cmd(const char *alg) +{ + sta::Sta::sta()->setArcDelayCalc(alg); +} + +void +set_delay_calc_incremental_tolerance(float tol) +{ + sta::Sta::sta()->setIncrementalDelayTolerance(tol); +} + +%} // inline diff --git a/dcalc/DelayCalc.tcl b/dcalc/DelayCalc.tcl new file mode 100644 index 0000000..beaca5b --- /dev/null +++ b/dcalc/DelayCalc.tcl @@ -0,0 +1,125 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +namespace eval sta { + +# Allow any combination of -from/-to pins. +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" \ + flags {-min -max} + set corner [parse_corner keys] + set min_max [parse_min_max_flags flags] + check_argc_eq0 $cmd $cmd_args + + set digits $sta_report_default_digits + if [info exists keys($digits_key)] { + set digits $keys($digits_key) + check_positive_integer $digits_key $digits + } + 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] { + 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 + } + } + } elseif [info exists keys(-from)] { + set from_pin [get_port_pin_error "from_pin" $keys(-from)] + 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 + } + $iter finish + } + } elseif [info exists keys(-to)] { + set to_pin [get_port_pin_error "to_pin" $keys(-to)] + 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 + } + $iter finish + } + } +} + +proc report_edge_dcalc { edge corner min_max digits } { + set role [$edge role] + if { $role != "wire" } { + set from_vertex [$edge from] + set from_pin [$from_vertex pin] + set to_vertex [$edge to] + set to_pin [$to_vertex pin] + set cell [[$to_pin instance] cell] + set library [$cell library] + # Filter timing checks based on min_max. + if {!(($min_max == "max" && $role == "hold") \ + || ($min_max=="min" && $role=="setup"))} { + puts "Library: [get_name $library]" + puts "Cell: [get_name $cell]" + puts "Arc sense: [$edge sense]" + puts "Arc type: $role" + + set arc_iter [$edge timing_arc_iterator] + while {[$arc_iter has_next]} { + set arc [$arc_iter next] + set from [get_name [$from_pin port]] + set from_tr [$arc from_trans] + set to [get_name [$to_pin port]] + set to_tr [$arc to_trans] + puts "$from $from_tr -> $to $to_tr" + puts -nonewline [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] + puts "Annotated value = [format_time $delay $digits]" + } + puts "" + puts "............................................." + puts "" + } + $arc_iter finish + } + } +} + +################################################################ + +define_hidden_cmd_args "set_delay_calculator" [delay_calc_names] + +proc set_delay_calculator { alg } { + if { [is_delay_calc_name $alg] } { + set_delay_calculator_cmd $alg + } else { + sta_error "delay calculator $alg not found." + } +} + +# sta namespace end +} diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc new file mode 100644 index 0000000..7ca8acf --- /dev/null +++ b/dcalc/DmpCeff.cc @@ -0,0 +1,1780 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +// "Performance Computation for Precharacterized CMOS Gates with RC Loads", +// Florentin Dartu, Noel Menezes and Lawrence Pileggi, IEEE Transactions +// on Computer-Aided Design of Integrated Circuits and Systems, Vol 15, No 5, +// May 1996, pg 544-553. +// +// The only real change from the paper is that Vl, the measured low +// slew voltage is matched instead of y20 in eqn 12. + +#include // abs, min +#include // sqrt +#include "Machine.hh" +#include "Report.hh" +#include "Debug.hh" +#include "Units.hh" +#include "TimingArc.hh" +#include "TableModel.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "Parasitics.hh" +#include "DcalcAnalysisPt.hh" +#include "ArcDelayCalc.hh" +#include "DmpCeff.hh" + +namespace sta { + +using std::abs; +using std::min; + +// 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 }; + +static const char *dmp_param_index_strings[] = {"t0", "dt", "Ceff"}; + +// Indices of Newton-Raphson function value vector. +enum DmpFunc { y20, y50, ipi }; + +static const char *dmp_func_index_strings[] = {"y20", "y50", "Ipi"}; + +static double +gateModelRd(const LibertyCell *cell, + GateTableModel *gate_model, + double in_slew, + double c2, + double c1, + float related_out_cap, + const Pvt *pvt, + bool pocv_enabled); +static bool +evalDmpEqnsState(void *state); +static void +evalVoEqns(void *state, + double x, + double &y, + double &dy); +static void +evalVlEqns(void *state, + double x, + double &y, + double &dy); + +static double +findRoot(void (*func)(void *state, double x, double &y, double &dy), + void *state, + double x1, + double x2, + double x_tol, + int max_iter, + const char *&error); +static bool +newtonRaphson(const int max_iter, + double x[], + const int n, + const double x_tol, + // eval(state) is called to fill fvec and fjac. + // Returns false if fails. + bool (*eval)(void *state), + void *state, + // Temporaries supplied by caller. + double *fvec, + double **fjac, + int *index, + double *p, + double *scale, + const char *&error); +static void +luSolve(double **a, + const int size, + const int *index, + double b[]); +static bool +luDecomp(double **a, + const int size, + int *index, + double *scale, + const char *&error); + +//////////////////////////////////////////////////////////////// + +// Base class for Dartu/Menezes/Pileggi algorithm. +// Derived classes handle different cases of zero values in the Pi model. +class DmpAlg : public StaState +{ +public: + DmpAlg(int nr_order, StaState *sta); + virtual ~DmpAlg(); + virtual const char *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 TransRiseFall *tr, + double rd, + double in_slew, + float related_out_cap, + double c2, + double rpi, + double c1) = 0; + virtual void gateDelaySlew(double &delay, + double &slew) = 0; + virtual void loadDelaySlew(const Pin *load_pin, + double elmore, + ArcDelay &delay, + Slew &slew); + 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 bool evalDmpEqns() = 0; + // Output response to vs(t) ramp driving pi model load. + double vo(double t); + double dVoDt(double t); + // Load responce to driver waveform. + double vl(double t); + double dVlDt(double t); + double vCross() { return v_cross_; } + +protected: + void init(const LibertyLibrary *library, + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const TransRiseFall *tr, + double rd, + double in_slew, + float related_out_cap); + // Find driver parameters t0, delta_t, Ceff. + bool findDriverParams(double &ceff); + void gateCapDelaySlew(double cl, + double &delay, + double &slew); + void gateDelays(double ceff, + double &t_vth, + double &t_vl, + double &slew); + virtual double dv0dt(double t) = 0; + // Partial derivatives of y(t) (jacobian). + void dy(double t, + double t0, + double dt, + double cl, + double &dydt0, + double &dyddt, + double &dydcl); + double y0dt(double t, + double cl); + double y0dcl(double t, + double cl); + void showX(); + void showFvec(); + void showJacobian(); + bool findDriverDelaySlew(double &delay, + double &slew); + bool findVoCrossing(double vth, + double &t); + void showVo(); + bool findVlCrossing(double vth, + double &t); + void showVl(); + void fail(const char *reason); + + // Output response to vs(t) ramp driving capacitive load. + double y(double t, + double t0, + double dt, + double cl); + // Output response to unit ramp driving capacitive load. + double y0(double t, + double cl); + // Output response to unit ramp driving pi model load. + virtual double v0(double t) = 0; + // Upper bound on time that vo crosses vh. + virtual double voCrossingUpperBound() = 0; + // Load responce to driver unit ramp. + virtual double vl0(double t) = 0; + // Upper bound on time that vl crosses vh. + double vlCrossingUpperBound(); + + // Inputs to the delay calculator. + const LibertyCell *drvr_cell_; + const LibertyLibrary *drvr_library_; + const Pvt *pvt_; + const GateTableModel *gate_model_; + double in_slew_; + float related_out_cap_; + double rd_; + // Logic threshold (percentage of supply voltage). + double vth_; + // Slew lower limit (percentage of supply voltage). + double vl_; + // Slew upper limit (percentage of supply voltage). + double vh_; + // Table slews are scaled by slew_derate to get + // measured slews from vl to vh. + double slew_derate_; + + // Driver parameters calculated by this algorithm. + double t0_; + double dt_; + double ceff_; + + // Driver parameter Newton-Raphson state. + int nr_order_; + double *x_; + double *fvec_; + double **fjac_; + double *scale_; + double *p_; + int *index_; + + // Gate slew used to check load delay. + double gate_slew_; + double vo_delay_; + // True if the driver parameters are valid for finding the load delays. + bool driver_valid_; + // Load rspf elmore delay. + double elmore_; + double p3_; + +private: + virtual double dvl0dt(double t) = 0; + + // Implicit argument passed to evalVoEqns, evalVlEqns. + double v_cross_; +}; + +DmpAlg::DmpAlg(int nr_order, + StaState *sta): + StaState(sta), + nr_order_(nr_order) +{ + x_ = new double[nr_order_]; + fvec_ = new double[nr_order_]; + scale_ = new double[nr_order_]; + p_ = new double[nr_order_]; + fjac_ = new double*[nr_order_]; + for (int i = 0; i < nr_order_; i++) + fjac_[i] = new double[nr_order_]; + index_ = new int[nr_order_]; +} + +DmpAlg::~DmpAlg() +{ + delete [] x_; + delete [] fvec_; + delete [] scale_; + delete [] p_; + for (int i = 0; i < nr_order_; i++) + delete [] fjac_[i]; + delete [] fjac_; + delete [] index_; +} + +void +DmpAlg::init(const LibertyLibrary *drvr_library, + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const TransRiseFall *tr, + double rd, + double in_slew, + float related_out_cap) +{ + drvr_library_ = drvr_library; + drvr_cell_ = drvr_cell; + pvt_ = pvt; + gate_model_ = gate_model; + rd_ = rd; + in_slew_ = in_slew; + related_out_cap_ = related_out_cap; + driver_valid_ = false; + vth_ = drvr_library->outputThreshold(tr); + vl_ = drvr_library->slewLowerThreshold(tr); + vh_ = drvr_library->slewUpperThreshold(tr); + slew_derate_ = drvr_library->slewDerateFromLibrary(); +} + +// Find Ceff, delta_t and t0 for the driver. +// Caller must initialize/retrieve x_[DmpParam::ceff] because +// order 2 eqns don't have a ceff eqn. +// Return true if successful. +bool +DmpAlg::findDriverParams(double &ceff) +{ + double t_vth, t_vl, slew; + gateDelays(ceff, t_vth, t_vl, slew); + double dt = slew / (vh_ - vl_); + double t0 = t_vth + log(1.0 - vth_) * rd_ * ceff - vth_ * dt; + x_[DmpParam::dt] = dt; + x_[DmpParam::t0] = t0; + const char *nr_error; + if (newtonRaphson(100, x_, nr_order_, driver_param_tol, evalDmpEqnsState, + this, fvec_, fjac_, index_, p_, scale_, nr_error)) { + t0_ = x_[DmpParam::t0]; + dt_ = x_[DmpParam::dt]; + debugPrint3(debug_, "delay_calc", 3, " t0 = %s dt = %s ceff = %s\n", + units_->timeUnit()->asString(t0_), + units_->timeUnit()->asString(dt_), + units_->capacitanceUnit()->asString(x_[DmpParam::ceff])); + if (debug_->check("delay_calc", 4)) + showVo(); + return true; + } + else { + fail(nr_error); + return false; + } +} + +static bool +evalDmpEqnsState(void *state) +{ + DmpAlg *alg = reinterpret_cast(state); + return alg->evalDmpEqns(); +} + +void +DmpAlg::gateCapDelaySlew(double ceff, + double &delay, + double &slew) +{ + ArcDelay model_delay; + Slew model_slew; + gate_model_->gateDelay(drvr_cell_, pvt_, + static_cast(in_slew_), + static_cast(ceff), + related_out_cap_, + pocv_enabled_, + model_delay, model_slew); + delay = delayAsFloat(model_delay); + slew = delayAsFloat(model_slew); +} + +void +DmpAlg::gateDelays(double ceff, + double &t_vth, + double &t_vl, + double &slew) +{ + gateCapDelaySlew(ceff, t_vth, slew); + // Gate table slew is reported. Convert to measured. + slew *= slew_derate_; + t_vl = t_vth - slew * (vth_ - vl_) / (vh_ - vl_); +} + +double +DmpAlg::y(double t, + 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; +} + +double +DmpAlg::y0(double t, + double cl) +{ + return t - rd_ * cl * (1.0 - exp(-t / (rd_ * cl))); +} + +void +DmpAlg::dy(double t, + double t0, + double dt, + double cl, + double &dydt0, + double &dyddt, + double &dydcl) +{ + 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; + } + 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; + } +} + +double +DmpAlg::y0dt(double t, + double cl) +{ + return 1.0 - exp(-t / (rd_ * cl)); +} + +double +DmpAlg::y0dcl(double t, + double cl) +{ + return rd_ * ((1.0 + t / (rd_ * cl)) * exp(-t / (rd_ * cl)) - 1); +} + +void +DmpAlg::showX() +{ + for (int i = 0; i < nr_order_; i++) + debug_->print("%4s %12.3e\n", dmp_param_index_strings[i], x_[i]); +} + +void +DmpAlg::showFvec() +{ + for (int i = 0; i < nr_order_; i++) + debug_->print("%4s %12.3e\n", dmp_func_index_strings[i], fvec_[i]); +} + +void +DmpAlg::showJacobian() +{ + debug_->print(" "); + for (int j = 0; j < nr_order_; j++) + debug_->print("%12s", dmp_param_index_strings[j]); + debug_->print("\n"); + for (int i = 0; i < nr_order_; i++) { + debug_->print("%4s ", dmp_func_index_strings[i]); + for (int j = 0; j < nr_order_; j++) + debug_->print("%12.3e ", fjac_[i][j]); + debug_->print("\n"); + } +} + +// Return true if successful. +bool +DmpAlg::findDriverDelaySlew(double &delay, + double &slew) +{ + double tl, th; + if (findVoCrossing(vth_, delay) + && findVoCrossing(vl_, tl) + && findVoCrossing(vh_, th)) { + slew = (th - tl) / slew_derate_; + return true; + } + else + return false; +} + +// Find t such that vo(t)=v. +// Return true if successful. +bool +DmpAlg::findVoCrossing(double vth, + double &t) +{ + v_cross_ = vth; + double ub = voCrossingUpperBound(); + const char *error; + t = findRoot(evalVoEqns, this, t0_, ub, vth_time_tol, find_root_max_iter, + error); + if (error) + fail(error); + return (error == 0); +} + +static void +evalVoEqns(void *state, + double x, + double &y, + double &dy) +{ + DmpAlg *pi_ceff = reinterpret_cast(state); + y = pi_ceff->vo(x) - pi_ceff->vCross(); + dy = pi_ceff->dVoDt(x); +} + +double +DmpAlg::vo(double t) +{ + double t1 = t - t0_; + if (t1 <= 0.0) + return 0.0; + else if (t1 <= dt_) + return v0(t1) / dt_; + else + return (v0(t1) - v0(t1 - dt_)) / dt_; +} + +double +DmpAlg::dVoDt(double t) +{ + double t1 = t - t0_; + if (t1 <= 0) + return 0.0; + else if (t1 <= dt_) + return dv0dt(t1) / dt_; + else + return (dv0dt(t1) - dv0dt(t1 - dt_)) / dt_; +} + +void +DmpAlg::showVo() +{ + debug_->print(" t vo(t)\n"); + double ub = voCrossingUpperBound(); + for (double t = t0_; t < t0_ + ub; t += dt_ / 10.0) + debug_->print(" %g %g\n", t, vo(t)); + // debug_->print(" %.3g %.3g", t-t0_, vo(t)*3); + // debug_->print("\n"); +} + +void +DmpAlg::loadDelaySlew(const Pin *, + double elmore, + ArcDelay &delay, + Slew &slew) +{ + if (elmore == 0.0) { + delay = 0.0; + slew = static_cast(gate_slew_); + } + else if (elmore < gate_slew_ * 1e-3) { + // Elmore delay is small compared to driver slew. + delay = static_cast(elmore); + slew = static_cast(gate_slew_); + } + else { + elmore_ = elmore; + p3_ = 1.0 / elmore; + if (driver_valid_ + && debug_->check("delay_calc", 4)) + showVl(); + // Use the driver thresholds and rely on thresholdAdjust to + // convert the delay and slew to the load's thresholds. + double tl, th, load_delay; + if (driver_valid_ + && findVlCrossing(vth_, load_delay) + && findVlCrossing(vl_, tl) + && findVlCrossing(vh_, th)) { + // Measure delay from Vo, the load dependent source excitation. + double delay1 = load_delay - vo_delay_; + 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\n"); + // Use elmore delay. + delay1 = 1.0 / p3_; + } + if (slew1 < gate_slew_) { + // Only report a problem if the difference is significant. + if ((gate_slew_ - slew1) > vth_time_tol * gate_slew_) + fail("load slew less than driver slew\n"); + slew1 = static_cast(gate_slew_); + } + delay = static_cast(delay1); + slew = static_cast(slew1); + } + else { + // Failed - use elmore delay and driver slew. + delay = static_cast(elmore_); + slew = static_cast(gate_slew_); + } + } +} + +// Find t such that vl(t)=v. +// Return true if successful. +bool +DmpAlg::findVlCrossing(double vth, + double &t) +{ + v_cross_ = vth; + double ub = vlCrossingUpperBound(); + const char *error; + t = findRoot(evalVlEqns, this, t0_, ub, vth_time_tol, find_root_max_iter, + error); + if (error) + fail("findVlCrossing: Vl(t) did not cross threshold\n"); + return (error == 0); +} + +double +DmpAlg::vlCrossingUpperBound() +{ + return voCrossingUpperBound() + elmore_; +} + +static void +evalVlEqns(void *state, + double x, + double &y, + double &dy) +{ + DmpAlg *pi_ceff = reinterpret_cast(state); + y = pi_ceff->vl(x) - pi_ceff->vCross(); + dy = pi_ceff->dVlDt(x); +} + +double +DmpAlg::vl(double t) +{ + double t1 = t - t0_; + if (t1 <= 0) + return 0.0; + else if (t1 <= dt_) + return vl0(t1) / dt_; + else + return (vl0(t1) - vl0(t1 - dt_)) / dt_; +} + +double +DmpAlg::dVlDt(double t) +{ + double t1 = t - t0_; + if (t1 <= 0) + return 0.0; + else if (t1 <= dt_) + return dvl0dt(t1) / dt_; + else + return (dvl0dt(t1) - dvl0dt(t1 - dt_)) / dt_; +} + +void +DmpAlg::showVl() +{ + debug_->print(" t vl(t)\n"); + double ub = vlCrossingUpperBound(); + for (double t = t0_; t < t0_ + ub * 2.0; t += ub / 10.0) + debug_->print(" %g %g\n", t, vl(t)); +} + +void +DmpAlg::fail(const char *reason) +{ + // Allow only failures to be reported with a unique debug flag. + if (debug_->check("delay_calc", 1) || debug_->check("delay_calc_dmp", 1)) + debug_->print("delay_calc: DMP failed - %s", reason); +} + +//////////////////////////////////////////////////////////////// + +// Capacitive load. +class DmpCap : public DmpAlg +{ +public: + DmpCap(StaState *sta); + virtual const char *name() { return "cap"; } + virtual void init(const LibertyLibrary *library, + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const TransRiseFall *tr, + double rd, + double in_slew, + float related_out_cap, + double c2, + double rpi, + double c1); + virtual void gateDelaySlew(double &delay, + double &slew); + virtual void loadDelaySlew(const Pin *, + double elmore, + ArcDelay &delay, + Slew &slew); + virtual bool evalDmpEqns(); + virtual double voCrossingUpperBound(); + +private: + virtual double v0(double t); + virtual double dv0dt(double t); + virtual double vl0(double t); + virtual double dvl0dt(double t); +}; + +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 TransRiseFall *tr, + double rd, + double in_slew, + float related_out_cap, + double c2, + double, + double c1) +{ + debugPrint0(debug_, "delay_calc", 3, "Using DMP cap\n"); + DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, tr, + rd, in_slew, related_out_cap); + ceff_ = c1 + c2; +} + +void +DmpCap::gateDelaySlew(double &delay, + double &slew) +{ + debugPrint1(debug_, "delay_calc", 3, " ceff = %s\n", + units_->capacitanceUnit()->asString(ceff_)); + gateCapDelaySlew(ceff_, delay, slew); + gate_slew_ = slew; +} + +void +DmpCap::loadDelaySlew(const Pin *, + double elmore, + ArcDelay &delay, + Slew &slew) +{ + delay = static_cast(elmore); + slew = static_cast(gate_slew_); +} + +bool +DmpCap::evalDmpEqns() +{ + return true; +} + +double +DmpCap::v0(double) +{ + return 0.0; +} + +double +DmpCap::dv0dt(double) +{ + return 0.0; +} + +double +DmpCap::voCrossingUpperBound() +{ + return 0.0; +} + +double +DmpCap::vl0(double) +{ + return 0.0; +} + +double +DmpCap::dvl0dt(double) +{ + return 0.0; +} + +//////////////////////////////////////////////////////////////// + +// No non-zero pi model parameters, two poles, one zero +class DmpPi : public DmpAlg +{ +public: + DmpPi(StaState *sta); + virtual const char *name() { return "Pi"; } + virtual void init(const LibertyLibrary *library, + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const TransRiseFall *tr, + double rd, + double in_slew, + float related_out_cap, + double c2, + double rpi, + double c1); + virtual void gateDelaySlew(double &delay, + double &slew); + virtual bool evalDmpEqns(); + virtual double voCrossingUpperBound(); + +private: + bool findDriverParamsPi(); + virtual double v0(double t); + virtual double dv0dt(double t); + double ipiIceff(double t0, + double dt, + double ceff_time, + double ceff); + virtual double vl0(double t); + virtual double dvl0dt(double t); + + // Pi model. + double c1_; + double c2_; + double rpi_; + // Poles/zero. + double p1_; + double p2_; + double z1_; + // Residues. + double k0_; + double k1_; + double k2_; + double k3_; + double k4_; + // Ipi coefficients. + double A_; + double B_; + double D_; +}; + +DmpPi::DmpPi(StaState *sta) : + DmpAlg(3, sta), + c1_(0.0), + c2_(0.0), + rpi_(0.0), + 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) +{ +} + +void +DmpPi::init(const LibertyLibrary *drvr_library, + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const TransRiseFall *tr, + double rd, + double in_slew, + float related_out_cap, + double c2, + double rpi, + double c1) +{ + debugPrint0(debug_, "delay_calc", 3, "Using DMP Pi\n"); + DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, tr, rd, + in_slew, related_out_cap); + c1_ = c1; + c2_ = c2; + rpi_ = rpi; + + // 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); + p1_ = (b + sqrt_) / (2 * a); + p2_ = (b - sqrt_) / (2 * a); + + double p1p2 = (p1_ * p2_); + k2_ = z1_ / p1p2; + k1_ = (1.0 - k2_ * (p1_ + p2_)) / p1p2; + k4_ = (k1_ * p1_ + k2_) / (p2_ - p1_); + k3_ = -k1_ - k4_; + + double z_ = (c1_ + c2_) / (rpi_ * c1_ * c2_); + A_ = z_ / p1p2; + B_ = (z_ - p1_) / (p1_ * (p1_ - p2_)); + D_ = (z_ - p2_) / (p2_ * (p2_ - p1_)); +} + +void +DmpPi::gateDelaySlew(double &delay, + double &slew) +{ + if (findDriverParamsPi()) { + ceff_ = x_[DmpParam::ceff]; + driver_valid_ = true; + double table_slew; + // Table gate delays are more accurate than using Vo waveform delay + // (-12% max, -5% avg vs 26% max, -7% avg). + gateCapDelaySlew(ceff_, delay, table_slew); + // Vo slew is more accurate than table + // (-8% max, -3% avg vs -32% max, -12% avg). + // Need Vo delay to measure load wire delay waveform. + if (!findDriverDelaySlew(vo_delay_, slew)) + // Fall back to table slew if findDriverDelaySlew fails. + slew = table_slew; + } + else { + // Driver calculation failed - use Ceff=c1+c2. + ceff_ = c1_ + c2_; + gateCapDelaySlew(ceff_, delay, slew); + } + // Save for wire delay calc. + gate_slew_ = slew; +} + +bool +DmpPi::findDriverParamsPi() +{ + double ceff = c1_ + c2_; + x_[DmpParam::ceff] = ceff; + return findDriverParams(x_[DmpParam::ceff]); +} + +// Given x_ as a vector of input parameters, fill fvec_ with the +// equations evaluated at x_ and fjac_ with the jacobian evaluated at x_. +bool +DmpPi::evalDmpEqns() +{ + double t0 = x_[DmpParam::t0]; + double dt = x_[DmpParam::dt]; + double ceff = x_[DmpParam::ceff]; + + if (ceff > (c1_ + c2_) || ceff < 0.0) + return false; + + double t_vth, t_vl, slew; + gateDelays(ceff, t_vth, t_vl, slew); + double ceff_time = slew / (vh_ - vl_); + if (ceff_time > 1.4 * dt) + ceff_time = 1.4 * dt; + + if (dt <= 0.0) + return false; + + double exp_p1_dt = exp(-p1_ * dt); + double exp_p2_dt = exp(-p2_ * dt); + double exp_dt_rd_ceff = exp(-dt / (rd_ * ceff)); + + // y50 in the paper. + double y_t_vth = y(t_vth, t0, dt, ceff); + // y20 in the paper. Match Vl. + double y_t_vl = y(t_vl, t0, dt, ceff); + fvec_[DmpFunc::ipi] = ipiIceff(t0, dt, ceff_time, ceff); + fvec_[DmpFunc::y50] = y_t_vth - vth_; + fvec_[DmpFunc::y20] = y_t_vl - 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); + fjac_[DmpFunc::ipi][DmpParam::ceff] = + (2 * rd_ * ceff - dt - (2 * rd_ * ceff + dt) * exp(-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]); + + dy(t_vth, t0, dt, ceff, + fjac_[DmpFunc::y50][DmpParam::t0], + fjac_[DmpFunc::y50][DmpParam::dt], + fjac_[DmpFunc::y50][DmpParam::ceff]); + + if (debug_->check("delay_calc", 4)) { + showX(); + showFvec(); + showJacobian(); + debug_->print(".................\n"); + } + return true; +} + +// Eqn 13, Eqn 14. +double +DmpPi::ipiIceff(double, double dt, + double ceff_time, + double ceff) +{ + double exp_p1_dt = exp(-p1_ * ceff_time); + double exp_p2_dt = exp(-p2_ * ceff_time); + double exp_dt_rd_ceff = exp(-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); + return ipi - iceff; +} + +double +DmpPi::v0(double t) +{ + return k0_ * (k1_ + k2_ * t + k3_ * exp(-p1_ * t) + k4_ * exp(-p2_ * t)); +} + +double +DmpPi::dv0dt(double t) +{ + return k0_ * (k2_ - k3_ * p1_ * exp(-p1_ * t) - k4_ * p2_ * exp(-p2_ * t)); +} + +double +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_)); + return D1 + t + D3 * exp(-p1_ * t) + D4 * exp(-p2_ * t) + D5 * exp(-p3_ * t); +} + +double +DmpPi::voCrossingUpperBound() +{ + return t0_ + dt_ + (c1_ + c2_) * (rd_ + rpi_) * 2.0; +} + +double +DmpPi::dvl0dt(double t) +{ + 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_)); + return 1.0 - D3 * p1_ * exp(-p1_ * t) - D4 * p2_ * exp(-p2_ * t) + - D5 * p3_ * exp(-p3_ * t); +} + +//////////////////////////////////////////////////////////////// + +// Capacitive load, so Ceff is known. +// Solve for t0, delta t. +class DmpOnePole : public DmpAlg +{ +public: + DmpOnePole(StaState *sta); + virtual bool evalDmpEqns(); + virtual double voCrossingUpperBound(); +}; + +DmpOnePole::DmpOnePole(StaState *sta) : + DmpAlg(2, sta) +{ +} + +bool +DmpOnePole::evalDmpEqns() +{ + double t0 = x_[DmpParam::t0]; + double dt = x_[DmpParam::dt]; + + double t_vth, t_vl, ignore, dummy; + gateDelays(ceff_, t_vth, t_vl, ignore); + + 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_; + + if (debug_->check("delay_calc", 4)) { + showX(); + showFvec(); + } + + dy(t_vl, t0, dt, ceff_, + fjac_[DmpFunc::y20][DmpParam::t0], + fjac_[DmpFunc::y20][DmpParam::dt], + dummy); + + dy(t_vth, t0, dt, ceff_, + fjac_[DmpFunc::y50][DmpParam::t0], + fjac_[DmpFunc::y50][DmpParam::dt], + dummy); + + if (debug_->check("delay_calc", 4)) { + showJacobian(); + debug_->print(".................\n"); + } + return true; +} + +double +DmpOnePole::voCrossingUpperBound() +{ + return t0_ + dt_ + ceff_ * rd_ * 2.0; +} + +//////////////////////////////////////////////////////////////// + +// C2 = 0, one pole, one zero. +class DmpZeroC2 : public DmpOnePole +{ +public: + DmpZeroC2(StaState *sta); + virtual const char *name() { return "c2=0"; } + virtual void init(const LibertyLibrary *drvr_library, + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const TransRiseFall *tr, + double rd, + double in_slew, + float related_out_cap, + double c2, + double rpi, + double c1); + virtual void gateDelaySlew(double &delay, + double &slew); + +private: + virtual double v0(double t); + virtual double dv0dt(double t); + virtual double vl0(double t); + virtual double dvl0dt(double t); + virtual double voCrossingUpperBound(); + + double c1_; + double rpi_; + // Pole/zero. + double p1_; + double z1_; + // Residues. + double k0_; + double k1_; + double k2_; + double k3_; +}; + +DmpZeroC2::DmpZeroC2(StaState *sta) : + DmpOnePole(sta), + c1_(0.0), + rpi_(0.0), + p1_(0.0), + z1_(0.0), + k0_(0.0), + k1_(0.0), + k2_(0.0), + k3_(0.0) +{ +} + +void +DmpZeroC2::init(const LibertyLibrary *drvr_library, + const LibertyCell *drvr_cell, + const Pvt *pvt, + const GateTableModel *gate_model, + const TransRiseFall *tr, + double rd, + double in_slew, + float related_out_cap, + double, + double rpi, + double c1) +{ + debugPrint0(debug_, "delay_calc", 3, "Using DMP C2=0\n"); + DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, tr, rd, + in_slew, related_out_cap); + ceff_ = c1_ = c1; + rpi_ = rpi; + + z1_ = 1.0 / (rpi_ * c1_); + p1_ = 1.0 / (c1_ * (rd_ + rpi_)); + + k0_ = p1_ / z1_; + k2_ = 1.0 / k0_; + k1_ = (p1_ - z1_) / (p1_ * p1_); + k3_ = -k1_; +} + +void +DmpZeroC2::gateDelaySlew(double &delay, + double &slew) +{ + driver_valid_ = findDriverParams(c1_); + ceff_ = c1_; + if (!findDriverDelaySlew(delay, slew)) + // Fall back to table slew if findDriverDelaySlew fails. + gateCapDelaySlew(ceff_, delay, slew); + vo_delay_ = delay; + gate_slew_ = slew; +} + +double +DmpZeroC2::v0(double t) +{ + return k0_ * (k1_ + k2_ * t + k3_ * exp(-p1_ * t)); +} + +double +DmpZeroC2::dv0dt(double t) +{ + return k0_ * (k2_ - k3_ * p1_ * exp(-p1_ * t)); +} + +double +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_)); + return D1 + t + D3 * exp(-p1_ * t) + D5 * exp(-p3_ * t); +} + +double +DmpZeroC2::dvl0dt(double t) +{ + double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_); + double D5 = k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_)); + return 1.0 - D3 * p1_ * exp(-p1_ * t) - D5 * p3_ * exp(-p3_ * t); +} + +double +DmpZeroC2::voCrossingUpperBound() +{ + return t0_ + dt_ + c1_ * (rd_ + rpi_) * 2.0; +} + +//////////////////////////////////////////////////////////////// + +// Find the root of a function between x1 and x2 using a combination +// of Newton-Raphson and bisection search. +// x_tol is a percentage that change in x must be less than (1.0 = 100%). +// error is non-null if a problem occurs. +static double +findRoot(void (*func)(void *state, double x, double &y, double &dy), + void *state, + double x1, + double x2, + double x_tol, + int max_iter, + const char *&error) +{ + double y1, y2, dy; + func(state, x1, y1, dy); + func(state, x2, y2, dy); + + if ((y1 > 0.0 && y2 > 0.0) || (y1 < 0.0 && y2 < 0.0)) { + error = "findRoot: initial bounds do not surround a root\n"; + return 0.0; + } + + error = 0; + if (y1 == 0.0) + return x1; + + if (y2 == 0.0) + return x2; + + if (y1 > 0.0) { + // Swap x1/x2 so func(x1) < 0. + double tmp = x1; + x1 = x2; + x2 = tmp; + } + double root = (x1 + x2) * 0.5; + double dx_prev = abs(x2 - x1); + double dx = dx_prev; + double y; + func(state, 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))) { + // Bisect x1/x2 interval. + dx_prev = dx; + dx = (x2 - x1) * 0.5; + root = x1 + dx; + } + else { + dx_prev = dx; + dx = y / dy; + root -= dx; + } + if (abs(dx) <= x_tol * abs(root)) + // Converged. + return root; + + func(state, root, y, dy); + if (y < 0.0) + x1 = root; + else + x2 = root; + } + error = "findRoot: max iterations exceeded\n"; + return 0.0; +} + +// 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 true if successful. +static bool +newtonRaphson(const int max_iter, + double x[], + const int size, + const double x_tol, + bool (*eval)(void *state), + void *state, + // Temporaries supplied by caller. + double *fvec, + double **fjac, + int *index, + double *p, + double *scale, + const char *&error) +{ + for (int k = 0; k < max_iter; k++) { + if (!eval(state)) { + error = "Newton-Raphson eval failed.\n"; + return false; + } + + for (int i = 0; i < size; i++) + // Right-hand side of linear equations. + p[i] = -fvec[i]; + const char *lu_error; + if (luDecomp(fjac, size, index, scale, lu_error)) { + luSolve(fjac, size, index, p); + + 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]; + } + if (all_under_x_tol) + return true; + } + else { + error = lu_error; + return false; + } + } + error = "Newton-Raphson max iterations exceeded.\n"; + return false; +} + +// luDecomp, luSolve based on MatClass from C. R. Birchenhall, +// University of Manchester +// 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 +// 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 true if successful. +bool +luDecomp(double **a, + const int size, + int *index, + // Temporary supplied by caller. + // scale stores the implicit scaling of each row. + double *scale, + const char *&error) +{ + // 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; + } + if (big == 0.0) { + error = "LU decomposition: no non-zero row element.\n"; + return false; + } + 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]; + for (int k = 0; k < i; k++) + sum -= a[i][k] * a[k][j]; + a[i][j] = sum; + } + // Run down jth subdiag to form the residuals after the elimination + // of the first j-1 subdiags. These residuals divided by the + // appropriate diagonal term will become the multipliers in the + // elimination of the jth. subdiag. Find index of largest scaled + // term in imax. + double big = 0.0; + int imax = 0; + for (int i = j; i < size; i++) { + double sum = a[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); + if (dum >= big) { + 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; + } + scale[imax] = scale[j]; + } + 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 (j != size_1) { + double pivot = 1.0 / a[j][j]; + for (int i = j + 1; i < size; i++) + a[i][j] *= pivot; + } + } + return true; +} + +// 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. +void +luSolve(double **a, + const int size, + const int *index, + double b[]) +{ + // Transform b 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]; + if (non_zero != -1) { + for (int j = non_zero; j <= i - 1; j++) + sum -= a[i][j] * b[j]; + } + else { + if (sum != 0.0) + non_zero = i; + } + b[i] = sum; + } + // Backsubstitution. + for (int i = size - 1; i >= 0; i--) { + double sum = b[i]; + for (int j = i + 1; j < size; j++) + sum -= a[i][j] * b[j]; + b[i] = sum / a[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; + +DmpCeffDelayCalc::DmpCeffDelayCalc(StaState *sta) : + RCDelayCalc(sta), + dmp_cap_(new DmpCap(sta)), + dmp_pi_(new DmpPi(sta)), + dmp_zero_c2_(new DmpZeroC2(sta)), + dmp_alg_(nullptr) +{ +} + +DmpCeffDelayCalc::~DmpCeffDelayCalc() +{ + delete dmp_cap_; + delete dmp_pi_; + delete dmp_zero_c2_; +} + +void +DmpCeffDelayCalc::inputPortDelay(const Pin *port_pin, + float in_slew, + const TransRiseFall *tr, + Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap) +{ + dmp_alg_ = nullptr; + input_port_ = true; + RCDelayCalc::inputPortDelay(port_pin, in_slew, tr, parasitic, dcalc_ap); +} + +void +DmpCeffDelayCalc::gateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) +{ + input_port_ = false; + drvr_tr_ = arc->toTrans()->asRiseFall(); + drvr_library_ = drvr_cell->libertyLibrary(); + drvr_parasitic_ = drvr_parasitic; + GateTimingModel *model = gateModel(arc, dcalc_ap); + GateTableModel *table_model = dynamic_cast(model); + if (table_model && drvr_parasitic) { + float in_slew1 = delayAsFloat(in_slew); + float c2, rpi, c1; + parasitics_->piModel(drvr_parasitic, c2, rpi, c1); + setCeffAlgorithm(drvr_library_, drvr_cell, pvt, table_model, + in_slew1, related_out_cap, + c2, rpi, c1); + double dmp_gate_delay, dmp_drvr_slew; + gateDelaySlew(dmp_gate_delay, dmp_drvr_slew); + gate_delay = static_cast(dmp_gate_delay); + drvr_slew = static_cast(dmp_drvr_slew); + } + else { + LumpedCapDelayCalc::gateDelay(drvr_cell, arc, in_slew, load_cap, + drvr_parasitic, related_out_cap, pvt, + dcalc_ap, gate_delay, drvr_slew); + if (drvr_parasitic + && !unsuppored_model_warned_) { + unsuppored_model_warned_ = true; + report_->warn("cell %s delay model not supported on SPF parasitics by DMP delay calculator\n", + drvr_cell->name()); + } + } + drvr_slew_ = drvr_slew; + multi_drvr_slew_factor_ = 1.0F; +} + +void +DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library, + const LibertyCell *drvr_cell, + const Pvt *pvt, + GateTableModel *gate_model, + double in_slew, + float related_out_cap, + double c2, + double rpi, + double c1) +{ + double rd = gate_model + ? gateModelRd(drvr_cell, gate_model, in_slew, c2, c1, + related_out_cap, pvt, pocv_enabled_) + : 0.0; + // 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)) + dmp_alg_ = dmp_cap_; + else if (c2 < c1 * 1e-3) + dmp_alg_ = dmp_zero_c2_; + else + // The full monty. + dmp_alg_ = dmp_pi_; + dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, + drvr_tr_, rd, in_slew, related_out_cap, c2, rpi, c1); + debugPrint6(debug_, "delay_calc", 3, + " DMP in_slew = %s c2 = %s rpi = %s c1 = %s Rd = %s (%s alg)\n", + units_->timeUnit()->asString(in_slew), + units_->capacitanceUnit()->asString(c2), + units_->resistanceUnit()->asString(rpi), + units_->capacitanceUnit()->asString(c1), + units_->resistanceUnit()->asString(rd), + dmp_alg_->name()); +} + +float +DmpCeffDelayCalc::ceff(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap) +{ + ArcDelay gate_delay; + Slew drvr_slew; + gateDelay(drvr_cell, arc, in_slew, load_cap, + drvr_parasitic, related_out_cap, pvt, dcalc_ap, + gate_delay, drvr_slew); + if (dmp_alg_) + return static_cast(dmp_alg_->ceff()); + else + return load_cap; +} + +void +DmpCeffDelayCalc::reportGateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + int digits, + string *result) +{ + ArcDelay gate_delay; + Slew drvr_slew; + gateDelay(drvr_cell, arc, in_slew, load_cap, + drvr_parasitic, related_out_cap, pvt, dcalc_ap, + gate_delay, drvr_slew); + GateTimingModel *model = gateModel(arc, dcalc_ap); + float c_eff = 0.0; + if (drvr_parasitic_ && dmp_alg_) { + c_eff = dmp_alg_->ceff(); + const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); + const Units *units = drvr_library->units(); + const Unit *cap_unit = units->capacitanceUnit(); + const Unit *res_unit = units->resistanceUnit(); + float c2, rpi, c1; + parasitics_->piModel(drvr_parasitic_, c2, rpi, c1); + *result += "Pi model C2="; + *result += cap_unit->asString(c2, digits); + *result += " Rpi="; + *result += res_unit->asString(rpi, digits); + *result += " C1="; + *result += cap_unit->asString(c1, digits); + *result += ", Ceff="; + *result += cap_unit->asString(c_eff, digits); + *result += '\n'; + } + else + c_eff = load_cap; + if (model) { + float in_slew1 = delayAsFloat(in_slew); + model->reportGateDelay(drvr_cell, pvt, in_slew1, c_eff, + related_out_cap, pocv_enabled_, + digits, result); + } +} + +static double +gateModelRd(const LibertyCell *cell, + GateTableModel *gate_model, + double in_slew, + double c2, + double c1, + float related_out_cap, + const Pvt *pvt, + bool pocv_enabled) +{ + float cap1 = static_cast((c1 + c2) * .75); + float cap2 = cap1 * 1.1F; + float in_slew1 = static_cast(in_slew); + ArcDelay d1, d2; + Slew s1, s2; + gate_model->gateDelay(cell, pvt, in_slew1, cap1, related_out_cap, pocv_enabled, + d1, s1); + gate_model->gateDelay(cell, pvt, in_slew1, cap2, related_out_cap, pocv_enabled, + d2, s2); + return abs(delayAsFloat(d1) - delayAsFloat(d2)) / (cap2 - cap1); +} + +void +DmpCeffDelayCalc::gateDelaySlew(double &delay, + double &slew) +{ + dmp_alg_->gateDelaySlew(delay, slew); +} + +void +DmpCeffDelayCalc::loadDelaySlew(const Pin *load_pin, + double elmore, + ArcDelay &delay, + Slew &slew) +{ + if (dmp_alg_) + dmp_alg_->loadDelaySlew(load_pin, elmore, delay, slew); +} + +// Notify algorithm components. +void +DmpCeffDelayCalc::copyState(const StaState *sta) +{ + StaState::copyState(sta); + dmp_cap_->copyState(sta); + dmp_pi_->copyState(sta); + dmp_zero_c2_->copyState(sta); +} + +} // namespace diff --git a/dcalc/DmpCeff.hh b/dcalc/DmpCeff.hh new file mode 100644 index 0000000..95062d4 --- /dev/null +++ b/dcalc/DmpCeff.hh @@ -0,0 +1,104 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_DMP_CEFF_H +#define STA_DMP_CEFF_H + +#include "LibertyClass.hh" +#include "RCDelayCalc.hh" + +namespace sta { + +class DmpAlg; +class DmpCap; +class DmpPi; +class DmpZeroC2; + +// Delay calculator using Dartu/Menezes/Pileggi effective capacitance +// algorithm for RSPF loads. +class DmpCeffDelayCalc : public RCDelayCalc +{ +public: + DmpCeffDelayCalc(StaState *sta); + virtual ~DmpCeffDelayCalc(); + virtual void inputPortDelay(const Pin *port_pin, + float in_slew, + const TransRiseFall *tr, + Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap); + virtual void gateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // return values + ArcDelay &gate_delay, + Slew &drvr_slew); + virtual float ceff(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap); + virtual void reportGateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + int digits, + string *result); + virtual void copyState(const StaState *sta); + +protected: + void gateDelaySlew(double &delay, + double &slew); + void loadDelaySlew(const Pin *load_pin, + double elmore, + ArcDelay &delay, + Slew &slew); + // Select the appropriate special case Dartu/Menezes/Pileggi algorithm. + void setCeffAlgorithm(const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + GateTableModel *gate_model, + double in_slew, + float related_out_cap, + double c2, + double rpi, + double c1); + + bool input_port_; + static bool unsuppored_model_warned_; + +private: + // Dmp algorithms for each special pi model case. + // These objects are reused to minimize make/deletes. + DmpCap *dmp_cap_; + DmpPi *dmp_pi_; + DmpZeroC2 *dmp_zero_c2_; + DmpAlg *dmp_alg_; +}; + +} // namespace +#endif diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc new file mode 100644 index 0000000..dcb8518 --- /dev/null +++ b/dcalc/DmpDelayCalc.cc @@ -0,0 +1,414 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "Sdc.hh" +#include "Parasitics.hh" +#include "DcalcAnalysisPt.hh" +#include "GraphDelayCalc.hh" +#include "DmpCeff.hh" +#include "DmpDelayCalc.hh" + +namespace sta { + +// PiElmore parasitic delay calculator using Dartu/Menezes/Pileggi +// effective capacitance and elmore delay. +class DmpCeffElmoreDelayCalc : public DmpCeffDelayCalc +{ +public: + DmpCeffElmoreDelayCalc(StaState *sta); + virtual ArcDelayCalc *copy(); + virtual void gateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_, + Slew &drvr_slew); + virtual void loadDelay(const Pin *load_pin, + ArcDelay &wire_delay, + Slew &load_slew); +}; + +ArcDelayCalc * +makeDmpCeffElmoreDelayCalc(StaState *sta) +{ + return new DmpCeffElmoreDelayCalc(sta); +} + +DmpCeffElmoreDelayCalc::DmpCeffElmoreDelayCalc(StaState *sta) : + DmpCeffDelayCalc(sta) +{ +} + +ArcDelayCalc * +DmpCeffElmoreDelayCalc::copy() +{ + return new DmpCeffElmoreDelayCalc(this); +} + +void +DmpCeffElmoreDelayCalc::gateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) +{ + DmpCeffDelayCalc::gateDelay(drvr_cell, arc, in_slew, + load_cap, drvr_parasitic, related_out_cap, + pvt, dcalc_ap, + gate_delay, drvr_slew); +} + +void +DmpCeffElmoreDelayCalc::loadDelay(const Pin *load_pin, + ArcDelay &wire_delay, + Slew &load_slew) +{ + ArcDelay wire_delay1 = 0.0; + Slew load_slew1 = drvr_slew_; + bool elmore_exists = false; + float elmore = 0.0; + if (drvr_parasitic_) + parasitics_->findElmore(drvr_parasitic_, load_pin, elmore, elmore_exists); + if (elmore_exists) { + if (input_port_) { + // Input port with no external driver. + if (parasitics_->isReducedParasiticNetwork(drvr_parasitic_)) + dspfWireDelaySlew(load_pin, elmore, wire_delay1, load_slew1); + else { + // The elmore delay on an input port is used for the wire + // delay and the load slew is the same as the driver slew. + wire_delay1 = elmore; + load_slew1 = drvr_slew_; + } + } + else + loadDelaySlew(load_pin, elmore, wire_delay1, load_slew1); + } + thresholdAdjust(load_pin, wire_delay1, load_slew1); + wire_delay = wire_delay1; + load_slew = load_slew1 * multi_drvr_slew_factor_; +} + +//////////////////////////////////////////////////////////////// + +// PiPoleResidue parasitic delay calculator using Dartu/Menezes/Pileggi +// effective capacitance and two poles/residues. +class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc +{ +public: + DmpCeffTwoPoleDelayCalc(StaState *sta); + virtual ArcDelayCalc *copy(); + virtual Parasitic *findParasitic(const Pin *drvr_pin, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap); + virtual void inputPortDelay(const Pin *port_pin, + float in_slew, + const TransRiseFall *tr, + Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap); + virtual void gateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew); + virtual void loadDelay(const Pin *load_pin, + ArcDelay &wire_delay, + Slew &load_slew); + +private: + void loadDelay(Parasitic *pole_residue, + double p1, + double k1, + ArcDelay &wire_delay, + Slew &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); + + bool parasitic_is_pole_residue_; + float vth_; + float vl_; + float vh_; + float slew_derate_; +}; + +ArcDelayCalc * +makeDmpCeffTwoPoleDelayCalc(StaState *sta) +{ + return new DmpCeffTwoPoleDelayCalc(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) +{ +} + +ArcDelayCalc * +DmpCeffTwoPoleDelayCalc::copy() +{ + return new DmpCeffTwoPoleDelayCalc(this); +} + +Parasitic * +DmpCeffTwoPoleDelayCalc::findParasitic(const Pin *drvr_pin, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap) +{ + // set_load has precidence over parasitics. + if (!sdc_->drvrPinHasWireCap(drvr_pin)) { + Parasitic *parasitic = nullptr; + const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + if (parasitics_->haveParasitics()) { + // Prefer PiPoleResidue. + parasitic = parasitics_->findPiPoleResidue(drvr_pin, tr, + parasitic_ap); + if (parasitic) + return parasitic; + + parasitic = parasitics_->findPiElmore(drvr_pin, tr, parasitic_ap); + if (parasitic) + return parasitic; + + Parasitic *parasitic_network = + parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + if (parasitic_network) { + parasitics_->reduceToPiPoleResidue2(parasitic_network, drvr_pin, + dcalc_ap->operatingConditions(), + dcalc_ap->corner(), + dcalc_ap->constraintMinMax(), + parasitic_ap); + parasitic = parasitics_->findPiPoleResidue(drvr_pin, tr, parasitic_ap); + reduced_parasitic_drvrs_.push_back(drvr_pin); + return parasitic; + } + } + + const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); + Wireload *wireload = sdc_->wireloadDefaulted(cnst_min_max); + if (wireload) { + float pin_cap, wire_cap, fanout; + bool has_wire_cap; + graph_delay_calc_->netCaps(drvr_pin, tr, dcalc_ap, + pin_cap, wire_cap, fanout, has_wire_cap); + parasitic = parasitics_->estimatePiElmore(drvr_pin, tr, wireload, + fanout, pin_cap, + dcalc_ap->operatingConditions(), + dcalc_ap->corner(), + cnst_min_max, + parasitic_ap); + // Estimated parasitics are not recorded in the "database", so + // it for deletion after the drvr pin delay calc is finished. + unsaved_parasitics_.push_back(parasitic); + return parasitic; + } + } + return nullptr; +} + +void +DmpCeffTwoPoleDelayCalc::inputPortDelay(const Pin *port_pin, + float in_slew, + const TransRiseFall *tr, + Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap) +{ + parasitic_is_pole_residue_ = parasitics_->isPiPoleResidue(parasitic); + DmpCeffDelayCalc::inputPortDelay(port_pin, in_slew, tr, parasitic, dcalc_ap); +} + +void +DmpCeffTwoPoleDelayCalc::gateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) +{ + parasitic_is_pole_residue_ = parasitics_->isPiPoleResidue(drvr_parasitic); + const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); + const TransRiseFall *tr = arc->toTrans()->asRiseFall(); + vth_ = drvr_library->outputThreshold(tr); + vl_ = drvr_library->slewLowerThreshold(tr); + vh_ = drvr_library->slewUpperThreshold(tr); + slew_derate_ = drvr_library->slewDerateFromLibrary(); + DmpCeffDelayCalc::gateDelay(drvr_cell, arc, in_slew, + load_cap, drvr_parasitic, + related_out_cap, pvt, dcalc_ap, + gate_delay, drvr_slew); +} + +void +DmpCeffTwoPoleDelayCalc::loadDelay(const Pin *load_pin, + ArcDelay &wire_delay, + Slew &load_slew) +{ + // NEED to handle PiElmore parasitic. + ArcDelay wire_delay1 = 0.0; + Slew load_slew1 = drvr_slew_; + Parasitic *pole_residue = 0; + if (parasitic_is_pole_residue_) + pole_residue = parasitics_->findPoleResidue(drvr_parasitic_, load_pin); + if (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); + if (pole1.imag() == 0.0 + && residue1.imag() == 0.0) { + float p1 = pole1.real(); + float k1 = residue1.real(); + if (input_port_) { + float elmore = 1.0F / p1; + // Input port with no external driver. + if (parasitics_->isReducedParasiticNetwork(drvr_parasitic_)) + dspfWireDelaySlew(load_pin, elmore, wire_delay1, load_slew1); + else { + // For RSPF on an input port the elmore delay is used for the + // wire delay and the load slew is the same as the driver slew. + wire_delay1 = elmore; + load_slew1 = drvr_slew_; + } + } + else { + if (pole_count >= 2) + loadDelay(pole_residue, p1, k1, wire_delay1, load_slew1); + else { + float elmore = 1.0F / p1; + wire_delay1 = elmore; + load_slew1 = drvr_slew_; + } + } + } + } + } + thresholdAdjust(load_pin, wire_delay1, load_slew1); + wire_delay = wire_delay1; + load_slew = load_slew1 * multi_drvr_slew_factor_; +} + +void +DmpCeffTwoPoleDelayCalc::loadDelay(Parasitic *pole_residue, + double p1, double k1, + ArcDelay &wire_delay, + Slew &load_slew) +{ + ComplexFloat pole2, residue2; + parasitics_->poleResidue(pole_residue, 1, pole2, residue2); + if (!fuzzyZero(drvr_slew_) + && pole2.imag() == 0.0 + && residue2.imag() == 0.0) { + double p2 = pole2.real(); + double k2 = residue2.real(); + double k1_p1_2 = k1 / (p1 * p1); + double k2_p2_2 = k2 / (p2 * p2); + double B = k1_p1_2 + k2_p2_2; + // 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; + wire_delay = loadDelay(vth_, p1, p2, k1, k2, B, k1_p1_2, k2_p2_2, tt, y_tt) + - tt * vth_; + + float tl = loadDelay(vl_, p1, p2, k1, k2, B, k1_p1_2, k2_p2_2, tt, y_tt); + float th = loadDelay(vh_, p1, p2, k1, k2, B, k1_p1_2, k2_p2_2, tt, y_tt); + load_slew = (th - tl) / slew_derate_; + } +} + +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) +{ + if (y_tt < vth) { + // t1 > tt + // Initial guess. + double t1 = log(k1 * (exp(p1 * tt) - 1.0) / ((1.0 - vth) * p1 * p1 * tt))/p1; + // Take one newton-raphson step. + double exp_p1_t1 = exp(-p1 * t1); + double exp_p2_t1 = exp(-p2 * t1); + 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; + double yp_t1 = (k1 / p1 * (exp_p1_t1_tt - exp_p1_t1) + - k2 / p2 * (exp_p2_t1_tt - exp_p2_t1)) / tt; + double delay = t1 - (y_t1 - vth) / yp_t1; + return static_cast(delay); + } + else { + // t1 < tt + // Initial guess based on y(tt). + double t1 = vth * tt / y_tt; + // Take one newton-raphson step. + 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; + double yp_t1 = (1 - k1 / p1 * exp_p1_t1 + - k2 / p2 * exp_p2_t1) / tt; + double delay = t1 - (y_t1 - vth) / yp_t1; + return static_cast(delay); + } +} + +} // namespace diff --git a/dcalc/DmpDelayCalc.hh b/dcalc/DmpDelayCalc.hh new file mode 100644 index 0000000..3f3e82c --- /dev/null +++ b/dcalc/DmpDelayCalc.hh @@ -0,0 +1,28 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_DMP_DELAY_CALC_H +#define STA_DMP_DELAY_CALC_H + +namespace sta { + +ArcDelayCalc * +makeDmpCeffElmoreDelayCalc(StaState *sta); +ArcDelayCalc * +makeDmpCeffTwoPoleDelayCalc(StaState *sta); + +} // namespace +#endif diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc new file mode 100644 index 0000000..f446a4e --- /dev/null +++ b/dcalc/GraphDelayCalc.cc @@ -0,0 +1,175 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Sdc.hh" +#include "Corner.hh" +#include "GraphDelayCalc.hh" + +namespace sta { + +GraphDelayCalc::GraphDelayCalc(StaState *sta) : + StaState(sta) +{ +} + +void +GraphDelayCalc::copyState(const StaState *sta) +{ + StaState::copyState(sta); +} + +void +GraphDelayCalc::setObserver(DelayCalcObserver *observer) +{ + // Observer not needed by GraphDelayCalc. + delete observer; +} + +string * +GraphDelayCalc::reportDelayCalc(Edge *, + TimingArc *, + const Corner *, + const MinMax *, + int) +{ + return new string; +} + +float +GraphDelayCalc::incrementalDelayTolerance() +{ + return 0.0; +} + +void +GraphDelayCalc::loadCap(const Pin *, + Parasitic *, + const TransRiseFall *, + const DcalcAnalysisPt *, + // Return values. + float &pin_cap, + float &wire_cap) const +{ + pin_cap = wire_cap = 0.0F; +} + +float +GraphDelayCalc::loadCap(const Pin *, + const TransRiseFall *, + const DcalcAnalysisPt *) const +{ + return 0.0F; +} + +float +GraphDelayCalc::loadCap(const Pin *, + Parasitic *, + const TransRiseFall *, + const DcalcAnalysisPt *) const +{ + return 0.0F; +} + +float +GraphDelayCalc::loadCap(const Pin *, + const DcalcAnalysisPt *) const +{ + return 0.0F; +} + +void +GraphDelayCalc::netCaps(const Pin *, + const TransRiseFall *, + const DcalcAnalysisPt *, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_set_load) const +{ + pin_cap = wire_cap = fanout = 0.0F; + has_set_load = false; +} + +float +GraphDelayCalc::ceff(Edge *, + TimingArc *, + const DcalcAnalysisPt *) +{ + return 0.0; +} + +void +GraphDelayCalc::minPulseWidth(const Pin *pin, + const TransRiseFall *hi_low, + DcalcAPIndex ap_index, + const MinMax *min_max, + // Return values. + float &min_width, + bool &exists) +{ + // Sdf annotation. + graph_->widthCheckAnnotation(pin, hi_low, ap_index, + min_width, exists); + if (!exists) { + // Liberty library. + LibertyPort *port = network_->libertyPort(pin); + if (port) { + Instance *inst = network_->instance(pin); + Pvt *pvt = inst ? sdc_->pvt(inst, min_max) : nullptr; + OperatingConditions *op_cond=sdc_->operatingConditions(min_max); + port->minPulseWidth(hi_low, op_cond, pvt, min_width, exists); + } + } +} + +void +GraphDelayCalc::minPeriod(const Pin *pin, + // Return values. + float &min_period, + bool &exists) +{ + exists = false; + const MinMax *min_max = MinMax::max(); + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + // Sdf annotation. + float min_period1 = 0.0; + bool exists1 = false; + graph_->periodCheckAnnotation(pin, dcalc_ap->index(), + min_period, exists); + if (exists1 + && (!exists || min_period1 < min_period)) { + min_period = min_period1; + exists = true; + } + } + if (!exists) { + LibertyPort *port = network_->libertyPort(pin); + if (port) { + // Liberty library. + Instance *inst = network_->instance(pin); + OperatingConditions *op_cond = sdc_->operatingConditions(min_max); + Pvt *pvt = inst ? sdc_->pvt(inst, min_max) : nullptr; + port->minPeriod(op_cond, pvt, min_period, exists); + } + } +} + +} // namespace diff --git a/dcalc/GraphDelayCalc.hh b/dcalc/GraphDelayCalc.hh new file mode 100644 index 0000000..103a93b --- /dev/null +++ b/dcalc/GraphDelayCalc.hh @@ -0,0 +1,143 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_GRAPH_DELAY_CALC_H +#define STA_GRAPH_DELAY_CALC_H + +#include +#include "DisallowCopyAssign.hh" +#include "StaState.hh" +#include "GraphClass.hh" +#include "Delay.hh" +#include "DcalcAnalysisPt.hh" + +namespace sta { + +using std::string; + +class BfsFwdIterator; +class SearchPred; +class DelayCalcObserver; +class Parasitic; +class Corner; + +// Base class for graph delay calculator. +// This class annotates the arc delays and slews on the graph by calling +// the timing arc delay calculation primitive through an implementation +// of the ArcDelayCalc abstract class. +// This class does not traverse the graph or call an arc delay +// calculator. Use it with applications that use an external delay +// calculator and annotate all edge delays. +class GraphDelayCalc : public StaState +{ +public: + explicit GraphDelayCalc(StaState *sta); + virtual ~GraphDelayCalc() {} + virtual void copyState(const StaState *sta); + // Find arc delays and vertex slews thru level. + virtual void findDelays(Level /* level */) {}; + // Invalidate all delays/slews. + virtual void delaysInvalid() {}; + // Invalidate vertex and downstream delays/slews. + virtual void delayInvalid(Vertex * /* vertex */) {} +; + virtual void delayInvalid(const Pin * /* pin */) {}; + virtual void deleteVertexBefore(Vertex * /* vertex */) {}; + // Reset to virgin state. + virtual void clear() {} + // Returned string is owned by the caller. + virtual string *reportDelayCalc(Edge *edge, + TimingArc *arc, + const Corner *corner, + const MinMax *min_max, + int digits); + // Percentage (0.0:1.0) change in delay that causes downstream + // delays to be recomputed during incremental delay calculation. + virtual float incrementalDelayTolerance(); + virtual void setIncrementalDelayTolerance(float /* tol */) {} + // Set the observer for edge delay changes. + virtual void setObserver(DelayCalcObserver *observer); + // pin_cap = net pin capacitances + port external pin capacitance, + // wire_cap = annotated net capacitance + port external wire capacitance. + virtual void loadCap(const Pin *drvr_pin, + Parasitic *drvr_parasitic, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + float &pin_cap, + float &wire_cap) const; + // Load pin_cap + wire_cap including parasitic. + virtual float loadCap(const Pin *drvr_pin, + const TransRiseFall *to_tr, + const DcalcAnalysisPt *dcalc_ap) const; + // Load pin_cap + wire_cap including parasitic min/max for rise/fall. + virtual float loadCap(const Pin *drvr_pin, + const DcalcAnalysisPt *dcalc_ap) const; + // Load pin_cap + wire_cap. + virtual float loadCap(const Pin *drvr_pin, + Parasitic *drvr_parasitic, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap) const; + virtual void netCaps(const Pin *drvr_pin, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_set_load) const; + virtual float ceff(Edge *edge, + TimingArc *arc, + const DcalcAnalysisPt *dcalc_ap); + // Precedence: + // SDF annotation + // Liberty library + // (ignores set_min_pulse_width constraint) + void minPulseWidth(const Pin *pin, + const TransRiseFall *hi_low, + DcalcAPIndex ap_index, + const MinMax *min_max, + // Return values. + float &min_width, + bool &exists); + // Precedence: + // SDF annotation + // Liberty library + void minPeriod(const Pin *pin, + // Return values. + float &min_period, + bool &exists); + +private: + DISALLOW_COPY_AND_ASSIGN(GraphDelayCalc); +}; + +// Abstract base class for edge delay change observer. +class DelayCalcObserver +{ +public: + DelayCalcObserver() {} + virtual ~DelayCalcObserver() {} + virtual void delayChangedFrom(Vertex *vertex) = 0; + virtual void delayChangedTo(Vertex *vertex) = 0; + virtual void checkDelayChangedTo(Vertex *vertex) = 0; + +private: + DISALLOW_COPY_AND_ASSIGN(DelayCalcObserver); +}; + +} // namespace +#endif diff --git a/dcalc/GraphDelayCalc1.cc b/dcalc/GraphDelayCalc1.cc new file mode 100644 index 0000000..3e606e6 --- /dev/null +++ b/dcalc/GraphDelayCalc1.cc @@ -0,0 +1,1783 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Debug.hh" +#include "Stats.hh" +#include "Mutex.hh" +#include "MinMax.hh" +#include "PortDirection.hh" +#include "TimingRole.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "InputDrive.hh" +#include "Sdc.hh" +#include "Corner.hh" +#include "Graph.hh" +#include "Levelize.hh" +#include "SearchPred.hh" +#include "Bfs.hh" +#include "Parasitics.hh" +#include "ArcDelayCalc.hh" +#include "DcalcAnalysisPt.hh" +#include "NetCaps.hh" +#include "GraphDelayCalc1.hh" + +namespace sta { + +using std::abs; + +static const Slew default_slew = 0.0; + +typedef Set MultiDrvrNetSet; + +static bool +isLeafDriver(const Pin *pin, + const Network *network); + +// Cache parallel delay/slew values for nets with multiple drivers. +class MultiDrvrNet +{ +public: + MultiDrvrNet(VertexSet *drvrs); + ~MultiDrvrNet(); + const VertexSet *drvrs() const { return drvrs_; } + VertexSet *drvrs() { return drvrs_; } + Vertex *dcalcDrvr() const { return dcalc_drvr_; } + void setDcalcDrvr(Vertex *drvr); + void parallelDelaySlew(const TransRiseFall *drvr_tr, + const DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc, + GraphDelayCalc1 *dcalc, + // Return values. + ArcDelay ¶llel_delay, + Slew ¶llel_slew); + void netCaps(const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_set_load); + void findCaps(const GraphDelayCalc1 *dcalc, + const Sdc *sdc); + +private: + void findDelaysSlews(ArcDelayCalc *arc_delay_calc, + GraphDelayCalc1 *dcalc); + + // Driver that triggers delay calculation for all the drivers on the net. + Vertex *dcalc_drvr_; + VertexSet *drvrs_; + // [drvr_tr->index][dcalc_ap->index] + ArcDelay *parallel_delays_; + // [drvr_tr->index][dcalc_ap->index] + Slew *parallel_slews_; + // [drvr_tr->index][dcalc_ap->index] + NetCaps *net_caps_; + bool delays_valid_:1; +}; + +MultiDrvrNet::MultiDrvrNet(VertexSet *drvrs) : + dcalc_drvr_(nullptr), + drvrs_(drvrs), + parallel_delays_(nullptr), + parallel_slews_(nullptr), + net_caps_(nullptr), + delays_valid_(false) +{ +} + +MultiDrvrNet::~MultiDrvrNet() +{ + delete drvrs_; + if (delays_valid_) { + delete [] parallel_delays_; + delete [] parallel_slews_; + } + delete [] net_caps_; +} + +void +MultiDrvrNet::parallelDelaySlew(const TransRiseFall *drvr_tr, + const DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc, + GraphDelayCalc1 *dcalc, + // Return values. + ArcDelay ¶llel_delay, + Slew ¶llel_slew) +{ + if (!delays_valid_) { + findDelaysSlews(arc_delay_calc, dcalc); + delays_valid_ = true; + } + + int index = dcalc_ap->index() * TransRiseFall::index_count + + drvr_tr->index(); + parallel_delay = parallel_delays_[index]; + parallel_slew = parallel_slews_[index]; +} + +void +MultiDrvrNet::findDelaysSlews(ArcDelayCalc *arc_delay_calc, + GraphDelayCalc1 *dcalc) +{ + Corners *corners = dcalc->corners(); + int count = TransRiseFall::index_count * corners->dcalcAnalysisPtCount(); + parallel_delays_ = new ArcDelay[count]; + parallel_slews_ = new Slew[count]; + for (auto dcalc_ap : corners->dcalcAnalysisPts()) { + DcalcAPIndex ap_index = dcalc_ap->index(); + const Pvt *pvt = dcalc_ap->operatingConditions(); + for (auto drvr_tr : TransRiseFall::range()) { + int drvr_tr_index = drvr_tr->index(); + int index = ap_index*TransRiseFall::index_count+drvr_tr_index; + dcalc->findMultiDrvrGateDelay(this, drvr_tr, pvt, dcalc_ap, + arc_delay_calc, + parallel_delays_[index], + parallel_slews_[index]); + } + } +} + +void +MultiDrvrNet::netCaps(const TransRiseFall *drvr_tr, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_set_load) +{ + int index = dcalc_ap->index() * TransRiseFall::index_count + + drvr_tr->index(); + NetCaps &net_caps = net_caps_[index]; + pin_cap = net_caps.pinCap(); + wire_cap = net_caps.wireCap(); + fanout = net_caps.fanout(); + has_set_load = net_caps.hasSetLoad(); +} + +void +MultiDrvrNet::findCaps(const GraphDelayCalc1 *dcalc, + const Sdc *sdc) +{ + Corners *corners = dcalc->corners(); + int count = TransRiseFall::index_count * corners->dcalcAnalysisPtCount(); + net_caps_ = new NetCaps[count]; + const Pin *drvr_pin = dcalc_drvr_->pin(); + for (auto dcalc_ap : corners->dcalcAnalysisPts()) { + DcalcAPIndex ap_index = dcalc_ap->index(); + const Corner *corner = dcalc_ap->corner(); + const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); + const MinMax *min_max = dcalc_ap->constraintMinMax(); + for (auto drvr_tr : TransRiseFall::range()) { + int drvr_tr_index = drvr_tr->index(); + int index = ap_index * TransRiseFall::index_count + drvr_tr_index; + NetCaps &net_caps = net_caps_[index]; + float pin_cap, wire_cap, fanout; + bool has_set_load; + // Find pin and external pin/wire capacitance. + sdc->connectedCap(drvr_pin, drvr_tr, op_cond, corner, min_max, + pin_cap, wire_cap, fanout, has_set_load); + net_caps.init(pin_cap, wire_cap, fanout, has_set_load); + } + } +} + +void +MultiDrvrNet::setDcalcDrvr(Vertex *drvr) +{ + dcalc_drvr_ = drvr; +} + +//////////////////////////////////////////////////////////////// + + +GraphDelayCalc1::GraphDelayCalc1(StaState *sta) : + GraphDelayCalc(sta), + observer_(nullptr), + delays_seeded_(false), + incremental_(false), + 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)), + multi_drvr_nets_found_(false), + incremental_delay_tolerance_(0.0) +{ +} + +GraphDelayCalc1::~GraphDelayCalc1() +{ + delete search_pred_; + delete search_non_latch_pred_; + delete clk_pred_; + delete iter_; + deleteMultiDrvrNets(); + clearIdealClkMap(); + delete observer_; +} + +void +GraphDelayCalc1::deleteMultiDrvrNets() +{ + MultiDrvrNetSet drvr_nets; + MultiDrvrNetMap::Iterator multi_iter(multi_drvr_net_map_); + while (multi_iter.hasNext()) { + MultiDrvrNet *multi_drvr = multi_iter.next(); + // 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(); +} + +void +GraphDelayCalc1::copyState(const StaState *sta) +{ + GraphDelayCalc::copyState(sta); + // Notify sub-components. + iter_->copyState(sta); +} + +void +GraphDelayCalc1::clear() +{ + delaysInvalid(); + deleteMultiDrvrNets(); + multi_drvr_nets_found_ = false; + GraphDelayCalc::clear(); +} + +float +GraphDelayCalc1::incrementalDelayTolerance() +{ + return incremental_delay_tolerance_; +} + +void +GraphDelayCalc1::setIncrementalDelayTolerance(float tol) +{ + incremental_delay_tolerance_ = tol; +} + +void +GraphDelayCalc1::setObserver(DelayCalcObserver *observer) +{ + delete observer_; + observer_ = observer; +} + +void +GraphDelayCalc1::delaysInvalid() +{ + debugPrint0(debug_, "delay_calc", 1, "delays invalid\n"); + delays_exist_ = false; + delays_seeded_ = false; + incremental_ = false; + iter_->clear(); + clearIdealClkMap(); + // No need to keep track of incremental updates any more. + invalid_delays_.clear(); + invalid_checks_.clear(); +} + +void +GraphDelayCalc1::delayInvalid(const Pin *pin) +{ + if (graph_ + && incremental_) { + if (network_->isHierarchical(pin)) { + EdgesThruHierPinIterator edge_iter(pin, network_, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + delayInvalid(edge->from(graph_)); + } + } + else { + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex) + delayInvalid(vertex); + if (bidirect_drvr_vertex) + delayInvalid(bidirect_drvr_vertex); + } + } +} + +void +GraphDelayCalc1::delayInvalid(Vertex *vertex) +{ + debugPrint1(debug_, "delay_calc", 2, "delays invalid %s\n", + vertex->name(sdc_network_)); + if (graph_ && incremental_) { + 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()); + } +} + +void +GraphDelayCalc1::deleteVertexBefore(Vertex *vertex) +{ + iter_->deleteVertexBefore(vertex); + if (incremental_) + invalid_delays_.erase(vertex); + MultiDrvrNet *multi_drvr = multiDrvrNet(vertex); + if (multi_drvr) { + multi_drvr->drvrs()->erase(vertex); + multi_drvr_net_map_.erase(vertex); + } +} + +//////////////////////////////////////////////////////////////// + +class FindVertexDelays : public VertexVisitor +{ +public: + FindVertexDelays(GraphDelayCalc1 *graph_delay_calc1, + ArcDelayCalc *arc_delay_calc, + bool own_arc_delay_calc); + virtual ~FindVertexDelays(); + virtual void visit(Vertex *vertex); + virtual VertexVisitor *copy(); + virtual void levelFinished(); + +protected: + GraphDelayCalc1 *graph_delay_calc1_; + ArcDelayCalc *arc_delay_calc_; + bool own_arc_delay_calc_; +}; + +FindVertexDelays::FindVertexDelays(GraphDelayCalc1 *graph_delay_calc1, + ArcDelayCalc *arc_delay_calc, + bool own_arc_delay_calc) : + VertexVisitor(), + graph_delay_calc1_(graph_delay_calc1), + arc_delay_calc_(arc_delay_calc), + own_arc_delay_calc_(own_arc_delay_calc) +{ +} + +FindVertexDelays::~FindVertexDelays() +{ + if (own_arc_delay_calc_) + delete arc_delay_calc_; +} + +VertexVisitor * +FindVertexDelays::copy() +{ + // Copy StaState::arc_delay_calc_ because it needs separate state + // for each thread. + return new FindVertexDelays(graph_delay_calc1_,arc_delay_calc_->copy(),true); +} + +void +FindVertexDelays::visit(Vertex *vertex) +{ + graph_delay_calc1_->findVertexDelay(vertex, arc_delay_calc_, true); +} + +void +FindVertexDelays::levelFinished() +{ + graph_delay_calc1_->mergeIdealClks(); +} + +void +GraphDelayCalc1::mergeIdealClks() +{ + for (auto vertex_clks : ideal_clks_map_next_) { + const Vertex *vertex = vertex_clks.first; + ClockSet *prev_clks = ideal_clks_map_.findKey(vertex); + delete prev_clks; + ideal_clks_map_[vertex] = vertex_clks.second; + } + ideal_clks_map_next_.clear(); +} + +// The logical structure of incremental delay calculation closely +// resembles the incremental search arrival time algorithm +// (Search::findArrivals). +void +GraphDelayCalc1::findDelays(Level level) +{ + if (arc_delay_calc_) { + Stats stats(debug_); + int dcalc_count = 0; + debugPrint1(debug_, "delay_calc", 1, "find delays to level %d\n", level); + if (!delays_seeded_) { + iter_->clear(); + ensureMultiDrvrNetsFound(); + seedRootSlews(); + delays_seeded_ = true; + } + else + iter_->ensureSize(); + if (incremental_) + seedInvalidDelays(); + + mergeIdealClks(); + FindVertexDelays visitor(this, arc_delay_calc_, false); + dcalc_count += iter_->visitParallel(level, &visitor); + + // Timing checks require slews at both ends of the arc, + // so find their delays after all slews are known. + for (Vertex *check_vertex : invalid_checks_) + findCheckDelays(check_vertex, arc_delay_calc_); + invalid_checks_.clear(); + + delays_exist_ = true; + incremental_ = true; + debugPrint1(debug_, "delay_calc", 1, "found %d delays\n", dcalc_count); + stats.report("Delay calc"); + } +} + +void +GraphDelayCalc1::seedInvalidDelays() +{ + VertexSet::Iterator vertex_iter(invalid_delays_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + if (vertex->isRoot()) + seedRootSlew(vertex, arc_delay_calc_); + else { + if (search_non_latch_pred_->searchFrom(vertex)) + iter_->enqueue(vertex); + } + } + invalid_delays_.clear(); +} + +class FindNetDrvrs : public PinVisitor +{ +public: + FindNetDrvrs(PinSet &drvr_pins, + const Network *network, + const Graph *graph); + virtual void operator()(Pin *pin); + +protected: + PinSet &drvr_pins_; + const Network *network_; + const Graph *graph_; +}; + +FindNetDrvrs::FindNetDrvrs(PinSet &drvr_pins, + const Network *network, + const Graph *graph) : + drvr_pins_(drvr_pins), + network_(network), + graph_(graph) +{ +} + +void +FindNetDrvrs::operator()(Pin *pin) +{ + Vertex *vertex = graph_->pinDrvrVertex(pin); + if (isLeafDriver(pin, network_) + && !vertex->isRoot()) + drvr_pins_.insert(pin); +} + +void +GraphDelayCalc1::ensureMultiDrvrNetsFound() +{ + if (!multi_drvr_nets_found_) { + LeafInstanceIterator *inst_iter = network_->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + Vertex *drvr_vertex = graph_->pinDrvrVertex(pin); + if (network_->isDriver(pin) + && !multi_drvr_net_map_.hasKey(drvr_vertex)) { + PinSet drvr_pins; + FindNetDrvrs visitor(drvr_pins, network_, graph_); + network_->visitConnectedPins(pin, visitor); + if (drvr_pins.size() > 1) + makeMultiDrvrNet(drvr_pins); + } + } + delete pin_iter; + } + delete inst_iter; + multi_drvr_nets_found_ = true; + } +} + +void +GraphDelayCalc1::makeMultiDrvrNet(PinSet &drvr_pins) +{ + debugPrint0(debug_, "delay_calc", 3, "multi-driver net\n"); + VertexSet *drvr_vertices = new VertexSet; + MultiDrvrNet *multi_drvr = new MultiDrvrNet(drvr_vertices); + Level max_drvr_level = 0; + Vertex *max_drvr = nullptr; + PinSet::Iterator pin_iter(drvr_pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + Vertex *drvr_vertex = graph_->pinDrvrVertex(pin); + debugPrint1(debug_, "delay_calc", 3, " %s\n", + network_->pathName(pin)); + multi_drvr_net_map_[drvr_vertex] = multi_drvr; + drvr_vertices->insert(drvr_vertex); + Level drvr_level = drvr_vertex->level(); + if (max_drvr == nullptr + || drvr_level > max_drvr_level) + max_drvr = drvr_vertex; + } + multi_drvr->setDcalcDrvr(max_drvr); + multi_drvr->findCaps(this, sdc_); +} + +static bool +isLeafDriver(const Pin *pin, + const Network *network) +{ + PortDirection *dir = network->direction(pin); + const Instance *inst = network->instance(pin); + return network->isLeaf(inst) && dir->isAnyOutput(); +} + +MultiDrvrNet * +GraphDelayCalc1::multiDrvrNet(const Vertex *drvr_vertex) const +{ + return multi_drvr_net_map_.findKey(drvr_vertex); +} + +void +GraphDelayCalc1::seedRootSlews() +{ + VertexSet::Iterator root_iter(levelize_->roots()); + while (root_iter.hasNext()) { + Vertex *vertex = root_iter.next(); + seedRootSlew(vertex, arc_delay_calc_); + findIdealClks(vertex); + } +} + +void +GraphDelayCalc1::seedRootSlew(Vertex *vertex, + ArcDelayCalc *arc_delay_calc) +{ + if (vertex->isDriver(network_)) + seedDrvrSlew(vertex, arc_delay_calc); + else + seedLoadSlew(vertex); + iter_->enqueueAdjacentVertices(vertex); +} + +void +GraphDelayCalc1::seedDrvrSlew(Vertex *drvr_vertex, + ArcDelayCalc *arc_delay_calc) +{ + const Pin *drvr_pin = drvr_vertex->pin(); + debugPrint1(debug_, "delay_calc", 2, "seed driver slew %s\n", + drvr_vertex->name(sdc_network_)); + InputDrive *drive = 0; + if (network_->isTopLevelPort(drvr_pin)) { + Port *port = network_->port(drvr_pin); + drive = sdc_->findInputDrive(port); + } + for (auto tr : TransRiseFall::range()) { + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + if (drive) { + const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); + LibertyCell *drvr_cell; + LibertyPort *from_port, *to_port; + float *from_slews; + drive->driveCell(tr, 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, tr, + from_port, from_slews, to_port, dcalc_ap); + } + else + seedNoDrvrCellSlew(drvr_vertex, drvr_pin, tr, drive, dcalc_ap, + arc_delay_calc); + } + else + seedNoDrvrSlew(drvr_vertex, drvr_pin, tr, dcalc_ap, arc_delay_calc); + } + } +} + +void +GraphDelayCalc1::seedNoDrvrCellSlew(Vertex *drvr_vertex, + const Pin *drvr_pin, + const TransRiseFall *tr, + InputDrive *drive, + DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc) +{ + DcalcAPIndex ap_index = dcalc_ap->index(); + const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); + Slew slew = default_slew; + float drive_slew; + bool exists; + drive->slew(tr, cnst_min_max, drive_slew, exists); + if (exists) + slew = drive_slew; + else { + // Top level bidirect driver uses load slew unless + // bidirect instance paths are disabled. + if (sdc_->bidirectDrvrSlewFromLoad(drvr_pin)) { + Vertex *load_vertex = graph_->pinLoadVertex(drvr_pin); + slew = graph_->slew(load_vertex, tr, ap_index); + } + } + Delay drive_delay = delay_zero; + float drive_res; + drive->driveResistance(tr, cnst_min_max, drive_res, exists); + Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, tr, dcalc_ap); + if (exists) { + float cap = loadCap(drvr_pin, parasitic, tr, dcalc_ap); + drive_delay = cap * drive_res; + slew = cap * drive_res; + } + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + if (!drvr_vertex->slewAnnotated(tr, slew_min_max)) + graph_->setSlew(drvr_vertex, tr, ap_index, slew); + arc_delay_calc->inputPortDelay(drvr_pin, delayAsFloat(slew), tr, + parasitic, dcalc_ap); + annotateLoadDelays(drvr_vertex, tr, drive_delay, false, dcalc_ap, + arc_delay_calc); +} + +void +GraphDelayCalc1::seedNoDrvrSlew(Vertex *drvr_vertex, + const Pin *drvr_pin, + const TransRiseFall *tr, + DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc) +{ + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + DcalcAPIndex ap_index = dcalc_ap->index(); + Slew slew(default_slew); + // Top level bidirect driver uses load slew unless + // bidirect instance paths are disabled. + if (sdc_->bidirectDrvrSlewFromLoad(drvr_pin)) { + Vertex *load_vertex = graph_->pinLoadVertex(drvr_pin); + slew = graph_->slew(load_vertex, tr, ap_index); + } + if (!drvr_vertex->slewAnnotated(tr, slew_min_max)) + graph_->setSlew(drvr_vertex, tr, ap_index, slew); + Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, tr, dcalc_ap); + arc_delay_calc->inputPortDelay(drvr_pin, delayAsFloat(slew), tr, + parasitic, dcalc_ap); + annotateLoadDelays(drvr_vertex, tr, delay_zero, false, dcalc_ap, + arc_delay_calc); +} + +void +GraphDelayCalc1::seedLoadSlew(Vertex *vertex) +{ + const Pin *pin = vertex->pin(); + debugPrint1(debug_, "delay_calc", 2, "seed load slew %s\n", + vertex->name(sdc_network_)); + ClockSet *clks = sdc_->findVertexPinClocks(pin); + initSlew(vertex); + for (auto tr : TransRiseFall::range()) { + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + if (!vertex->slewAnnotated(tr, 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(tr, slew_min_max); + if (slew_min_max->compare(clk_slew, slew)) + slew = clk_slew; + } + } + DcalcAPIndex ap_index = dcalc_ap->index(); + graph_->setSlew(vertex, tr, ap_index, slew); + } + } + } +} + +// If a driving cell does not specify a -from_pin, the first port +// defined in the cell that has a timing group to the output port +// is used. Not exactly reasonable, but it's compatible. +LibertyPort * +GraphDelayCalc1::driveCellDefaultFromPort(LibertyCell *cell, + LibertyPort *to_port) +{ + LibertyPort *from_port = 0; + int from_port_index = 0; + LibertyCellTimingArcSetIterator set_iter(cell); + while (set_iter.hasNext()) { + TimingArcSet *arc_set = set_iter.next(); + if (arc_set->to() == to_port) { + LibertyPort *set_from_port = arc_set->from(); + int set_from_port_index = findPortIndex(cell, set_from_port); + if (from_port == nullptr + || set_from_port_index < from_port_index) { + from_port = set_from_port; + from_port_index = set_from_port_index; + } + } + } + return from_port; +} + +// Find the index that port is defined in cell. +int +GraphDelayCalc1::findPortIndex(LibertyCell *cell, + LibertyPort *port) +{ + int index = 0; + LibertyCellPortIterator port_iter(cell); + while (port_iter.hasNext()) { + LibertyPort *cell_port = port_iter.next(); + if (cell_port == port) + return index; + index++; + } + internalError("port not found in cell"); + return 0; +} + +void +GraphDelayCalc1::findInputDriverDelay(LibertyCell *drvr_cell, + const Pin *drvr_pin, + Vertex *drvr_vertex, + const TransRiseFall *tr, + LibertyPort *from_port, + float *from_slews, + LibertyPort *to_port, + DcalcAnalysisPt *dcalc_ap) +{ + debugPrint2(debug_, "delay_calc", 2, " driver cell %s %s\n", + drvr_cell->name(), + tr->asString()); + LibertyCellTimingArcSetIterator set_iter(drvr_cell); + while (set_iter.hasNext()) { + TimingArcSet *arc_set = set_iter.next(); + if (arc_set->from() == from_port + && arc_set->to() == to_port) { + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + if (arc->toTrans()->asRiseFall() == tr) { + float from_slew = from_slews[arc->fromTrans()->index()]; + findInputArcDelay(drvr_cell, drvr_pin, drvr_vertex, + arc, from_slew, dcalc_ap); + } + } + } + } +} + +// Driving cell delay is the load dependent delay, which is the gate +// delay minus the intrinsic delay. Driving cell delays are annotated +// to the wire arcs from the input port pin to the load pins. +void +GraphDelayCalc1::findInputArcDelay(LibertyCell *drvr_cell, + const Pin *drvr_pin, + Vertex *drvr_vertex, + TimingArc *arc, + float from_slew, + DcalcAnalysisPt *dcalc_ap) +{ + debugPrint5(debug_, "delay_calc", 3, " %s %s -> %s %s (%s)\n", + arc->from()->name(), + arc->fromTrans()->asString(), + arc->to()->name(), + arc->toTrans()->asString(), + arc->role()->asString()); + TransRiseFall *drvr_tr = arc->toTrans()->asRiseFall(); + if (drvr_tr) { + DcalcAPIndex ap_index = dcalc_ap->index(); + const Pvt *pvt = dcalc_ap->operatingConditions(); + Parasitic *drvr_parasitic = arc_delay_calc_->findParasitic(drvr_pin, drvr_tr, + dcalc_ap); + float load_cap = loadCap(drvr_pin, drvr_parasitic, drvr_tr, dcalc_ap); + + ArcDelay intrinsic_delay; + Slew intrinsic_slew; + arc_delay_calc_->gateDelay(drvr_cell, arc, Slew(from_slew), + 0.0, 0, 0.0, pvt, dcalc_ap, + intrinsic_delay, intrinsic_slew); + + // For input drivers there is no instance to find a related_output_pin. + ArcDelay gate_delay; + Slew gate_slew; + arc_delay_calc_->gateDelay(drvr_cell, arc, + Slew(from_slew), load_cap, + drvr_parasitic, 0.0, pvt, dcalc_ap, + gate_delay, gate_slew); + ArcDelay load_delay = gate_delay - intrinsic_delay; + debugPrint3(debug_, "delay_calc", 3, + " gate delay = %s intrinsic = %s slew = %s\n", + delayAsString(gate_delay, this), + delayAsString(intrinsic_delay, this), + delayAsString(gate_slew, this)); + graph_->setSlew(drvr_vertex, drvr_tr, ap_index, gate_slew); + annotateLoadDelays(drvr_vertex, drvr_tr, load_delay, false, dcalc_ap, + arc_delay_calc_); + } +} + +void +GraphDelayCalc1::findVertexDelay(Vertex *vertex, + ArcDelayCalc *arc_delay_calc, + bool propagate) +{ + const Pin *pin = vertex->pin(); + bool ideal_clks_changed = findIdealClks(vertex); + // Don't clobber root slews. + if (!vertex->isRoot()) { + debugPrint2(debug_, "delay_calc", 2, "find delays %s (%s)\n", + vertex->name(sdc_network_), + network_->cellName(network_->instance(pin))); + if (network_->isLeaf(pin)) { + if (vertex->isDriver(network_)) { + bool delay_changed = findDriverDelays(vertex, arc_delay_calc); + if (propagate) { + if (network_->direction(pin)->isInternal()) + enqueueTimingChecksEdges(vertex); + // Enqueue adjacent vertices even if the delays did not + // change when non-incremental to stride past annotations. + if (delay_changed || ideal_clks_changed || !incremental_) + iter_->enqueueAdjacentVertices(vertex); + } + } + else { + // Load vertex. + enqueueTimingChecksEdges(vertex); + // Enqueue driver vertices from this input load. + if (propagate) + iter_->enqueueAdjacentVertices(vertex); + } + } + // Bidirect port drivers are enqueued by their load vertex in + // annotateLoadDelays. + else if (vertex->isBidirectDriver() + && network_->isTopLevelPort(pin)) + seedRootSlew(vertex, arc_delay_calc); + } +} + +void +GraphDelayCalc1::enqueueTimingChecksEdges(Vertex *vertex) +{ + if (vertex->hasChecks() + || vertex->isCheckClk()) { + UniqueLock lock(check_vertices_lock_); + invalid_checks_.insert(vertex); + } +} + +bool +GraphDelayCalc1::findDriverDelays(Vertex *drvr_vertex, + ArcDelayCalc *arc_delay_calc) +{ + bool delay_changed = false; + MultiDrvrNet *multi_drvr = multiDrvrNet(drvr_vertex); + if (multi_drvr) { + Vertex *dcalc_drvr = multi_drvr->dcalcDrvr(); + if (drvr_vertex == dcalc_drvr) { + bool init_load_slews = true; + VertexSet::Iterator drvr_iter(multi_drvr->drvrs()); + while (drvr_iter.hasNext()) { + Vertex *drvr_vertex = drvr_iter.next(); + // Only init load slews once so previous driver dcalc results + // aren't clobbered. + delay_changed |= findDriverDelays1(drvr_vertex, init_load_slews, + multi_drvr, arc_delay_calc); + init_load_slews = false; + } + } + } + else + delay_changed = findDriverDelays1(drvr_vertex, true, nullptr, arc_delay_calc); + arc_delay_calc->finishDrvrPin(); + return delay_changed; +} + +bool +GraphDelayCalc1::findDriverDelays1(Vertex *drvr_vertex, + bool init_load_slews, + MultiDrvrNet *multi_drvr, + ArcDelayCalc *arc_delay_calc) +{ + const Pin *drvr_pin = drvr_vertex->pin(); + Instance *drvr_inst = network_->instance(drvr_pin); + LibertyCell *drvr_cell = network_->libertyCell(drvr_inst); + initSlew(drvr_vertex); + initWireDelays(drvr_vertex, init_load_slews); + bool delay_changed = 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)) + delay_changed |= findDriverEdgeDelays(drvr_cell, drvr_inst, drvr_pin, + drvr_vertex, multi_drvr, edge, + arc_delay_calc); + } + if (delay_changed && observer_) + observer_->delayChangedTo(drvr_vertex); + return delay_changed; +} + +// Init slews to zero on root vertices that are not inputs, such as +// floating input pins. +void +GraphDelayCalc1::initRootSlews(Vertex *vertex) +{ + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + DcalcAPIndex ap_index = dcalc_ap->index(); + for (auto tr : TransRiseFall::range()) { + if (!vertex->slewAnnotated(tr, slew_min_max)) + graph_->setSlew(vertex, tr, ap_index, default_slew); + } + } +} + +bool +GraphDelayCalc1::findDriverEdgeDelays(LibertyCell *drvr_cell, + Instance *drvr_inst, + const Pin *drvr_pin, + Vertex *drvr_vertex, + MultiDrvrNet *multi_drvr, + Edge *edge, + ArcDelayCalc *arc_delay_calc) +{ + Vertex *in_vertex = edge->from(graph_); + TimingArcSet *arc_set = edge->timingArcSet(); + const LibertyPort *related_out_port = arc_set->relatedOut(); + const Pin *related_out_pin = 0; + bool delay_changed = false; + if (related_out_port) + related_out_pin = network_->findPin(drvr_inst, related_out_port); + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + const Pvt *pvt = sdc_->pvt(drvr_inst, dcalc_ap->constraintMinMax()); + if (pvt == nullptr) + pvt = dcalc_ap->operatingConditions(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + const TransRiseFall *tr = arc->toTrans()->asRiseFall(); + Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, tr, + dcalc_ap); + float related_out_cap = 0.0; + if (related_out_pin) { + Parasitic *related_out_parasitic = + arc_delay_calc->findParasitic(related_out_pin, tr, dcalc_ap); + related_out_cap = loadCap(related_out_pin, + related_out_parasitic, + tr, dcalc_ap); + } + delay_changed |= findArcDelay(drvr_cell, drvr_pin, drvr_vertex, + multi_drvr, arc, parasitic, + related_out_cap, + in_vertex, edge, pvt, dcalc_ap, + arc_delay_calc); + } + } + + if (delay_changed && observer_) { + observer_->delayChangedFrom(in_vertex); + observer_->delayChangedFrom(drvr_vertex); + } + return delay_changed; +} + +float +GraphDelayCalc1::loadCap(const Pin *drvr_pin, + const DcalcAnalysisPt *dcalc_ap) const +{ + const MinMax *min_max = dcalc_ap->constraintMinMax(); + float load_cap = 0.0; + for (auto drvr_tr : TransRiseFall::range()) { + Parasitic *drvr_parasitic = arc_delay_calc_->findParasitic(drvr_pin, drvr_tr, + dcalc_ap); + float cap = loadCap(drvr_pin, nullptr, drvr_parasitic, drvr_tr, dcalc_ap); + if (min_max->compare(cap, load_cap)) + load_cap = cap; + } + return load_cap; +} + +float +GraphDelayCalc1::loadCap(const Pin *drvr_pin, + const TransRiseFall *drvr_tr, + const DcalcAnalysisPt *dcalc_ap) const +{ + Parasitic *drvr_parasitic = arc_delay_calc_->findParasitic(drvr_pin, drvr_tr, + dcalc_ap); + float cap = loadCap(drvr_pin, nullptr, drvr_parasitic, drvr_tr, dcalc_ap); + return cap; +} + +float +GraphDelayCalc1::loadCap(const Pin *drvr_pin, + Parasitic *drvr_parasitic, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap) const +{ + return loadCap(drvr_pin, nullptr, drvr_parasitic, tr, dcalc_ap); +} + +float +GraphDelayCalc1::loadCap(const Pin *drvr_pin, + MultiDrvrNet *multi_drvr, + Parasitic *drvr_parasitic, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap) const +{ + float pin_cap, wire_cap; + bool has_set_load; + float fanout; + if (multi_drvr) + multi_drvr->netCaps(tr, dcalc_ap, + pin_cap, wire_cap, fanout, has_set_load); + else + netCaps(drvr_pin, tr, dcalc_ap, + pin_cap, wire_cap, fanout, has_set_load); + loadCap(drvr_parasitic, has_set_load, pin_cap, wire_cap); + return wire_cap + pin_cap; +} + +void +GraphDelayCalc1::loadCap(const Pin *drvr_pin, + Parasitic *drvr_parasitic, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + float &pin_cap, + float &wire_cap) const +{ + bool has_set_load; + float fanout; + // Find pin and external pin/wire capacitance. + netCaps(drvr_pin, tr, dcalc_ap, + pin_cap, wire_cap, fanout, has_set_load); + loadCap(drvr_parasitic, has_set_load, pin_cap, wire_cap); +} + +void +GraphDelayCalc1::loadCap(Parasitic *drvr_parasitic, + bool has_set_load, + // Return values. + float &pin_cap, + float &wire_cap) const +{ + // set_load has precidence over parasitics. + if (!has_set_load && drvr_parasitic) { + if (parasitics_->isParasiticNetwork(drvr_parasitic)) + wire_cap += parasitics_->capacitance(drvr_parasitic); + else { + // PiModel includes both pin and external caps. + float cap = parasitics_->capacitance(drvr_parasitic); + if (pin_cap > cap) { + pin_cap = 0.0; + wire_cap = cap; + } + else + wire_cap = cap - pin_cap; + } + } +} + +void +GraphDelayCalc1::netCaps(const Pin *drvr_pin, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_set_load) const +{ + MultiDrvrNet *multi_drvr = 0; + if (graph_) { + Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin); + multi_drvr = multiDrvrNet(drvr_vertex); + } + if (multi_drvr) + multi_drvr->netCaps(tr, dcalc_ap, + pin_cap, wire_cap, fanout, has_set_load); + else { + const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); + const Corner *corner = dcalc_ap->corner(); + const MinMax *min_max = dcalc_ap->constraintMinMax(); + // Find pin and external pin/wire capacitance. + sdc_->connectedCap(drvr_pin, tr, op_cond, corner, min_max, + pin_cap, wire_cap, fanout, has_set_load); + } +} + +void +GraphDelayCalc1::initSlew(Vertex *vertex) +{ + for (auto tr : TransRiseFall::range()) { + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + if (!vertex->slewAnnotated(tr, slew_min_max)) { + DcalcAPIndex ap_index = dcalc_ap->index(); + graph_->setSlew(vertex, tr, ap_index, slew_min_max->initValue()); + } + } + } +} + +// Init wire delays and load slews. +void +GraphDelayCalc1::initWireDelays(Vertex *drvr_vertex, + bool init_load_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_); + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + const MinMax *delay_min_max = dcalc_ap->delayMinMax(); + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + Delay delay_init_value(delay_min_max->initValue()); + Slew slew_init_value(slew_min_max->initValue()); + DcalcAPIndex ap_index = dcalc_ap->index(); + for (auto tr : TransRiseFall::range()) { + if (!graph_->wireDelayAnnotated(wire_edge, tr, ap_index)) + graph_->setWireArcDelay(wire_edge, tr, ap_index, delay_init_value); + // Init load vertex slew. + if (init_load_slews + && !load_vertex->slewAnnotated(tr, slew_min_max)) + graph_->setSlew(load_vertex, tr, ap_index, slew_init_value); + } + } + } + } +} + +// Call the arc delay calculator to find the delay thru a single gate +// input to output timing arc, the wire delays from the gate output to +// each load pin, and the slew at each load pin. Annotate the graph +// with the results. +bool +GraphDelayCalc1::findArcDelay(LibertyCell *drvr_cell, + const Pin *drvr_pin, + Vertex *drvr_vertex, + MultiDrvrNet *multi_drvr, + TimingArc *arc, + Parasitic *drvr_parasitic, + float related_out_cap, + Vertex *from_vertex, + Edge *edge, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc) +{ + bool delay_changed = false; + TransRiseFall *from_tr = arc->fromTrans()->asRiseFall(); + TransRiseFall *drvr_tr = arc->toTrans()->asRiseFall(); + if (from_tr && drvr_tr) { + DcalcAPIndex ap_index = dcalc_ap->index(); + debugPrint6(debug_, "delay_calc", 3, + " %s %s -> %s %s (%s) %s\n", + arc->from()->name(), + arc->fromTrans()->asString(), + arc->to()->name(), + arc->toTrans()->asString(), + arc->role()->asString(), + dcalc_ap->corner()->name()); + // Delay calculation is done even when the gate delays/slews are + // annotated because the wire delays may not be annotated. + const Slew from_slew = edgeFromSlew(from_vertex, from_tr, edge, dcalc_ap); + ArcDelay gate_delay; + Slew gate_slew; + if (multi_drvr + && network_->direction(drvr_pin)->isOutput()) + multiDrvrGateDelay(multi_drvr, drvr_cell, drvr_pin, arc, + pvt, dcalc_ap, from_slew, drvr_parasitic, + related_out_cap, + arc_delay_calc, + gate_delay, gate_slew); + else { + float load_cap = loadCap(drvr_pin, multi_drvr, drvr_parasitic, + drvr_tr, dcalc_ap); + arc_delay_calc->gateDelay(drvr_cell, arc, + from_slew, load_cap, drvr_parasitic, + related_out_cap, pvt, dcalc_ap, + gate_delay, gate_slew); + } + debugPrint2(debug_, "delay_calc", 3, + " gate delay = %s slew = %s\n", + delayAsString(gate_delay, this), + delayAsString(gate_slew, this)); + // Merge slews. + const Slew &drvr_slew = graph_->slew(drvr_vertex, drvr_tr, ap_index); + const MinMax *slew_min_max = dcalc_ap->slewMinMax(); + if (fuzzyGreater(gate_slew, drvr_slew, dcalc_ap->slewMinMax()) + && !drvr_vertex->slewAnnotated(drvr_tr, slew_min_max)) + graph_->setSlew(drvr_vertex, drvr_tr, ap_index, gate_slew); + if (!graph_->arcDelayAnnotated(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 + > incremental_delay_tolerance_)) + delay_changed = true; + graph_->setArcDelay(edge, arc, ap_index, gate_delay); + } + annotateLoadDelays(drvr_vertex, drvr_tr, delay_zero, true, dcalc_ap, + arc_delay_calc); + } + return delay_changed; +} + +void +GraphDelayCalc1::multiDrvrGateDelay(MultiDrvrNet *multi_drvr, + LibertyCell *drvr_cell, + const Pin *drvr_pin, + TimingArc *arc, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + const Slew from_slew, + Parasitic *drvr_parasitic, + float related_out_cap, + ArcDelayCalc *arc_delay_calc, + // Return values. + ArcDelay &gate_delay, + Slew &gate_slew) +{ + ArcDelay intrinsic_delay; + Slew intrinsic_slew; + arc_delay_calc->gateDelay(drvr_cell, arc, from_slew, + 0.0, 0, 0.0, pvt, dcalc_ap, + intrinsic_delay, intrinsic_slew); + ArcDelay parallel_delay; + Slew parallel_slew; + const TransRiseFall *drvr_tr = arc->toTrans()->asRiseFall(); + multi_drvr->parallelDelaySlew(drvr_tr, dcalc_ap, arc_delay_calc, this, + parallel_delay, parallel_slew); + + gate_delay = parallel_delay + intrinsic_delay; + gate_slew = parallel_slew; + + float load_cap = loadCap(drvr_pin, multi_drvr, drvr_parasitic, + drvr_tr, dcalc_ap); + Delay gate_delay1; + Slew gate_slew1; + arc_delay_calc->gateDelay(drvr_cell, arc, + from_slew, load_cap, drvr_parasitic, + related_out_cap, pvt, dcalc_ap, + gate_delay1, gate_slew1); + float factor = delayRatio(gate_slew, gate_slew1); + arc_delay_calc->setMultiDrvrSlewFactor(factor); +} + +void +GraphDelayCalc1::findMultiDrvrGateDelay(MultiDrvrNet *multi_drvr, + const TransRiseFall *drvr_tr, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc, + // Return values. + ArcDelay ¶llel_delay, + Slew ¶llel_slew) +{ + ArcDelay delay_sum = 1.0; + Slew slew_sum = 1.0; + VertexSet::Iterator drvr_iter(multi_drvr->drvrs()); + while (drvr_iter.hasNext()) { + Vertex *drvr_vertex1 = drvr_iter.next(); + Pin *drvr_pin1 = drvr_vertex1->pin(); + Instance *drvr_inst1 = network_->instance(drvr_pin1); + LibertyCell *drvr_cell1 = network_->libertyCell(drvr_inst1); + if (network_->isDriver(drvr_pin1)) { + VertexInEdgeIterator edge_iter(drvr_vertex1, graph_); + while (edge_iter.hasNext()) { + Edge *edge1 = edge_iter.next(); + TimingArcSet *arc_set1 = edge1->timingArcSet(); + const LibertyPort *related_out_port = arc_set1->relatedOut(); + TimingArcSetArcIterator arc_iter(arc_set1); + while (arc_iter.hasNext()) { + TimingArc *arc1 = arc_iter.next(); + TransRiseFall *drvr_tr1 = arc1->toTrans()->asRiseFall(); + if (drvr_tr1 == drvr_tr) { + Vertex *from_vertex1 = edge1->from(graph_); + TransRiseFall *from_tr1 = arc1->fromTrans()->asRiseFall(); + Slew from_slew1 = edgeFromSlew(from_vertex1, from_tr1, edge1, dcalc_ap); + ArcDelay intrinsic_delay1; + Slew intrinsic_slew1; + arc_delay_calc->gateDelay(drvr_cell1, arc1, from_slew1, + 0.0, 0, 0.0, pvt, dcalc_ap, + intrinsic_delay1, intrinsic_slew1); + Parasitic *parasitic1 = + arc_delay_calc->findParasitic(drvr_pin1, drvr_tr1, dcalc_ap); + const Pin *related_out_pin1 = 0; + float related_out_cap1 = 0.0; + if (related_out_port) { + Instance *inst1 = network_->instance(drvr_pin1); + related_out_pin1 = network_->findPin(inst1, related_out_port); + if (related_out_pin1) { + Parasitic *related_out_parasitic1 = + arc_delay_calc->findParasitic(related_out_pin1, drvr_tr, + dcalc_ap); + related_out_cap1 = loadCap(related_out_pin1, + related_out_parasitic1, + drvr_tr, dcalc_ap); + } + } + float load_cap1 = loadCap(drvr_pin1, parasitic1, + drvr_tr, dcalc_ap); + ArcDelay gate_delay1; + Slew gate_slew1; + arc_delay_calc->gateDelay(drvr_cell1, arc1, + from_slew1, load_cap1, parasitic1, + related_out_cap1, pvt, dcalc_ap, + gate_delay1, gate_slew1); + delay_sum += 1.0F / (gate_delay1 - intrinsic_delay1); + slew_sum += 1.0F / gate_slew1; + } + } + } + } + } + parallel_delay = 1.0F / delay_sum; + parallel_slew = 1.0F / slew_sum; +} + +// Use clock slew for register/latch clk->q edges. +Slew +GraphDelayCalc1::edgeFromSlew(const Vertex *from_vertex, + const TransRiseFall *from_tr, + const Edge *edge, + const DcalcAnalysisPt *dcalc_ap) +{ + const TimingRole *role = edge->role(); + if (role->genericRole() == TimingRole::regClkToQ() + && isIdealClk(from_vertex)) + return idealClkSlew(from_vertex, from_tr, dcalc_ap->slewMinMax()); + else + return graph_->slew(from_vertex, from_tr, dcalc_ap->index()); +} + +Slew +GraphDelayCalc1::idealClkSlew(const Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max) +{ + float slew = min_max->initValue(); + const ClockSet *clks = idealClks(vertex); + ClockSet::ConstIterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + float clk_slew = clk->slew(tr, min_max); + if (min_max->compare(clk_slew, slew)) + slew = clk_slew; + } + return slew; +} + +// Annotate wire arc delays and load pin slews. +// extra_delay is additional wire delay to add to delay returned +// by the delay calculator. +void +GraphDelayCalc1::annotateLoadDelays(Vertex *drvr_vertex, + const TransRiseFall *drvr_tr, + const ArcDelay &extra_delay, + bool merge, + const DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc) +{ + 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(); + if (wire_edge->isWire()) { + Vertex *load_vertex = wire_edge->to(graph_); + Pin *load_pin = load_vertex->pin(); + ArcDelay wire_delay; + Slew load_slew; + arc_delay_calc->loadDelay(load_pin, wire_delay, load_slew); + debugPrint3(debug_, "delay_calc", 3, + " %s load delay = %s slew = %s\n", + load_vertex->name(sdc_network_), + delayAsString(wire_delay, this), + delayAsString(load_slew, this)); + if (!load_vertex->slewAnnotated(drvr_tr, slew_min_max)) { + if (drvr_vertex->slewAnnotated(drvr_tr, slew_min_max)) { + // Copy the driver slew to the load if it is annotated. + const Slew &drvr_slew = graph_->slew(drvr_vertex,drvr_tr,ap_index); + graph_->setSlew(load_vertex, drvr_tr, ap_index, drvr_slew); + } + else { + const Slew &slew = graph_->slew(load_vertex, drvr_tr, ap_index); + if (!merge + || fuzzyGreater(load_slew, slew, slew_min_max)) + graph_->setSlew(load_vertex, drvr_tr, ap_index, load_slew); + } + } + if (!graph_->wireDelayAnnotated(wire_edge, drvr_tr, 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_tr, + ap_index); + Delay wire_delay_extra = extra_delay + wire_delay; + const MinMax *delay_min_max = dcalc_ap->delayMinMax(); + if (!merge + || fuzzyGreater(wire_delay_extra, delay, delay_min_max)) { + graph_->setWireArcDelay(wire_edge, drvr_tr, ap_index, + wire_delay_extra); + if (observer_) + observer_->delayChangedTo(load_vertex); + } + } + // Enqueue bidirect driver from load vertex. + if (sdc_->bidirectDrvrSlewFromLoad(load_pin)) + iter_->enqueue(graph_->pinDrvrVertex(load_pin)); + } + } +} + +void +GraphDelayCalc1::findCheckDelays(Vertex *vertex, + ArcDelayCalc *arc_delay_calc) +{ + debugPrint2(debug_, "delay_calc", 2, "find checks %s (%s)\n", + vertex->name(sdc_network_), + network_->cellName(network_->instance(vertex->pin()))); + if (vertex->hasChecks()) { + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role()->isTimingCheck()) + findCheckEdgeDelays(edge, arc_delay_calc); + } + if (network_->isLatchData(vertex->pin())) { + // Latch D->Q arcs have to be re-evaled if level(D) > level(E) + // because levelization does not traverse D->Q arcs to break loops. + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (edge->role() == TimingRole::latchDtoQ()) + findVertexDelay(to_vertex, arc_delay_calc_, false); + } + } + } + if (vertex->isCheckClk()) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role()->isTimingCheck()) + findCheckEdgeDelays(edge, arc_delay_calc); + } + } +} + +void +GraphDelayCalc1::findCheckEdgeDelays(Edge *edge, + ArcDelayCalc *arc_delay_calc) +{ + Vertex *from_vertex = edge->from(graph_); + Vertex *to_vertex = edge->to(graph_); + TimingArcSet *arc_set = edge->timingArcSet(); + const Pin *to_pin = to_vertex->pin(); + Instance *inst = network_->instance(to_pin); + const LibertyCell *cell = network_->libertyCell(inst); + bool delay_changed = false; + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + TransRiseFall *from_tr = arc->fromTrans()->asRiseFall(); + TransRiseFall *to_tr = arc->toTrans()->asRiseFall(); + if (from_tr && to_tr) { + const LibertyPort *related_out_port = arc_set->relatedOut(); + const Pin *related_out_pin = 0; + if (related_out_port) + related_out_pin = network_->findPin(inst, related_out_port); + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + DcalcAPIndex ap_index = dcalc_ap->index(); + if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) { + const Pvt *pvt = sdc_->pvt(inst,dcalc_ap->constraintMinMax()); + if (pvt == nullptr) + pvt = dcalc_ap->operatingConditions(); + const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_tr, + dcalc_ap); + int slew_index = dcalc_ap->checkDataSlewIndex(); + const Slew &to_slew = graph_->slew(to_vertex, to_tr, slew_index); + debugPrint5(debug_, "delay_calc", 3, + " %s %s -> %s %s (%s)\n", + arc_set->from()->name(), + arc->fromTrans()->asString(), + arc_set->to()->name(), + arc->toTrans()->asString(), + arc_set->role()->asString()); + debugPrint2(debug_, "delay_calc", 3, + " from_slew = %s to_slew = %s\n", + delayAsString(from_slew, this), + delayAsString(to_slew, this)); + float related_out_cap = 0.0; + if (related_out_pin) { + Parasitic *related_out_parasitic = + arc_delay_calc->findParasitic(related_out_pin, to_tr, dcalc_ap); + related_out_cap = loadCap(related_out_pin, + related_out_parasitic, + to_tr, dcalc_ap); + } + ArcDelay check_delay; + arc_delay_calc->checkDelay(cell, arc, + from_slew, to_slew, + related_out_cap, + pvt, dcalc_ap, + check_delay); + debugPrint1(debug_, "delay_calc", 3, + " check_delay = %s\n", + delayAsString(check_delay, this)); + graph_->setArcDelay(edge, arc, ap_index, check_delay); + delay_changed = true; + } + } + } + } + + if (delay_changed && observer_) + observer_->checkDelayChangedTo(to_vertex); +} + +// Use clock slew for timing check clock edges. +Slew +GraphDelayCalc1::checkEdgeClkSlew(const Vertex *from_vertex, + const TransRiseFall *from_tr, + const DcalcAnalysisPt *dcalc_ap) +{ + if (isIdealClk(from_vertex)) + return idealClkSlew(from_vertex, from_tr, dcalc_ap->checkClkSlewMinMax()); + else + return graph_->slew(from_vertex, from_tr, dcalc_ap->checkClkSlewIndex()); +} + +//////////////////////////////////////////////////////////////// + +bool +GraphDelayCalc1::findIdealClks(Vertex *vertex) +{ + const Pin *pin = vertex->pin(); + ClockSet *ideal_clks = nullptr; + if (sdc_->isVertexPinClock(pin)) { + // Seed ideal clocks pins. + if (!sdc_->isPropagatedClock(pin)) { + ClockSet *clks = sdc_->findVertexPinClocks(pin); + ClockSet::ConstIterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + if (!clk->isPropagated()) { + if (ideal_clks == nullptr) { + ideal_clks = new ClockSet; + debugPrint1(debug_, "ideal_clks", 1, " %s\n", + vertex->name(sdc_network_)); + } + ideal_clks->insert(clk); + debugPrint1(debug_, "ideal_clks", 1, " %s\n", clk->name()); + } + } + } + } + else { + if (!sdc_->isPropagatedClock(pin)) { + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (clk_pred_->searchThru(edge)) { + Vertex *from_vertex = edge->from(graph_); + ClockSet *from_clks = idealClks(from_vertex); + if (from_clks) { + ClockSet::ConstIterator from_clk_iter(from_clks); + while (from_clk_iter.hasNext()) { + Clock *from_clk = from_clk_iter.next(); + if (ideal_clks == nullptr) { + ideal_clks = new ClockSet; + debugPrint1(debug_, "ideal_clks", 1, " %s\n", + vertex->name(sdc_network_)); + } + ideal_clks->insert(from_clk); + debugPrint1(debug_, "ideal_clks", 1, " %s\n", from_clk->name()); + } + } + } + } + } + } + return setIdealClks(vertex, ideal_clks); +} + +void +GraphDelayCalc1::clearIdealClkMap() +{ + ideal_clks_map_.deleteContentsClear(); + ideal_clks_map_next_.deleteContentsClear(); +} + +bool +GraphDelayCalc1::setIdealClks(const Vertex *vertex, + ClockSet *clks) +{ + bool changed = false; + ClockSet *clks1 = ideal_clks_map_.findKey(vertex); + if (!ClockSet::equal(clks, clks1)) { + // Only lock for updates to vertex ideal clks. + // Finding ideal clks by level means only changes at the current + // delay calc level are changed. + UniqueLock lock(ideal_clks_map_next_lock_); + ideal_clks_map_next_[vertex] = clks; + changed = true; + } + return changed; +} + +ClockSet * +GraphDelayCalc1::idealClks(const Vertex *vertex) +{ + return ideal_clks_map_.findKey(vertex); +} + +bool +GraphDelayCalc1::isIdealClk(const Vertex *vertex) +{ + const ClockSet *clks = idealClks(vertex); + return clks != 0 + && clks->size() > 0; +} + +float +GraphDelayCalc1::ceff(Edge *edge, + TimingArc *arc, + const DcalcAnalysisPt *dcalc_ap) +{ + Vertex *from_vertex = edge->from(graph_); + Vertex *to_vertex = edge->to(graph_); + Pin *to_pin = to_vertex->pin(); + Instance *inst = network_->instance(to_pin); + LibertyCell *cell = network_->libertyCell(inst); + TimingArcSet *arc_set = edge->timingArcSet(); + float ceff = 0.0; + const Pvt *pvt = sdc_->pvt(inst, dcalc_ap->constraintMinMax()); + if (pvt == nullptr) + pvt = dcalc_ap->operatingConditions(); + TransRiseFall *from_tr = arc->fromTrans()->asRiseFall(); + TransRiseFall *to_tr = arc->toTrans()->asRiseFall(); + if (from_tr && to_tr) { + const LibertyPort *related_out_port = arc_set->relatedOut(); + const Pin *related_out_pin = 0; + if (related_out_port) + related_out_pin = network_->findPin(inst, related_out_port); + float related_out_cap = 0.0; + if (related_out_pin) { + Parasitic *related_out_parasitic = + arc_delay_calc_->findParasitic(related_out_pin, to_tr, dcalc_ap); + related_out_cap = loadCap(related_out_pin, related_out_parasitic, + to_tr, dcalc_ap); + } + Parasitic *to_parasitic = arc_delay_calc_->findParasitic(to_pin, to_tr, + dcalc_ap); + const Slew &from_slew = edgeFromSlew(from_vertex, from_tr, edge, dcalc_ap); + float load_cap = loadCap(to_pin, to_parasitic, to_tr, dcalc_ap); + ceff = arc_delay_calc_->ceff(cell, arc, + from_slew, load_cap, to_parasitic, + related_out_cap, pvt, dcalc_ap); + arc_delay_calc_->finishDrvrPin(); + } + return ceff; +} + +//////////////////////////////////////////////////////////////// + +string * +GraphDelayCalc1::reportDelayCalc(Edge *edge, + TimingArc *arc, + const Corner *corner, + const MinMax *min_max, + int digits) +{ + Vertex *from_vertex = edge->from(graph_); + Vertex *to_vertex = edge->to(graph_); + Pin *to_pin = to_vertex->pin(); + TimingRole *role = arc->role(); + Instance *inst = network_->instance(to_pin); + LibertyCell *cell = network_->libertyCell(inst); + TimingArcSet *arc_set = edge->timingArcSet(); + string *result = new string; + DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); + const Pvt *pvt = sdc_->pvt(inst, dcalc_ap->constraintMinMax()); + if (pvt == nullptr) + pvt = dcalc_ap->operatingConditions(); + TransRiseFall *from_tr = arc->fromTrans()->asRiseFall(); + TransRiseFall *to_tr = arc->toTrans()->asRiseFall(); + if (from_tr && to_tr) { + const LibertyPort *related_out_port = arc_set->relatedOut(); + const Pin *related_out_pin = 0; + if (related_out_port) + related_out_pin = network_->findPin(inst, related_out_port); + float related_out_cap = 0.0; + if (related_out_pin) { + Parasitic *related_out_parasitic = + arc_delay_calc_->findParasitic(related_out_pin, to_tr, dcalc_ap); + related_out_cap = loadCap(related_out_pin, related_out_parasitic, + to_tr, dcalc_ap); + } + if (role->isTimingCheck()) { + const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_tr, dcalc_ap); + int slew_index = dcalc_ap->checkDataSlewIndex(); + const Slew &to_slew = graph_->slew(to_vertex, to_tr, slew_index); + bool from_ideal_clk = isIdealClk(from_vertex); + const char *from_slew_annotation = from_ideal_clk ? " (ideal clock)" : nullptr; + arc_delay_calc_->reportCheckDelay(cell, arc, from_slew, from_slew_annotation, + to_slew, related_out_cap, pvt, dcalc_ap, + digits, result); + } + else { + Parasitic *to_parasitic = + arc_delay_calc_->findParasitic(to_pin, to_tr, dcalc_ap); + const Slew &from_slew = edgeFromSlew(from_vertex, from_tr, edge, dcalc_ap); + float load_cap = loadCap(to_pin, to_parasitic, to_tr, dcalc_ap); + arc_delay_calc_->reportGateDelay(cell, arc, + from_slew, load_cap, to_parasitic, + related_out_cap, pvt, dcalc_ap, + digits, result); + } + arc_delay_calc_->finishDrvrPin(); + } + return result; +} + +} // namespace diff --git a/dcalc/GraphDelayCalc1.hh b/dcalc/GraphDelayCalc1.hh new file mode 100644 index 0000000..6a323bc --- /dev/null +++ b/dcalc/GraphDelayCalc1.hh @@ -0,0 +1,246 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_GRAPH_DELAY_CALC1_H +#define STA_GRAPH_DELAY_CALC1_H + +#include +#include "GraphDelayCalc.hh" + +namespace sta { + +class MultiDrvrNet; +class FindVertexDelays; +class Corner; + +typedef Map MultiDrvrNetMap; +typedef Map VertexIdealClksMap; + +// This class traverses the graph calling the arc delay calculator and +// annotating delays on graph edges. +class GraphDelayCalc1 : public GraphDelayCalc +{ +public: + GraphDelayCalc1(StaState *sta); + virtual ~GraphDelayCalc1(); + virtual void copyState(const StaState *sta); + virtual void delaysInvalid(); + virtual void delayInvalid(Vertex *vertex); + virtual void delayInvalid(const Pin *pin); + virtual void deleteVertexBefore(Vertex *vertex); + virtual void clear(); + virtual void findDelays(Level level); + virtual string *reportDelayCalc(Edge *edge, + TimingArc *arc, + const Corner *corner, + const MinMax *min_max, + int digits); + virtual float incrementalDelayTolerance(); + virtual void setIncrementalDelayTolerance(float tol); + virtual void setObserver(DelayCalcObserver *observer); + // Load pin_cap + wire_cap. + virtual float loadCap(const Pin *drvr_pin, + const TransRiseFall *drvr_tr, + const DcalcAnalysisPt *dcalc_ap) const; + virtual float loadCap(const Pin *drvr_pin, + const DcalcAnalysisPt *dcalc_ap) const; + virtual void loadCap(const Pin *drvr_pin, + Parasitic *drvr_parasitic, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + float &pin_cap, + float &wire_cap) const; + virtual float loadCap(const Pin *drvr_pin, + Parasitic *drvr_parasitic, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap) const; + virtual void netCaps(const Pin *drvr_pin, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_set_load) const; + float ceff(Edge *edge, + TimingArc *arc, + const DcalcAnalysisPt *dcalc_ap); + +protected: + void seedInvalidDelays(); + void ensureMultiDrvrNetsFound(); + void makeMultiDrvrNet(PinSet &drvr_pins); + void initSlew(Vertex *vertex); + void seedRootSlew(Vertex *vertex, + ArcDelayCalc *arc_delay_calc); + void seedRootSlews(); + void seedDrvrSlew(Vertex *vertex, + ArcDelayCalc *arc_delay_calc); + void seedNoDrvrSlew(Vertex *drvr_vertex, + const Pin *drvr_pin, + const TransRiseFall *tr, + DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc); + void seedNoDrvrCellSlew(Vertex *drvr_vertex, + const Pin *drvr_pin, + const TransRiseFall *tr, + InputDrive *drive, + DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc); + void seedLoadSlew(Vertex *vertex); + void setInputPortWireDelays(Vertex *vertex); + void findInputDriverDelay(LibertyCell *drvr_cell, + const Pin *drvr_pin, + Vertex *drvr_vertex, + const TransRiseFall *tr, + LibertyPort *from_port, + float *from_slews, + LibertyPort *to_port, + DcalcAnalysisPt *dcalc_ap); + LibertyPort *driveCellDefaultFromPort(LibertyCell *cell, + LibertyPort *to_port); + int findPortIndex(LibertyCell *cell, + LibertyPort *port); + void findInputArcDelay(LibertyCell *drvr_cell, + const Pin *drvr_pin, + Vertex *drvr_vertex, + TimingArc *arc, + float from_slew, + DcalcAnalysisPt *dcalc_ap); + bool findDriverDelays(Vertex *drvr_vertex, + ArcDelayCalc *arc_delay_calc); + bool findDriverDelays1(Vertex *drvr_vertex, + bool init_load_slews, + MultiDrvrNet *multi_drvr, + ArcDelayCalc *arc_delay_calc); + bool findDriverEdgeDelays(LibertyCell *drvr_cell, + Instance *drvr_inst, + const Pin *drvr_pin, + Vertex *drvr_vertex, + MultiDrvrNet *multi_drvr, + Edge *edge, + ArcDelayCalc *arc_delay_calc); + void initWireDelays(Vertex *drvr_vertex, + bool init_load_slews); + void initRootSlews(Vertex *vertex); + void findVertexDelay(Vertex *vertex, + ArcDelayCalc *arc_delay_calc, + bool propagate); + void enqueueTimingChecksEdges(Vertex *vertex); + bool findIdealClks(Vertex *vertex); + bool findArcDelay(LibertyCell *drvr_cell, + const Pin *drvr_pin, + Vertex *drvr_vertex, + MultiDrvrNet *multi_drvr, + TimingArc *arc, + Parasitic *drvr_parasitic, + float related_out_cap, + Vertex *from_vertex, + Edge *edge, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc); + void annotateLoadDelays(Vertex *drvr_vertex, + const TransRiseFall *drvr_tr, + const ArcDelay &extra_delay, + bool merge, + const DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc); + void findCheckDelays(Vertex *vertex, + ArcDelayCalc *arc_delay_calc); + void findCheckEdgeDelays(Edge *edge, + ArcDelayCalc *arc_delay_calc); + void findMultiDrvrGateDelay(MultiDrvrNet *multi_drvr, + const TransRiseFall *drvr_tr, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc, + // Return values. + ArcDelay ¶llel_delay, + Slew ¶llel_slew); + void multiDrvrGateDelay(MultiDrvrNet *multi_drvr, + LibertyCell *drvr_cell, + const Pin *drvr_pin, + TimingArc *arc, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + const Slew from_slew, + Parasitic *drvr_parasitic, + float related_out_cap, + ArcDelayCalc *arc_delay_calc, + // Return values. + ArcDelay &gate_delay, + Slew &gate_slew); + void deleteMultiDrvrNets(); + Slew edgeFromSlew(const Vertex *from_vertex, + const TransRiseFall *from_tr, + const Edge *edge, + const DcalcAnalysisPt *dcalc_ap); + Slew checkEdgeClkSlew(const Vertex *from_vertex, + const TransRiseFall *from_tr, + const DcalcAnalysisPt *dcalc_ap); + bool bidirectDrvrSlewFromLoad(const Vertex *vertex) const; + void clearIdealClkMap(); + bool setIdealClks(const Vertex *vertex, + ClockSet *clks); + ClockSet *idealClks(const Vertex *vertex); + bool isIdealClk(const Vertex *vertex); + Slew idealClkSlew(const Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max); + MultiDrvrNet *multiDrvrNet(const Vertex *drvr_vertex) const; + void loadCap(Parasitic *drvr_parasitic, + bool has_set_load, + // Return values. + float &pin_cap, + float &wire_cap) const; + float loadCap(const Pin *drvr_pin, + MultiDrvrNet *multi_drvr, + Parasitic *drvr_parasitic, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap) const; + void mergeIdealClks(); + + // Observer for edge delay changes. + DelayCalcObserver *observer_; + bool delays_seeded_; + bool incremental_; + bool delays_exist_; + // Vertices with invalid -to delays. + VertexSet invalid_delays_; + // Vertices with invalid -from/-to timing checks. + VertexSet invalid_checks_; + std::mutex check_vertices_lock_; + SearchPred *search_pred_; + SearchPred *search_non_latch_pred_; + SearchPred *clk_pred_; + BfsFwdIterator *iter_; + MultiDrvrNetMap multi_drvr_net_map_; + bool multi_drvr_nets_found_; + // Percentage (0.0:1.0) change in delay that causes downstream + // delays to be recomputed during incremental delay calculation. + float incremental_delay_tolerance_; + VertexIdealClksMap ideal_clks_map_; + VertexIdealClksMap ideal_clks_map_next_; + std::mutex ideal_clks_map_next_lock_; + + friend class FindVertexDelays; + friend class MultiDrvrNet; +}; + +} // namespace +#endif diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc new file mode 100644 index 0000000..73e258b --- /dev/null +++ b/dcalc/LumpedCapDelayCalc.cc @@ -0,0 +1,299 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Debug.hh" +#include "Units.hh" +#include "TimingArc.hh" +#include "TimingModel.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "Parasitics.hh" +#include "DcalcAnalysisPt.hh" +#include "GraphDelayCalc.hh" +#include "LumpedCapDelayCalc.hh" + +namespace sta { + +ArcDelayCalc * +makeLumpedCapDelayCalc(StaState *sta) +{ + return new LumpedCapDelayCalc(sta); +} + +LumpedCapDelayCalc::LumpedCapDelayCalc(StaState *sta) : + ArcDelayCalc(sta) +{ +} + +ArcDelayCalc * +LumpedCapDelayCalc::copy() +{ + return new LumpedCapDelayCalc(this); +} + +Parasitic * +LumpedCapDelayCalc::findParasitic(const Pin *drvr_pin, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap) +{ + // set_load has precidence over parasitics. + if (!sdc_->drvrPinHasWireCap(drvr_pin)) { + Parasitic *parasitic = nullptr; + const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + if (parasitics_->haveParasitics()) { + // Prefer PiElmore. + parasitic = parasitics_->findPiElmore(drvr_pin, tr,parasitic_ap); + if (parasitic) + return parasitic; + + Parasitic *parasitic_network = + parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + if (parasitic_network) { + parasitics_->reduceToPiElmore(parasitic_network, drvr_pin, + dcalc_ap->operatingConditions(), + dcalc_ap->corner(), + dcalc_ap->constraintMinMax(), + parasitic_ap); + parasitic = parasitics_->findPiElmore(drvr_pin, tr,parasitic_ap); + reduced_parasitic_drvrs_.push_back(drvr_pin); + return parasitic; + } + } + + const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); + Wireload *wireload = sdc_->wireloadDefaulted(cnst_min_max); + if (wireload) { + float pin_cap, wire_cap, fanout; + bool has_wire_cap; + graph_delay_calc_->netCaps(drvr_pin, tr, dcalc_ap, + pin_cap, wire_cap, fanout, has_wire_cap); + parasitic = parasitics_->estimatePiElmore(drvr_pin, tr, wireload, + fanout, pin_cap, + dcalc_ap->operatingConditions(), + dcalc_ap->corner(), + cnst_min_max, + parasitic_ap); + // Estimated parasitics are not recorded in the "database", so + // it for deletion after the drvr pin delay calc is finished. + unsaved_parasitics_.push_back(parasitic); + return parasitic; + } + } + return nullptr; +} + +void +LumpedCapDelayCalc::finishDrvrPin() +{ + for (auto parasitic : unsaved_parasitics_) + parasitics_->deleteUnsavedParasitic(parasitic); + unsaved_parasitics_.clear(); + for (auto drvr_pin : reduced_parasitic_drvrs_) + parasitics_->deleteDrvrReducedParasitics(drvr_pin); + reduced_parasitic_drvrs_.clear(); +} + +void +LumpedCapDelayCalc::inputPortDelay(const Pin *, float in_slew, + const TransRiseFall *tr, + Parasitic *, + const DcalcAnalysisPt *) +{ + drvr_slew_ = in_slew; + drvr_tr_ = tr; + drvr_library_ = network_->defaultLibertyLibrary(); + multi_drvr_slew_factor_ = 1.0F; +} + +float +LumpedCapDelayCalc::ceff(const LibertyCell *, + TimingArc *, + const Slew &, + float load_cap, + Parasitic *, + float, + const Pvt *, + const DcalcAnalysisPt *) +{ + return load_cap; +} + +void +LumpedCapDelayCalc::gateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) +{ + GateTimingModel *model = gateModel(arc, dcalc_ap); + debugPrint3(debug_, "delay_calc", 3, + " in_slew = %s load_cap = %s related_load_cap = %s lumped\n", + delayAsString(in_slew, this), + units()->capacitanceUnit()->asString(load_cap), + units()->capacitanceUnit()->asString(related_out_cap)); + if (model) { + ArcDelay gate_delay1; + Slew drvr_slew1; + float in_slew1 = delayAsFloat(in_slew); + model->gateDelay(drvr_cell, pvt, in_slew1, load_cap, related_out_cap, + pocv_enabled_, gate_delay1, drvr_slew1); + gate_delay = gate_delay1; + drvr_slew = drvr_slew1; + drvr_slew_ = drvr_slew1; + } + else { + gate_delay = delay_zero; + drvr_slew = delay_zero; + drvr_slew_ = 0.0; + } + drvr_tr_ = arc->toTrans()->asRiseFall(); + drvr_library_ = drvr_cell->libertyLibrary(); + multi_drvr_slew_factor_ = 1.0F; +} + +void +LumpedCapDelayCalc::loadDelay(const Pin *load_pin, + ArcDelay &wire_delay, + Slew &load_slew) +{ + Delay wire_delay1 = 0.0; + Slew load_slew1 = drvr_slew_ * multi_drvr_slew_factor_; + thresholdAdjust(load_pin, wire_delay1, load_slew1); + wire_delay = wire_delay1; + load_slew = load_slew1 * multi_drvr_slew_factor_; +} + +void +LumpedCapDelayCalc::thresholdAdjust(const Pin *load_pin, + ArcDelay &load_delay, + Slew &load_slew) +{ + LibertyLibrary *load_library = thresholdLibrary(load_pin); + if (load_library + && drvr_library_ + && load_library != drvr_library_) { + float drvr_vth = drvr_library_->outputThreshold(drvr_tr_); + float load_vth = load_library->inputThreshold(drvr_tr_); + float drvr_slew_delta = drvr_library_->slewUpperThreshold(drvr_tr_) + - drvr_library_->slewLowerThreshold(drvr_tr_); + float load_delay_delta = + delayAsFloat(load_slew) * ((load_vth - drvr_vth) / drvr_slew_delta); + load_delay += (drvr_tr_ == TransRiseFall::rise()) + ? load_delay_delta + : -load_delay_delta; + float load_slew_delta = load_library->slewUpperThreshold(drvr_tr_) + - load_library->slewLowerThreshold(drvr_tr_); + 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)); + } +} + +LibertyLibrary * +LumpedCapDelayCalc::thresholdLibrary(const Pin *load_pin) +{ + if (network_->isTopLevelPort(load_pin)) + // Input/output slews use the default (first read) library + // for slew thresholds. + return network_->defaultLibertyLibrary(); + else { + LibertyPort *lib_port = network_->libertyPort(load_pin); + if (lib_port) + return lib_port->libertyCell()->libertyLibrary(); + else + return network_->defaultLibertyLibrary(); + } +} + +void +LumpedCapDelayCalc::reportGateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + int digits, + string *result) +{ + GateTimingModel *model = gateModel(arc, dcalc_ap); + if (model) { + float in_slew1 = delayAsFloat(in_slew); + model->reportGateDelay(drvr_cell, pvt, in_slew1, load_cap, + related_out_cap, false, digits, result); + } +} + +void +LumpedCapDelayCalc::checkDelay(const LibertyCell *cell, + TimingArc *arc, + const Slew &from_slew, + const Slew &to_slew, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &margin) +{ + CheckTimingModel *model = checkModel(arc, dcalc_ap); + if (model) { + float from_slew1 = delayAsFloat(from_slew); + float to_slew1 = delayAsFloat(to_slew); + model->checkDelay(cell, pvt, from_slew1, to_slew1, related_out_cap, + pocv_enabled_, margin); + } + else + margin = delay_zero; +} + +void +LumpedCapDelayCalc::reportCheckDelay(const LibertyCell *cell, + TimingArc *arc, + const Slew &from_slew, + const char *from_slew_annotation, + const Slew &to_slew, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + int digits, + string *result) +{ + CheckTimingModel *model = checkModel(arc, dcalc_ap); + if (model) { + float from_slew1 = delayAsFloat(from_slew); + float to_slew1 = delayAsFloat(to_slew); + model->reportCheckDelay(cell, pvt, from_slew1, from_slew_annotation, to_slew1, + related_out_cap, false, digits, result); + } +} + +void +LumpedCapDelayCalc::setMultiDrvrSlewFactor(float factor) +{ + multi_drvr_slew_factor_ = factor; +} + +} // namespace diff --git a/dcalc/LumpedCapDelayCalc.hh b/dcalc/LumpedCapDelayCalc.hh new file mode 100644 index 0000000..d849877 --- /dev/null +++ b/dcalc/LumpedCapDelayCalc.hh @@ -0,0 +1,117 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_LUMPED_CAP_DELAY_CALC_H +#define STA_LUMPED_CAP_DELAY_CALC_H + +#include "ArcDelayCalc.hh" + +namespace sta { + +// Liberty table model lumped capacitance arc delay calculator. +// Wire delays are zero. +class LumpedCapDelayCalc : public ArcDelayCalc +{ +public: + LumpedCapDelayCalc(StaState *sta); + virtual ArcDelayCalc *copy(); + virtual Parasitic *findParasitic(const Pin *drvr_pin, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap); + virtual void inputPortDelay(const Pin *port_pin, + float in_slew, + const TransRiseFall *tr, + Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap); + virtual void gateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew); + virtual void setMultiDrvrSlewFactor(float factor); + virtual void loadDelay(const Pin *load_pin, + // Return values. + ArcDelay &wire_delay, + Slew &load_slew); + virtual void checkDelay(const LibertyCell *cell, + TimingArc *arc, + const Slew &from_slew, + const Slew &to_slew, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &margin); + virtual float ceff(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap); + virtual void reportGateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + int digits, + string *result); + virtual void reportCheckDelay(const LibertyCell *cell, + TimingArc *arc, + const Slew &from_slew, + const char *from_slew_annotation, + const Slew &to_slew, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + int digits, + string *result); + virtual void finishDrvrPin(); + +protected: + // Find the liberty library to use for logic/slew thresholds. + LibertyLibrary *thresholdLibrary(const Pin *load_pin); + // Adjust load_delay and load_slew from driver thresholds to load thresholds. + void thresholdAdjust(const Pin *load_pin, + ArcDelay &load_delay, + Slew &load_slew); + + Slew drvr_slew_; + float multi_drvr_slew_factor_; + const LibertyLibrary *drvr_library_; + const TransRiseFall *drvr_tr_; + // Parasitics returned by findParasitic that are reduced or estimated + // that can be deleted after delay calculation for the driver pin + // is finished. + Vector unsaved_parasitics_; + Vector reduced_parasitic_drvrs_; +}; + +ArcDelayCalc * +makeLumpedCapDelayCalc(StaState *sta); + +} // namespace +#endif diff --git a/dcalc/Makefile.am b/dcalc/Makefile.am new file mode 100644 index 0000000..58f161a --- /dev/null +++ b/dcalc/Makefile.am @@ -0,0 +1,62 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +lib_LTLIBRARIES = libdcalc.la + +include_HEADERS = \ + ArcDelayCalc.hh \ + Arnoldi.hh \ + ArnoldiDelayCalc.hh \ + ArnoldiReduce.hh \ + DelayCalc.hh \ + DcalcAnalysisPt.hh \ + DmpCeff.hh \ + DmpDelayCalc.hh \ + GraphDelayCalc.hh \ + GraphDelayCalc1.hh \ + LumpedCapDelayCalc.hh \ + NetCaps.hh \ + RCDelayCalc.hh \ + SimpleRCDelayCalc.hh \ + UnitDelayCalc.hh + +libdcalc_la_SOURCES = \ + ArcDelayCalc.cc \ + ArnoldiDelayCalc.cc \ + ArnoldiReduce.cc \ + DcalcAnalysisPt.cc \ + DelayCalc.cc \ + DmpCeff.cc \ + DmpDelayCalc.cc \ + GraphDelayCalc.cc \ + GraphDelayCalc1.cc \ + LumpedCapDelayCalc.cc \ + NetCaps.cc \ + RCDelayCalc.cc \ + SimpleRCDelayCalc.cc \ + UnitDelayCalc.cc + +TCL_SRCS = \ + DelayCalc.i \ + DelayCalc.tcl + +EXTRA_DIST = \ + $(TCL_SRCS) + +libs: $(lib_LTLIBRARIES) + +xtags: $(SOURCES) $(HEADERS) + etags -a -o ../TAGS $(SOURCES) $(HEADERS) $(TCL_SRCS) diff --git a/dcalc/NetCaps.cc b/dcalc/NetCaps.cc new file mode 100644 index 0000000..a105bb1 --- /dev/null +++ b/dcalc/NetCaps.cc @@ -0,0 +1,49 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "NetCaps.hh" + +namespace sta { + +NetCaps::NetCaps() +{ +} + +NetCaps::NetCaps(float pin_cap, + float wire_cap, + float fanout, + bool has_set_load) : + pin_cap_(pin_cap), + wire_cap_(wire_cap), + fanout_(fanout), + has_set_load_(has_set_load) +{ +} + +void +NetCaps::init(float pin_cap, + float wire_cap, + float fanout, + bool has_set_load) +{ + pin_cap_ = pin_cap; + wire_cap_ = wire_cap; + fanout_ = fanout; + has_set_load_ = has_set_load; +} + +} // namespace diff --git a/dcalc/NetCaps.hh b/dcalc/NetCaps.hh new file mode 100644 index 0000000..8760613 --- /dev/null +++ b/dcalc/NetCaps.hh @@ -0,0 +1,52 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_NET_CAPS_H +#define STA_NET_CAPS_H + +#include "DisallowCopyAssign.hh" + +namespace sta { + +// Constraints::pinNetCap return values. +class NetCaps +{ +public: + NetCaps(); + NetCaps(float pin_cap, + float wire_cap, + float fanout, + bool has_set_load); + void init(float pin_cap, + float wire_cap, + float fanout, + bool has_set_load); + float pinCap() const { return pin_cap_; } + float wireCap() const{ return wire_cap_; } + float fanout() const{ return fanout_; } + bool hasSetLoad() const { return has_set_load_; } + +private: + DISALLOW_COPY_AND_ASSIGN(NetCaps); + + float pin_cap_; + float wire_cap_; + float fanout_; + bool has_set_load_; +}; + +} // namespace +#endif diff --git a/dcalc/RCDelayCalc.cc b/dcalc/RCDelayCalc.cc new file mode 100644 index 0000000..94f4298 --- /dev/null +++ b/dcalc/RCDelayCalc.cc @@ -0,0 +1,79 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "Parasitics.hh" +#include "GraphDelayCalc.hh" +#include "RCDelayCalc.hh" + +namespace sta { + +RCDelayCalc::RCDelayCalc(StaState *sta) : + LumpedCapDelayCalc(sta) +{ +} + +ArcDelayCalc * +RCDelayCalc::copy() +{ + return new RCDelayCalc(this); +} + +void +RCDelayCalc::inputPortDelay(const Pin *, + float in_slew, + const TransRiseFall *tr, + Parasitic *parasitic, + const DcalcAnalysisPt *) +{ + drvr_parasitic_ = parasitic; + drvr_slew_ = in_slew; + drvr_tr_ = tr; + drvr_cell_ = nullptr; + drvr_library_ = network_->defaultLibertyLibrary(); + multi_drvr_slew_factor_ = 1.0F; +} + +// For DSPF on an input port the elmore delay is used as the time +// constant of an exponential waveform. The delay to the logic +// threshold and slew are computed for the exponential waveform. +// Note that this uses the driver thresholds and relies on +// thresholdAdjust to convert the delay and slew to the load's thresholds. +void +RCDelayCalc::dspfWireDelaySlew(const Pin *, + float elmore, + ArcDelay &wire_delay, + Slew &load_slew) +{ + float vth = .5; + float vl = .2; + float vh = .8; + float slew_derate = 1.0; + if (drvr_library_) { + vth = drvr_library_->inputThreshold(drvr_tr_); + vl = drvr_library_->slewLowerThreshold(drvr_tr_); + vh = drvr_library_->slewUpperThreshold(drvr_tr_); + slew_derate = drvr_library_->slewDerateFromLibrary(); + } + wire_delay = static_cast(-elmore * log(1.0 - vth)); + load_slew = (drvr_slew_ + elmore * log((1.0 - vl) / (1.0 - vh)) + / slew_derate) * multi_drvr_slew_factor_; +} + +} // namespace diff --git a/dcalc/RCDelayCalc.hh b/dcalc/RCDelayCalc.hh new file mode 100644 index 0000000..666ce45 --- /dev/null +++ b/dcalc/RCDelayCalc.hh @@ -0,0 +1,48 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_RC_DELAY_CALC_H +#define STA_RC_DELAY_CALC_H + +#include "LumpedCapDelayCalc.hh" + +namespace sta { + +// Base class for delay calculators with RC wire delay. +class RCDelayCalc : public LumpedCapDelayCalc +{ +public: + RCDelayCalc(StaState *sta); + virtual ArcDelayCalc *copy(); + virtual void inputPortDelay(const Pin *port_pin, + float in_slew, + const TransRiseFall *tr, + Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap); + +protected: + // Helper function for input ports driving dspf parasitic. + void dspfWireDelaySlew(const Pin *load_pin, + float elmore, + ArcDelay &wire_delay, + Slew &load_slew); + + const LibertyCell *drvr_cell_; + Parasitic *drvr_parasitic_; +}; + +} // namespace +#endif diff --git a/dcalc/SimpleRCDelayCalc.cc b/dcalc/SimpleRCDelayCalc.cc new file mode 100644 index 0000000..94e6efc --- /dev/null +++ b/dcalc/SimpleRCDelayCalc.cc @@ -0,0 +1,113 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "Parasitics.hh" +#include "DcalcAnalysisPt.hh" +#include "SimpleRCDelayCalc.hh" + +namespace sta { + +ArcDelayCalc * +makeSimpleRCDelayCalc(StaState *sta) +{ + return new SimpleRCDelayCalc(sta); +} + +SimpleRCDelayCalc::SimpleRCDelayCalc(StaState *sta) : + RCDelayCalc(sta) +{ +} + +ArcDelayCalc * +SimpleRCDelayCalc::copy() +{ + return new SimpleRCDelayCalc(this); +} + +void +SimpleRCDelayCalc::inputPortDelay(const Pin *port_pin, + float in_slew, + const TransRiseFall *tr, + Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap) +{ + pvt_ = dcalc_ap->operatingConditions(); + RCDelayCalc::inputPortDelay(port_pin, in_slew, tr, parasitic, dcalc_ap); +} + +void +SimpleRCDelayCalc::gateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) +{ + drvr_parasitic_ = drvr_parasitic; + drvr_tr_ = arc->toTrans()->asRiseFall(); + drvr_cell_ = drvr_cell; + drvr_library_ = drvr_cell->libertyLibrary(); + pvt_ = pvt; + LumpedCapDelayCalc::gateDelay(drvr_cell, arc, in_slew, + load_cap, drvr_parasitic, related_out_cap, + pvt, dcalc_ap, + gate_delay, drvr_slew); +} + +void +SimpleRCDelayCalc::loadDelay(const Pin *load_pin, + ArcDelay &wire_delay, + Slew &load_slew) +{ + ArcDelay wire_delay1 = 0.0; + Slew load_slew1 = drvr_slew_; + bool elmore_exists = false; + float elmore = 0.0; + if (drvr_parasitic_) + parasitics_->findElmore(drvr_parasitic_, load_pin, elmore, elmore_exists); + if (elmore_exists) { + if (drvr_library_ && drvr_library_->wireSlewDegradationTable(drvr_tr_)) { + wire_delay1 = elmore; + load_slew1 = drvr_library_->degradeWireSlew(drvr_cell_, drvr_tr_, + pvt_, + delayAsFloat(drvr_slew_), + delayAsFloat(wire_delay1)); + } + else if (parasitics_->isReducedParasiticNetwork(drvr_parasitic_)) + dspfWireDelaySlew(load_pin, elmore, wire_delay1, load_slew1); + else { + // For RSPF on an input port the elmore delay is used for the + // wire delay and the slew is copied from the driver. + wire_delay1 = elmore; + load_slew1 = drvr_slew_; + } + } + thresholdAdjust(load_pin, wire_delay1, load_slew1); + wire_delay = wire_delay1; + load_slew = load_slew1 * multi_drvr_slew_factor_; +} + +} // namespace diff --git a/dcalc/SimpleRCDelayCalc.hh b/dcalc/SimpleRCDelayCalc.hh new file mode 100644 index 0000000..4e74482 --- /dev/null +++ b/dcalc/SimpleRCDelayCalc.hh @@ -0,0 +1,65 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SIMPLE_RC_DELAY_CALC_H +#define STA_SIMPLE_RC_DELAY_CALC_H + +#include "RCDelayCalc.hh" + +namespace sta { + +// Liberty table model lumped capacitance arc delay calculator. +// Effective capacitance is the pi model total capacitance (C1+C2). +// Wire delays are elmore delays. +// Driver slews are degraded to loads by rise/fall transition_degradation +// tables. +class SimpleRCDelayCalc : public RCDelayCalc +{ +public: + SimpleRCDelayCalc(StaState *sta); + virtual ArcDelayCalc *copy(); + virtual void inputPortDelay(const Pin *port_pin, + float in_slew, + const TransRiseFall *tr, + Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap); + virtual void gateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew); + virtual void loadDelay(const Pin *load_pin, + ArcDelay &wire_delay, + Slew &load_slew); + + using RCDelayCalc::gateDelay; + using RCDelayCalc::reportGateDelay; + +private: + const Pvt *pvt_; +}; + +ArcDelayCalc * +makeSimpleRCDelayCalc(StaState *sta); + +} // namespace +#endif diff --git a/dcalc/UnitDelayCalc.cc b/dcalc/UnitDelayCalc.cc new file mode 100644 index 0000000..e8b56a0 --- /dev/null +++ b/dcalc/UnitDelayCalc.cc @@ -0,0 +1,144 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Units.hh" +#include "UnitDelayCalc.hh" + +namespace sta { + +ArcDelayCalc * +makeUnitDelayCalc(StaState *sta) +{ + return new UnitDelayCalc(sta); +} + +UnitDelayCalc::UnitDelayCalc(StaState *sta) : + ArcDelayCalc(sta) +{ +} + +ArcDelayCalc * +UnitDelayCalc::copy() +{ + return new UnitDelayCalc(this); +} + +Parasitic * +UnitDelayCalc::findParasitic(const Pin *, + const TransRiseFall *, + const DcalcAnalysisPt *) +{ + return nullptr; +} + +void +UnitDelayCalc::inputPortDelay(const Pin *, + float, + const TransRiseFall *, + Parasitic *, + const DcalcAnalysisPt *) +{ +} + +void +UnitDelayCalc::gateDelay(const LibertyCell *, + TimingArc *, + const Slew &, + float, + Parasitic *, + float, + const Pvt *, const DcalcAnalysisPt *, + // Return values. + ArcDelay &gate_delay, Slew &drvr_slew) +{ + gate_delay = units_->timeUnit()->scale(); + drvr_slew = 0.0; +} + +void +UnitDelayCalc::loadDelay(const Pin *, + ArcDelay &wire_delay, + Slew &load_slew) +{ + wire_delay = 0.0; + load_slew = 0.0; +} + +float +UnitDelayCalc::ceff(const LibertyCell *, + TimingArc *, + const Slew &, + float, + Parasitic *, + float, + const Pvt *, + const DcalcAnalysisPt *) +{ + return 0.0; +} + +void +UnitDelayCalc::reportGateDelay(const LibertyCell *, + TimingArc *, + const Slew &, + float, + Parasitic *, + float, + const Pvt *, + const DcalcAnalysisPt *, + int, + string *result) +{ + *result += "Delay = 1.0\n"; + *result += "Slew = 0.0\n"; +} + +void +UnitDelayCalc::checkDelay(const LibertyCell *, + TimingArc *, + const Slew &, + const Slew &, + float, + const Pvt *, + const DcalcAnalysisPt *, + // Return values. + ArcDelay &margin) +{ + margin = units_->timeUnit()->scale(); +} + +void +UnitDelayCalc::reportCheckDelay(const LibertyCell *, + TimingArc *, + const Slew &, + const char *, + const Slew &, + float, + const Pvt *, + const DcalcAnalysisPt *, + int, + string *result) +{ + *result += "Check = 1.0\n"; +} + +void +UnitDelayCalc::finishDrvrPin() +{ +} + +} // namespace diff --git a/dcalc/UnitDelayCalc.hh b/dcalc/UnitDelayCalc.hh new file mode 100644 index 0000000..e3721aa --- /dev/null +++ b/dcalc/UnitDelayCalc.hh @@ -0,0 +1,98 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_UNIT_DELAY_CALC_H +#define STA_UNIT_DELAY_CALC_H + +#include "ArcDelayCalc.hh" + +namespace sta { + +// Unit delay calculator. +class UnitDelayCalc : public ArcDelayCalc +{ +public: + UnitDelayCalc(StaState *sta); + virtual ArcDelayCalc *copy(); + virtual Parasitic *findParasitic(const Pin *drvr_pin, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap); + virtual void gateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew); + virtual void loadDelay(const Pin *load_pin, + // Return values. + ArcDelay &wire_delay, + Slew &load_slew); + virtual void setMultiDrvrSlewFactor(float) {} + virtual float ceff(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap); + virtual void inputPortDelay(const Pin *port_pin, + float in_slew, + const TransRiseFall *tr, + Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap); + virtual void checkDelay(const LibertyCell *cell, + TimingArc *arc, + const Slew &from_slew, + const Slew &to_slew, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &margin); + virtual void reportGateDelay(const LibertyCell *drvr_cell, + TimingArc *arc, + const Slew &in_slew, + float load_cap, + Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + int digits, + string *result); + virtual void reportCheckDelay(const LibertyCell *cell, + TimingArc *arc, + const Slew &from_slew, + const char *from_slew_annotation, + const Slew &to_slew, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + int digits, + string *result); + virtual void finishDrvrPin(); +}; + +ArcDelayCalc * +makeUnitDelayCalc(StaState *sta); + +} // namespace +#endif diff --git a/doc/ApiChanges.txt b/doc/ApiChanges.txt new file mode 100644 index 0000000..81bbb00 --- /dev/null +++ b/doc/ApiChanges.txt @@ -0,0 +1,26 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2018, Parallax Software, Inc. +# +# This 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 . + +This file summarizes STA API changes for each release. + +Release 2.0.0 2018/06/11 +------------------------- + +Initial release. + +# Local Variables: +# mode:text +# End: diff --git a/doc/BugLog b/doc/BugLog new file mode 100644 index 0000000..8aed096 --- /dev/null +++ b/doc/BugLog @@ -0,0 +1,19 @@ +Release 2.0 Patches +------------------- +2018/10/23 spash msg embedded quotes seg fault +2018/10/23 read_verilog mod inst with no ports seg fault +2018/11/08 corners > 2 causes internal error +2018/11/09 Verilog ignore attributes (* blah *) +2018/12/24 all_fanout from input port +2018/12/25 liberty pg_types +2019/01/03 liberty 2D bus names +2019/01/07 WritePathSpice don't barf on spice subckts missing liberty cells +2019/01/15 generated clk -divide_by 16384 cycle accting +2019/01/18 write_path_spice ground coupling caps +2019/01/18 write_path_spice do not write zero caps +2019/01/19 write_path_spice tie-offs for demorgan'd nand/nor functs +2019/01/29 write_sdc set_driving_cell omit -library if missing from sdc +2019/01/31 generated clk -divide_by 16384 cycle accting fallout +2019/02/17 report_power internal power accuracy +2019/02/18 write_path_spice first line is comment +2019/02/21 write_path_spice include side load pins diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt new file mode 100644 index 0000000..eb0a14a --- /dev/null +++ b/doc/ChangeLog.txt @@ -0,0 +1,142 @@ +OpenSTA Timing Analyzer Release Notes +--------------------------------------------- + +This file summarizes user visible changes for each release. + +Release 2.0.0 2018/09/28 +------------------------- + +The command line options have changed to the following: + + -help show help and exit + -version show version and exit + -no_init do not read .sta init file + -threads count|max use count threads + -no_splash do not show the license splash at startup + -exit exit after reading cmd_file + cmd_file source cmd_file + + +.... + +Builds using Autotools/configure are no longer supported. +Use CMake as documented in README.md. + +.... + +The check_timing command -no_output_delay checks output ports for +set_output_delay. + +.... + +The report_power command reports the power consumption of the design +or a specific instance. + + report_power [-instances inst] [-digits digits] [> filename] [>> filename] + +The internal, switching, leakage and total power are reported. Design +power is reported separately for combinational, sequential, macro and +pad groups. + +Use -instances to report power for a specific instance. + +Use the set_power_activity command to specify activity/duty +globally using -global, the input port default using -input, +or for input ports using -input_ports, or pins using -pins. + + set_power_activity [-global] + [-input] + [-input_ports ports] + [-pins pins] + [-activiity activity] + [-duty duty] + +.... + +The write_path_spice command writes a spice netlist for a timing path. + + write_path_spice -path_args path_args + -spice_directory spice_directory + -subckt_file subckt_file + -lib_subckt_file lib_subckts_file\ + -model_file model_file + -power power + -ground ground + +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_.sp and subckt file is +path_.subckt. + +Spice netlists for liberty library cells are read from +lib_subckts_file. 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 .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. + +.... + +The report_checks and report_check_types commands now support an +-unconstrained flag. + + report_checks -unconstrained + report_check_types -unconstrained + +The sta_report_unconstrained_paths variable will be supported for +for compatibility in the current release. + +.... + +The transition_time path reporting field has been renamed to slew. + + report_checks -fields {slew} + report_check_types -fields {slew} + +... + +The read_parasitics command has been renamed read_spef and no longer +supports the SPF format. + +.... + +The make_instance command now takes a single instance name argument +and returns the instance. + + make_instance instance_name + +The make_net command now takes a single net name argument and returns +the net. + + make_net net_name + +The delete_instance command deletes a single instance instead of a list +of instances. + + delete_instance instance + +The delete_net command deletes a single net instead of a list +of nets. + + delete_net net + +The disconnect_pins command is renamed disconnect_pin and disconnects a +single pin. + + disconnect_pin net pin + +The report_tns and report_wns commands print the value returned by +total_negative_slack and worst_negative_slack respectively. + + report_tns + report_wns + report_worst_slack + +The set_clock_sense command was deprecated by SDC 2.1. +Use set_sense -type clock instead. + +# Local Variables: +# mode:text +# End: diff --git a/doc/CodingGuidelines.txt b/doc/CodingGuidelines.txt new file mode 100644 index 0000000..80ea0ee --- /dev/null +++ b/doc/CodingGuidelines.txt @@ -0,0 +1,156 @@ +Naming conventions + +directory - lowercase (directory) +filename - corresponding class name without prefix (Filename) +class - capitalized (ClassName) +member function - lowercase/capitalized (memberFunction) +member variable - lowercase/underscore/trailing underscore (member_variable_) + Trailing underscore prevents conflict with accessor + member function name. +function - lowercase/capitalized (functionName) +comments - use capitalized sentences that end with periods + +C++ code files should use a .cc file extension +C++ header files should use a .hh file extension + +Use ifdef/define's to protect headers from being read more than once. +Name the define variable the same as the header in uppercase. +For example, for Clock.hh + + #ifndef STA_CLOCK_H + #define STA_CLOCK_H + ... + #endif + +In general it is better to for class variables to use pointers to +objects of other classes rather than embedding the instance directly. +This only requires that the class be declared rather than defined, +many times breaking a dependency on another header file. + +Header files that define the classes of a sub-directory allow other +headers to have pointers to the objects without pulling in the details +of the class definitions. These headers are named "DirectoryClass.hh" +where Directory is the capitalized name of the sub-directory. + +Place comments describing public functions and classes in header files +rather than code files because a consumer is more likely to have +access to the header and that is the first place they will look. +Comments for private functions can be in the source file. + +The return type of a function should be on the line before the +function name. Spaces should be added after commas in the argument +list. Split the function arguments to fit on one line. For example: + +return_type +function(type1 arg1, type2, arg2) +{ +} + +Functions should be less than one screen long. Break long functions +up into smaller ones. Lines should be less than 80 characters long. + +Try to avoid assignments inside `if'-conditions. For example, don't +write this: + + if ((foo = (char *) malloc (sizeof *foo)) == 0) + fatal ("virtual memory exhausted"); + +instead, write this: + + foo = (char *) malloc (sizeof *foo); + if (foo == 0) + fatal ("virtual memory exhausted"); + + +Use braces around if/for bodies that are more than one line. +IE, + if (pred) + for (int i = 0; i < len; i++) { // this body should be in {}'s + ... + } + +Add a default clause to all switches calling switchCaseNotHandled: + + switch (type) { + case edge_interconnect: + ... + default: + switchCaseNotHandled(); + } + +Put return types for functions on the line before the function name: + + Cell * + Library::findCell(char *name) + { + ... + } + +Class member functions should be grouped in public, protected and then +private order. + + class Frob + { + public: + protected: + private: + + friend class Frobulator; + } + +Don't declare class variables as const. It means any downstream code +that accesses the member cannot modify it, which is overly +restrictive. + +Never use [] to lookup a map value because it creates a key/null value +pair if the lookup fails. Use sta::Map::findKey instead. + +Avoid nested classes/enums because SWIG has trouble with them. + +................................................................ +Warning +get_ not found +sdf timing arc not found +disabling timing arcs to break loops +virtual clock with no sources (no pins) +invalid endpoint for constrained paths +sdf DESIGN does not match top level cell name +set_input_delay on clk port (deprecation warning) +link cannot resolve reference (module/cell not found) + +Errors +cannot open file +file syntax error +cmd illegal command option combinations +cmd extra positional args +cmd unknown keyword option +cmd unknown +sdf pin not found + +................................................................ + +if configure.ac changes + autoconf + +if Makefile.am changes + automake + +Adding a new source file + Add header and source to source_dir/Makefile.am + cd source_dir; make clean + +Adding a new source directory + Add to configure.ac STA_SUBDIRS, AC_CONFIG_FILES + bootstrap + configure + make + +................................................................ +Swig notes + +C null pointers (zero) turn into "NULL" values in TCL. + +TCL "NULL" strings turn into NULL (zero) pointers in C. + +# TCL lexpr-funcall +eval exec $prog $args diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..87b7ce7 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,27 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +EXTRA_DIST = \ + ApiChanges.txt \ + BugLog \ + CodingGuidelines.txt \ + OpenSTA.odt \ + OpenSTA.pdf \ + StaApi.txt + +libs: + +xtags: diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt new file mode 100644 index 0000000..80786bd Binary files /dev/null and b/doc/OpenSTA.odt differ diff --git a/doc/OpenSTA.pdf b/doc/OpenSTA.pdf new file mode 100644 index 0000000..69304a8 Binary files /dev/null and b/doc/OpenSTA.pdf differ diff --git a/doc/StaApi.txt b/doc/StaApi.txt new file mode 100644 index 0000000..b699415 --- /dev/null +++ b/doc/StaApi.txt @@ -0,0 +1,593 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 STA is built in C++ with heavy use of STL (Standard Template +Libraries). It also uses the zlib library to read compressed Verilog, +SDF, SPF, and SPEF files. + +The sub-directories of the STA code are: + +doc + Documentation files. +util + Basic utilities. +liberty + Liberty timing library classes and file reader. +network + Network and library API used by all STA code. +verilog + Verilog netlist reader that implements the network API. +graph + Timing graph built from network and library cell timing arcs. +sdc + SDC timing constraint classes. +sdf + SDF reader, writer and annotator. +dcalc + Delay calculator API and implementations. +search + Search engine used to annotate the graph with arrival, required times + and find timing check slacks. +parasitics + Parasitics API, Spef and Spf readers. +app + Interface between Tcl and STA (built with SWIG). + Main program definition. +tcl + User interface code. + SDC (Synopsys Design Constraint) argument parsing. + +Each sub-directory builds a library that is linked to build the STA +executable or linked into another application. + +The file doc/CodingGuidelines.txt defines naming conventions used in +the code. + +STA API +------- + +Major components of the STA such as the network, timing graph, sdc, +and search are implemented as separate classes. The Sta class +contains an instance of each of these components. + +The Sta class defines the bulk of the externally visible API used by +the Tcl interface, and coordinates operations that involve multiple +components. For example, when a false path command is entered into +the Tcl command interpreter, the Sta passes the declaration on to the +Sdc component and tells the Search component to invalidate all arrival +and required times. + +Applications should call functions defined by the Sta class rather +than functions defined by the components. Calling functions defined +by the components will get you in trouble unless you understand them +in detail. For example, telling the delay calculator to recompute the +delays leaves the arrival times that depend on them wrong. Always +remember that the Sta coordinates the components. + +In general, objects passed as arguments to Sta functions that are +constructors become "owned" by the STA and should not be deleted by +the caller. For example, a set of pins passed into +Sta::makeExceptionFrom are used in the resulting object (rather than +copied into another set). On the other hand, strings passed as +arguments are copied by the Sta functions before they are retained in +STA data structures. + +In many cases the major components contain pointers to other +components. The StaState class is a simple container for these +components that makes initialization of pointers to the components +easier. + +An STA with modified behavior can be built by defining classes derived +from the component classes and overloading some of the member +functions (which may have to be modified to be virtual). Components +are created by Sta::makeComponents(). The Sta::makeComponents() +function in turn calls each of the Sta::make component +constructors. These constructors can be overloaded by redefining them +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 +----- + +Units for values in Sta and liberty data structures are always the +following: + + time seconds + length meters + capacitance farads + resistance ohms + +All file readers and the user interface are responsible for converting +any user input or output to these units. + +Utilities +--------- + +The most significant utilities are the Vector, Map and Set templated +classes built on top the respective STL classes. The main point of +these classes is to provide Java-like iterators that can be passed +around as one object. STL iterators require the container to be +useful. Iterators uniformly use the hasNext() function to test to see +if there is another member and next() to access the next iteration +member. + +Most printing is done in Tcl rather than the STA C++ code. Some +errors and warnings are reported by the STA in C++ + +All printing done by the STA core is done using the Report class API. +The report class supports output redirection to a file and logging to +a file. The Tcl interpreter prints to "channels" that are +encapsulated by functions in the the ReportTcl class. Printing inside +the STA is directed to the Tcl channels so that it appears with the +Tcl interpreter output. + +Network +------- + +The network API is the key to making the STA a timing engine that can +be bolted onto another application. This API allows the STA to +efficiently communicate with external network data structures without +the overhead of making and maintaining a copying of it. + +The network API encapsulates both library and netlist accessors. +Libraries are composed of cells that have ports the define connections +to the cell. Netlists are built out of cell instances, pins and nets. + +The ConcreteLibrary and ConcreteNetwork classes are used by the STA +netlist readers. These class definitions are to support a stand alone +STA that does not depend on external netlist data structures. + +External network data structures are interfaced to the STA by casting +pointers to network object across the interface. The external objects +do not have to be derived from STA network base classes. The network +API functions are typically very thin functions that cast the STA +network types to the external class types and call the corresponding +external network database accessor. + +Bus ports are expanded into ports for each bit in the bus, and +iterators are provided for the expanded and unexpanded set of cell +ports. + +Network instances are calls of cells in the design hierarchy. Both +hierarchcial and leaf instances are in the network. Hierarchical +instances have children instances at the next lower hierarchy level. +Leaf instances have liberty cells with timing model data. At the top +of the hierarchy is a top level instance that has instances for the +top level netlist. If a cell has multiple instances the entire +sub-tree of hierarchy is repeated in the network. This "unfolded" +network representation allows optimization to specialize instances of +a hierarchical block. A "folded" network representation that has only +one sub-tree for each hierarchical block means that all copies must +have identical sub-trees, preventing optimations that specialize the +contents. + +Pins are a connection between an instance and a net corresponding to a +port. For bus ports each bit in the bus has a corresponding pin +(library iterators can be used to find the pins that correspond to all +of the bits in a bus). Ports on the top level instance also have pins +in the network that are the top level inputs and outputs. + +Nets connect together a group of pins. Both hierarchical and leaf +pins are on a net. Nets can connect pins on multiple levels of +hierarchy. + +The network objects inside the STA are always pointers to instances of +undefined class objects. The implementation and definition of the +network objects themselves is never visible inside the STA. The +network API is implemented as an adapter that performs all operations +on all network objects. There is one network adapter instance used by +all STA code. For example, to find the cell of an instance + + Cell *cell = network->cell(instance); + +The network adapter returns iterators for looping over groups of +network objects. For example, the following code iterates over the +children of the top level instance. + + Instance *top_instance = network->topInstance(); + InstanceChildIterator *child_iter = network->childIterator(top_instance); + while (child_iter->hasNext()) { + Instance *child = child_iter->next(); + ... + } + delete child_iter; + +An adapter to a network database is built by defining a class derived +from the base class Network, or NetworkEdit if it supports incremental +editing operations. network/ConcreteNetwork.cc and oa/OaNetwork.cc +are sample network adapters. + +A network adaptor to interface to an external network database must +define the virtual functions of the Network class (about 45 +functions). The external network objects do not have to use any STA +network objects as base classes or even be C++ objects. These network +adapter functions should cast the network object pointers to the +underlying network object. + +Network adapters built on the Network class must define the following +functions to find corresponding liberty objects. + + virtual LibertyLibrary *libertyLibrary(Library *library) const; + virtual LibertyLibrary *makeLibertyLibrary(const char *name, + LibraryAnalysisPt *ap); + virtual LibertyCell *libertyCell(Cell *cell) const; + virtual LibertyPort *libertyPort(Port *port) const; + +The NetworkLiberty class provides implementations of the first two +functions for derived network classes. The OaNetwork class shows an +implentation that uses the NetworkLiberty class. + +If the network adapter implements the NetworkEdit API the following +TCL commands are supported: + + make_cell + replace_cell + delete_cell + make_net + delete_net + connect_pins + disconnect_pins + +Each of these commands call correponding functions in app/StaTcl.i +that notify the Sta before and/or after the network operation is +performed. + +Liberty +------- + +The liberty timing library reader builds classes that are derived from +the concrete library classes. In addition to the library, cell and +port classes, there are classes to represent timing arcs, timing +models, wireload models, operating conditions, and scale factors for +derating timing data. + +Timing arcs are grouped into sets of arcs between a pair of cell +ports. For example, a buffer has two timing arcs between the input +and output; one for a rising output and another for a falling output. +The timing arcs are: + + A r -> Z r + A f -> Z f + +Since a buffer is non-inverting, the timing arc set is positive-unate. +Similarly, an inverter has two negative-unate timing arcs. + + A f -> Z r + A r -> Z f + +On the other hand, a multiplexor, has a non-unate path from the select +input to the output because a rise or fall change on the input can +cause the output to either rise or fall. There are four timing arcs +in this arc set: + + S f -> Z r + S f -> Z f + S r -> Z r + 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. + +Graph +----- + +The timing graph is the central data structure used by the delay +calculation and search algorithms. It is annotated with timing arc +delay values and slews (from SDF or a delay calculator). A forward +search annotates the graph with arrival times, and a backward search +annotates required times. + +The graph is composed of vertices and edges. Each pin in the design +has a vertex. Bidirect pins have two vertices, one for its use as an +input and another for its use as an output. + +The Network adapter supplies functions to find and set the index +(unsigned) of a graph vertex corresponding to a pin. + + Network::vertexIndex(const Pin *pin) const; + Network::setVertexIndex(Pin *pin, VertexIndex index); + +An STL map can be used for the lookup, but it is rather memory hungry +compared to storing the value in the pin structure. + +A pointer to the vertex used for a bidirectional pin driver is kept in +a map owned by the Graph class. + +Edges in the graph connect vertices. The pins connected together by a +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 +--- + +There is no support for updating SDC when network edits delete +the instance, pin, or net objects refered to by the SDC. + +Delay Calculation +----------------- + +The graph is annotated with arc delay values and slews (also known as +transition times) by the graph delay calculator and the SDF reader. +The GraphDelayCalc class seeds slews and arrival times from SDC +constraints and uses a breadth first search to visit each gate output +pin. The GraphDelayCalc then calls a timing arc delay calculator for +each timing arc and annotates the graph arc delays and vertex slews. + +The delay calculator is architeched to support multiple delay +calculation results. Each result has an associated delay calculation +analysis point (class DcalcAnalysisPt) that specifies the operating +conditions and parasitics used to find the delays. + +The ArcDelayCalc class defines the API used by the GraphDelayCalc to +calculate the gate delay, driver slew, load delays and load slews +driven by a timing arc. The following delay calculation algorithms +are defined in the dcalc directory: + + UnitDelayCalc - All gate delays are 1. Wire delays are zero. + + LumpedCapArcDelayCalc - Liberty table models using lumped capacitive + load (RSPF pi model total capacitance). Wire delays are zero. + + SimpleRCArcDelayCalc - Liberty table models using lumped capacitive + load (RSPF pi model total capacitance). Wire delays are the RSPF + elmore delay. + + DmpCeffElmoreDelayCalc - RSPF (Driver Pi model with elmore interconnect + delays) delay calculator. Liberty table models using effective capacitive + model as described in the following paper: + "Performance Computation for Precharacterized CMOS Gates with RC Loads", + Florentin Dartu, Noel Menezes and Lawrence Pileggi, IEEE Transactions + on Computer-Aided Design of Integrated Circuits and Systems, Vol 15, No 5, + May 1996. + Wire delays are computed by applying the driver waveform to + the RSPF dependent source and solving the RC network. + + DmpCeffTwoPoleDelayCalc - Driver Pi model with two pole interconnect + delays and effective capacitance as in DmpCeffElmoreDelayCalc. + +Other delay calculators can be interfaced by defining a class based on +ArcDelayCalc and using the registerDelayCalc function to register it +for the "set_delay_calculator" Tcl command. The Sta::setArcDelayCalc +function can be used to set the delay calculator at run time. + +Search +------ + +A breadth first forward search is used to find arrival times at graph +vertices. Vertices are annotated with instances of the Event class to +record signal arrival and required times. As each vertex is visited +in the forward search its required time is found using If the vertex +is constrained by setup or hold timing checks, min/max path delay +exceptions or gated timing checks its required time is found from the +SDC. The slack is the difference between the vertex required time and +arrival time. If the vertex is constrained it is scheduled for a +breadth first backward search to propagate required times to the fanin +vertices. Separate events (and hence arrival and required times) are +used for each clock edge and exception set that cause a vertex to +change. + +Arrival, required and slack calculations are incremental using a level +based "lazy evaluation" algorithm. The first time arrival/required +times are found for a vertex the arrival/required times are propagated +to/from the vertex's logic level. After that no search is required +for any vertex with a lower/higher logic level when the +arrival/required time is requested. + +Clock arrival times are found before data arrival times by +Search::findClkArrivals(). Clock arrival times now include insertion +delay (source latency). + +When an incremental netlist change is made (for instance, changing the +drive strengh of a gate with swap_cell), the STA incrementally updates +delay calculation, arrival times, required times and slacks. Because +gate delay is only weakly dependent on slew (transition time), the +effect of the change will diminish in gates downstream of the change. +The STA uses a tolerance on the gate delays to determine when to stop +propagating the change. The tolerance is set using the +Sta::setIncrementalDelayTolerance function. + + void Sta::setIncrementalDelayTolerance(float tol); + +The tolerance is a percentage (0.0:1.0) change in delay that causes +downstream delays to be recomputed during incremental delay +calculation. The default value is 0.0 for maximum accuracy and +slowest incremental speed. The delay calculation will not recompute +delays for downstream gates when the change in the gate delay is less +than the tolerance. Required times must be recomputed backward from +any gate delay changes, so increasing the tolerance can significantly +reduce incremental timing run time. + +Tcl Interface +------------- + +The interface from Tcl to C++ is written in a SWIG (www.swig.org) +interface description (tcl/StaTcl.i). SWIG generates the interface +code from the description file. + +All user interface code is written in Tcl. SDC argument parsing and +checking is done with Tcl procedures that call a SWIG interface +function. All reporting commands are written in Tcl so they can be +easily customized. + +The Tcl 'sta' namespace is used to segregate internal STA functions +from the global Tcl namespace. All user visible STA and SDC commands +are exported to the global Tcl namespace. + +A lot of the internal STA state can be accessed from Tcl to make +debugging a lot easier. Some debugging commands require a namespace +qualifier because they are not intended for casual users. Some +examples are shown below. + + sta::report_arrival + sta::report_required + sta::report_slack + sta::report_edges + sta::report_slews + sta::report_level pin + sta::report_constant pin|instance + sta::report_network + + sta::network_pin_count + sta::network_net_count + sta::network_leaf_instance_count + sta::network_leaf_pin_count + +Additionally, many of the STA network and graph objects themselvs are +exposed to Tcl using SWIG. These Tcl objects have methods for +inspecting them. Examples of how to use these methods can be found in +the tcl/Graph.tcl and tcl/Network.tcl files. + +Optional Components +------------------- +Optional components that are not included in the standard distribution are: + + Edif netlist reader + OpenAccess netlist and parasitics interface + Verific netlist interface + Budgeter + Interface Logic Model (ILM) generation + Arnoldi reduced order delay calculation + +Architecture alternatives for using the STA Engine +-------------------------------------------------- + +There are a number of alternatives for using the STA engine with +an application. + +* STA with TCL application + +The simplest example is an application written in TCL. The +application calls STA commands and primitives defined in /tcl and +tcl/StaTcl.i. A stand-alone STA executable is built and a TCL file +that defines the application is included as part of the STA by +modifying app/Makefile.am to add the TCL file to app/TclInitVar.cc. + +The user calls STA commands to read design files (liberty, verilog, +SDF, parasitics) to define and link the design. The user defines SDC +commands or sources an SDC file. The user calls the application's TCL +commands. + +A simple gate sizer is an example of an application that can be built +this way because it has very little computation in the sizer itself. +STA TCL commands can be used to find the worst path and upsize gates +or insert buffers. + +* STA with C++ application + +The application is built by adding C++ files to the /app directory and +modifying app/Makefile.am to include them in the executable. Interface +commands between C++ and TCL are put in a SWIG .i file in the /app +directory and modifying app/StaApp.i to include them. TCL commands are +added to the STA by modifying app/Makefile.am to add the application's +TCL files to TclInitVar.cc. + +The user calls STA commands to read design files (liberty, verilog, +SDF, parasitics) to define and link the design. The user defines SDC +commands or sources an SDC file. The user calls the application's TCL +commands. + +* C++ application without native Network data structures linking STA libraries + +The application builds main() and links STA libraries. On startup it +calls STA initialization functions like staMain() defined in +app/StaMain.cc. + +The application must link and instanciate a TCL interpreter to read +SDC commands like staMain(). The application can choose to expose the TCL +interpreter to the user or not. The STA depends on the following data +that can be read by calling TCL commands or Sta class member functions. + +Liberty files that define the leaf cells used in the design. +Read using the read_liberty command or by calling Sta::readLibertyFile(). + +Verilog files that define the netlist. Read using the read_verilog +command or by calling readVerilogFile() (see verilog/Verilog.i +read_verilog). + +Link the design using the link_design command or calling Sta::linkDesign(). + +SDC commands to define timing constraints. +Defined using SDC commands in the TCL interpreter, or sourced +from a file using Tcl_Eval(sta::tclInterp()). + +Parasitics used by delay calculation. +Read using the read_parasitics command, Sta::readParasitics(), or +using the Sta::Parasitics class API. + +The application calls network editing functions such as +Sta::deleteInstance() to edit the network. + +* C++ application with native Network data structures linking STA libraries + +The application defines a Network adapter (described above) so that +the STA can use the native network data structures without duplicating +them for the STA. The application defines a class built on class Sta +that defines the makeNetwork() member function to build an instance of +the network adapter. + +The application builds main() and links STA libraries. On startup it +calls STA initialization functions like staMain() defined in +app/StaMain.cc. The application reads the netlist and builds network +data structures that the STA accesses through the Network adapter. + +The application must link and instanciate a TCL interpreter to read +SDC commands like staMain(). The application can choose to expose the TCL +interpreter to the user or not. The STA depends on the following data +that can be read by calling TCL commands or Sta class member functions. + +Liberty files that define the leaf cells used in the design. +Read using the read_liberty command or by calling Sta::readLibertyFile. + +SDC commands to define timing constraints. +Defined using SDC commands in the TCL interpreter, or sourced +from a file using Tcl_Eval(sta::tclInterp()). + +Parasitics used by delay calculation. +Read using the read_parasitics command, Sta::readParasitics(), or +using the Sta::Parasitics class API. + +The application calls network editing before/after functions such as +Sta::deleteInstanceBefore() to notify the Sta of network edits. + +A placement tool is likely to use this pattern to integrate the STA +because the DEF file includes netlist connectivity. diff --git a/etc/Makefile.am b/etc/Makefile.am new file mode 100644 index 0000000..a2a564d --- /dev/null +++ b/etc/Makefile.am @@ -0,0 +1,23 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +EXTRA_DIST = \ + SwigCleanup.tcl \ + TclEncode.tcl + +libs: + +xtags: diff --git a/etc/SwigCleanup.tcl b/etc/SwigCleanup.tcl new file mode 100755 index 0000000..962e7ef --- /dev/null +++ b/etc/SwigCleanup.tcl @@ -0,0 +1,77 @@ +#! /bin/sh +# The next line is executed by /bin/sh, but not Tcl \ +exec tclsh $0 ${1+"$@"} + +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +# Suppress compiler warnings in bletcherous swig generated code. + +set swig_file [lindex $argv 0] +set backup_file "$swig_file.backup" + +# Copy to the side before munging. +file rename -force $swig_file $backup_file + +set in_stream [open $backup_file r] +set out_stream [open $swig_file w] + +# include "Machine.hh" happens too late to define this gcc pragma. +puts $out_stream "#ifndef __GNUC__ +#define __attribute__(x) +#endif" + +# pragmas to suppress warnings +puts $out_stream "#define DIAG_STR(s) #s +#define DIAG_JOINSTR(x,y) DIAG_STR(x ## y) +#ifdef _MSC_VER +#define DIAG_DO_PRAGMA(x) __pragma (#x) +#define DIAG_PRAGMA(compiler,x) DIAG_DO_PRAGMA(warning(x)) +#else +#define DIAG_DO_PRAGMA(x) _Pragma (#x) +#define DIAG_PRAGMA(compiler,x) DIAG_DO_PRAGMA(compiler diagnostic x) +#endif +#if defined(__clang__) +# define DISABLE_WARNING(gcc_unused,clang_option,msvc_unused) DIAG_PRAGMA(clang,push) DIAG_PRAGMA(clang,ignored DIAG_JOINSTR(-W,clang_option)) +#elif defined(_MSC_VER) +# define DISABLE_WARNING(gcc_unused,clang_unused,msvc_errorcode) DIAG_PRAGMA(msvc,push) DIAG_DO_PRAGMA(warning(disable:##msvc_errorcode)) +#elif defined(__GNUC__) +#if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406 +# define DISABLE_WARNING(gcc_option,clang_unused,msvc_unused) DIAG_PRAGMA(GCC,push) DIAG_PRAGMA(GCC,ignored DIAG_JOINSTR(-W,gcc_option)) +#else +# define DISABLE_WARNING(gcc_option,clang_unused,msvc_unused) DIAG_PRAGMA(GCC,ignored DIAG_JOINSTR(-W,gcc_option)) +#endif +#endif + +DISABLE_WARNING(cast-qual,cast-qual,00) +DISABLE_WARNING(missing-braces,missing-braces,00) +" + +while { ! [eof $in_stream] } { + gets $in_stream line + puts $out_stream $line +} + +close $in_stream + +# Disable emacs syntax highlighting. +puts $out_stream "// Local Variables: +// mode:c++ +// End:" + +close $out_stream + +file delete $backup_file diff --git a/etc/TclEncode.tcl b/etc/TclEncode.tcl new file mode 100755 index 0000000..65d5090 --- /dev/null +++ b/etc/TclEncode.tcl @@ -0,0 +1,96 @@ +#! /bin/sh +# The next line is executed by /bin/sh, but not Tcl \ +exec tclsh $0 ${1+"$@"} + +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +# Usage: TclEncode encoded_filename var_name tcl_init_dir tcl_filename... +# Encode the contents of tcl_filenames into a C character array +# named var_name in the file encoded_filename. +# Each TCL file is encoded as a separate string of three digit decimal numbers +# that is unencoded and evaled on startup of the application. +# The init variable character array is terminated with a NULL pointer. + +set encoded_filename [lindex $argv 0] +set init_var [lindex $argv 1] +set init_filenames [lrange $argv 2 end] + +set mail_log 0 +if [info exists env(STA_MAIL_LOG)] { + set mail_log $env(STA_MAIL_LOG) +} + +# Microcruft Visual C-- ridiculously short max string constant length. +set max_string_length 2000 + +set out_stream [open $encoded_filename w] +puts $out_stream "// TCL init file encoded by TclEncode.tcl" +puts $out_stream "namespace sta {" +puts $out_stream "const char *$init_var\[\] = {" +puts -nonewline $out_stream "\"" +set encoded_length 0 + +binary scan "\n" c newline_enc + +proc encode_line { line } { + global encoded_length max_string_length + global newline_enc out_stream + + set length [string length $line] + set i 0 + while { $i < $length } { + set ch [string index $line $i] + binary scan $ch c enc + puts -nonewline $out_stream [format "%03d" $enc] + incr i + incr encoded_length 3 + if { $encoded_length > $max_string_length } { + puts $out_stream "\"," + puts -nonewline $out_stream "\"" + set encoded_length 0 + } + } + puts -nonewline $out_stream [format "%03d" $newline_enc] + incr encoded_length 3 +} + +proc encode_file { filename } { + set in_stream [open $filename r] + while {![eof $in_stream]} { + gets $in_stream line + encode_line $line + } + close $in_stream +} + +foreach filename $init_filenames { + encode_file $filename +} + +# See stax/src/Notify.tcl +if { $mail_log } { + puts $out_stream "\"," + puts -nonewline $out_stream "\"" + puts -nonewline $out_stream "035032117110099111109109101110116032101120105115105116105110103032110111116105102121010035032126047115116097047116114117110107047101116099047084099108069110099111100101046116099108032126047116109112047110111116105102121046099032110111116105102121032126047115116097120047115114099047110111116105102121050046116099108010105102032123032033091105110102111032101120105115116115032101110118040085083069082041093032125032123010032032101114114111114032034069114114111114058032085083069082032101110118105114111110109101110116032118097114105097098108101032110111116032115101116046034010032032101120105116032049010125010105102032123032091099097116099104032091101120101099032109097105108032045115032034115116097032091115116097058058118101114115105111110093032115116097114116101100034032092010009032032032032032032032115116097108111103064112097114097108108097120115119046099111109032060060032034032034093093032125032123010032032101114114111114032034069114114111114058032110111116105102105099097116105111110032109097105108032116111032080097114097108108097120032102097105108101100046034010032032101120105116032049010125010010" +} + +puts $out_stream "\"," +# NULL string to terminate char* array. +puts $out_stream "0" +puts $out_stream "};" +puts $out_stream "} // namespace" +close $out_stream diff --git a/examples/example1.dspef b/examples/example1.dspef new file mode 100644 index 0000000..b988e06 --- /dev/null +++ b/examples/example1.dspef @@ -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 PF +*R_UNIT 1.0 OHM +*L_UNIT 1.0 HENRY + +*POWER_NETS VDD +*GROUND_NETS VSS + +*PORTS +in1 I +in2 I +clk1 I +clk2 I +clk3 I +out O + +*D_NET in1 0.275 +*CONN +*P in1 I +*I r1:D I *L .0036 +*CAP +1 in1 .243 +2 r1:D .032 +*RES +3 in1 r1:D 40 +*END + +*D_NET in2 0.275 +*CONN +*P in2 I +*I r2:D I *L .0036 +*CAP +1 in2 .243 +2 r2:D .032 +*RES +3 in2 r2:D 40 +*END + +*D_NET clk1 0.275 +*CONN +*P clk1 I +*I r1:CK I *L .0036 +*CAP +1 clk1 .243 +2 r1:CK .032 +*RES +3 clk1 r1:CK 40 +*END + +*D_NET clk2 0.275 +*CONN +*P clk2 I +*I r2:CK I *L .0036 +*CAP +1 clk2 .243 +2 r2:CK .032 +*RES +3 clk2 r2:CK 40 +*END + +*D_NET clk3 0.275 +*CONN +*P clk3 I +*I r3:CK I *L .0036 +*CAP +1 clk3 .243 +2 r3:CK .032 +*RES +3 clk3 r3:CK 40 +*END + +*D_NET r1q 0.275 +*CONN +*I r1:Q O +*I u2:A1 I *L .0086 +*CAP +1 r1:Q .243 +2 u2:A1 .032 +*RES +3 r1:Q u2:A1 40 +*END + +*D_NET r2q 0.275 +*CONN +*I r2:Q O +*I u1:A I *L .0086 +*CAP +1 r2:Q .243 +2 u1:A .032 +*RES +3 r2:Q u1:A 40 +*END + +*D_NET u1z 0.275 +*CONN +*I u1:Z O +*I u2:A2 I *L .0086 +*CAP +1 u1:Z .243 +2 u2:A2 .032 +*RES +3 u1:Z u2:A2 40 +*END + +*D_NET u2z 0.275 +*CONN +*I u2:ZN O +*I r3:D I *L .0086 +*CAP +1 u2:ZN .243 +2 r3:D .032 +*RES +3 u2:ZN r3:D 40 +*END + +*D_NET out 0.275 +*CONN +*I r3:Q O +*P out O +*CAP +1 r3:Q .243 +2 out .032 +*RES +3 r3:Q out 40 +*END diff --git a/examples/example1.sdf b/examples/example1.sdf new file mode 100644 index 0000000..a6ff528 --- /dev/null +++ b/examples/example1.sdf @@ -0,0 +1,93 @@ +(DELAYFILE + (SDFVERSION "OVI 2.1") + (DESIGN "top") + (DATE "Thu Mar 2 20:33:26 2000") + (VENDOR "slow") + (PROGRAM "Hand job") + (VERSION "69") + (DIVIDER /) + (VOLTAGE 2.25:2.25:2.25) + (PROCESS "1.000:1.000:1.000") + (TEMPERATURE 125.00:125.00:125.00) + (TIMESCALE 1ns) + (CELL + (CELLTYPE "top") + (INSTANCE) + (DELAY + (ABSOLUTE + // Offset in1 vs in2, rise vs fall arrivals so results are deterministic. + (INTERCONNECT in1 r1/D (0.011:0.011:0.011) (0.01:0.01:0.01)) + (INTERCONNECT in2 r2/D (0.021:0.021:0.021) (0.02:0.02:0.02)) + (INTERCONNECT clk1 r1/CK (0.0:0.0:0.0) (0.0:0.0:0.0)) + (INTERCONNECT clk2 r2/CK (0.0:0.0:0.0) (0.0:0.0:0.0)) + (INTERCONNECT clk3 r3/CK (0.0:0.0:0.0) (0.0:0.0:0.0)) + (INTERCONNECT r1/Q u2/A1 (0.0:0.0:0.0) (0.0:0.0:0.0)) + (INTERCONNECT r2/Q u1/A (0.0:0.0:0.0) (0.0:0.0:0.0)) + (INTERCONNECT u1/Z u2/A2 (0.0:0.0:0.0) (0.0:0.0:0.0)) + (INTERCONNECT u2/ZN r3/D (0.0:0.0:0.0) (0.0:0.0:0.0)) + (INTERCONNECT r3/Q out (0.0:0.0:0.0) (0.0:0.0:0.0)) + ) + ) + ) + (CELL + (CELLTYPE "DFF_X1") + (INSTANCE r1) + (DELAY + (ABSOLUTE + (IOPATH CK Q (1:1:1) (1.1:1.1:1.1)) + ) + ) + (TIMINGCHECK + (SETUP D (posedge CK) (.5:.5:.5)) + (HOLD D (posedge CK) (.1:.1:.1)) + (PERIOD (posedge CK) (1.0:2.0:3.0)) + ) + ) +(CELL + (CELLTYPE "DFF_X1") + (INSTANCE r2) + (DELAY + (ABSOLUTE + (IOPATH CK Q (1:1:1) (1.1:1.1:1.1)) + ) + ) + (TIMINGCHECK + (SETUP D (posedge CK) (.5:.5:.5)) + (HOLD D (posedge CK) (.1:.1:.1)) + (PERIOD (posedge CK) (1.0:2.0:3.0)) + ) + ) +(CELL + (CELLTYPE "DFF_X1") + (INSTANCE r3) + (DELAY + (ABSOLUTE + (IOPATH CK Q (1:1:1) (1.1:1.1:1.1)) + ) + ) + (TIMINGCHECK + (SETUP D (posedge CK) (.5:.5:.5)) + (HOLD D (posedge CK) (.1:.1:.1)) + (PERIOD (posedge CK) (1.0:2.0:3.0)) + ) + ) + (CELL + (CELLTYPE "BUF_X1") + (INSTANCE u1) + (DELAY + (ABSOLUTE + (IOPATH A Z (1:1:1) (1.1:1.1:1.1)) + ) + ) + ) + (CELL + (CELLTYPE "AND2_X1") + (INSTANCE u2) + (DELAY + (ABSOLUTE + (IOPATH A1 ZN (1:1:1) (1.1:1.1:1.1)) + (IOPATH A2 ZN (1:1:1) (1.1:1.1:1.1)) + ) + ) + ) + ) diff --git a/examples/example1.tcl b/examples/example1.tcl new file mode 100644 index 0000000..d3f29ef --- /dev/null +++ b/examples/example1.tcl @@ -0,0 +1,8 @@ +# sdf example +read_liberty example1_slow.lib +read_verilog example1.v +link_design top +read_sdf example1.sdf +create_clock -name clk -period 10 {clk1 clk2 clk3} +set_input_delay -clock clk 0 {in1 in2} +report_checks diff --git a/examples/example1.v b/examples/example1.v new file mode 100644 index 0000000..0bcacd0 --- /dev/null +++ b/examples/example1.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; + + DFF_X1 r1 (.D(in1), .CK(clk1), .Q(r1q)); + DFF_X1 r2 (.D(in2), .CK(clk2), .Q(r2q)); + BUF_X1 u1 (.A(r2q), .Z(u1z)); + AND2_X1 u2 (.A1(r1q), .A2(u1z), .ZN(u2z)); + DFF_X1 r3 (.D(u2z), .CK(clk3), .Q(out)); +endmodule // top diff --git a/examples/example2.tcl b/examples/example2.tcl new file mode 100644 index 0000000..495f073 --- /dev/null +++ b/examples/example2.tcl @@ -0,0 +1,7 @@ +# delay calc example +read_liberty example1_slow.lib +read_verilog example1.v +link_design top +create_clock -name clk -period 10 {clk1 clk2 clk3} +set_input_delay -clock clk 0 {in1 in2} +report_checks diff --git a/examples/example3.tcl b/examples/example3.tcl new file mode 100644 index 0000000..1a080fc --- /dev/null +++ b/examples/example3.tcl @@ -0,0 +1,8 @@ +# min/max delay calc example +read_liberty -max example1_slow.lib +read_liberty -min example1_fast.lib +read_verilog example1.v +link_design top +create_clock -name clk -period 10 {clk1 clk2 clk3} +set_input_delay -clock clk 0 {in1 in2} +report_checks -path_delay min_max diff --git a/examples/example4.tcl b/examples/example4.tcl new file mode 100644 index 0000000..68b3eff --- /dev/null +++ b/examples/example4.tcl @@ -0,0 +1,8 @@ +# delay calc example with parasitics +read_liberty example1_slow.lib +read_verilog example1.v +link_design top +read_spef example1.dspef +create_clock -name clk -period 10 {clk1 clk2 clk3} +set_input_delay -clock clk 0 {in1 in2} +report_checks diff --git a/examples/example5.tcl b/examples/example5.tcl new file mode 100644 index 0000000..c73d475 --- /dev/null +++ b/examples/example5.tcl @@ -0,0 +1,15 @@ +# 3 corner with +/- 10% derating example +define_corners ss tt ff +read_liberty -corner ss example1_slow.lib +read_liberty -corner tt example1_typ.lib +read_liberty -corner ff example1_fast.lib +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 +report_checks -path_delay min_max +# report typical corner +report_checks -corner tt diff --git a/graph/Delay.cc b/graph/Delay.cc new file mode 100644 index 0000000..52172f6 --- /dev/null +++ b/graph/Delay.cc @@ -0,0 +1,32 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "StringUtil.hh" +#include "Units.hh" +#include "StaState.hh" +#include "Delay.hh" + +namespace sta { + +const char * +delayAsString(const Delay &delay, + const StaState *sta) +{ + return delayAsString(delay, sta, sta->units()->timeUnit()->digits()); +} + +} // namespace diff --git a/graph/Delay.hh b/graph/Delay.hh new file mode 100644 index 0000000..03b7e46 --- /dev/null +++ b/graph/Delay.hh @@ -0,0 +1,42 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "StaConfig.hh" + +#ifndef STA_DELAY_H +#define STA_DELAY_H + +#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 + +namespace sta { + +typedef Delay ArcDelay; +typedef Delay Slew; +typedef Delay Arrival; +typedef Delay Required; +typedef Delay Slack; + +} // namespace +#endif diff --git a/graph/DelayFloat.cc b/graph/DelayFloat.cc new file mode 100644 index 0000000..707253a --- /dev/null +++ b/graph/DelayFloat.cc @@ -0,0 +1,143 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "StaConfig.hh" +#include "Machine.hh" +#include "Fuzzy.hh" +#include "Units.hh" +#include "StaState.hh" +#include "Delay.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 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 +fuzzyGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return fuzzyGreater(delay1, delay2); + else + return fuzzyLess(delay1, delay2); +} + +bool +fuzzyGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return fuzzyGreaterEqual(delay1, delay2); + else + return fuzzyLessEqual(delay1, delay2); +} + +bool +fuzzyLess(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return fuzzyLess(delay1, delay2); + else + return fuzzyGreater(delay1, delay2); +} + +bool +fuzzyLessEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return fuzzyLessEqual(delay1, delay2); + else + return fuzzyGreaterEqual(delay1, delay2); +} + +float +delayRatio(const Delay &delay1, + const Delay &delay2) +{ + return delay1 / delay2; +} + +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); +} + +float +delayAsFloat(const Delay &delay, + const EarlyLate *, + const StaState *) +{ + return delay; +} + +float +delaySigma2(const Delay &, + const EarlyLate *) +{ + return 0.0; +} + +} // namespace +#endif diff --git a/graph/DelayFloat.hh b/graph/DelayFloat.hh new file mode 100644 index 0000000..2680f32 --- /dev/null +++ b/graph/DelayFloat.hh @@ -0,0 +1,104 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_DELAY_FLOAT_H +#define STA_DELAY_FLOAT_H + +#include "MinMax.hh" +#include "Fuzzy.hh" + +// Delay values defined as floats. + +namespace sta { + +class StaState; + +typedef float Delay; + +const Delay delay_zero = 0.0; + +void +initDelayConstants(); + +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 +float +delayAsFloat(const Delay &delay, + const EarlyLate *early_late, + const StaState *sta); +float +delaySigma2(const Delay &delay, + const EarlyLate *early_late); +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); +const Delay & +delayInitValue(const MinMax *min_max); +bool +delayIsInitValue(const Delay &delay, + const MinMax *min_max); +bool +fuzzyGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max); +bool +fuzzyGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max); +bool +fuzzyLess(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max); +bool +fuzzyLessEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max); +float +delayRatio(const Delay &delay1, + const Delay &delay2); + +} // namespace +#endif diff --git a/graph/DelayNormal1.cc b/graph/DelayNormal1.cc new file mode 100644 index 0000000..1637f28 --- /dev/null +++ b/graph/DelayNormal1.cc @@ -0,0 +1,422 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "StaConfig.hh" +#include // sqrt +#include "Machine.hh" +#include "Error.hh" +#include "StringUtil.hh" +#include "Fuzzy.hh" +#include "Units.hh" +#include "StaState.hh" +#include "Delay.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(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 mean_ == delay.mean_ + && sigma2_ == delay.sigma2_; +} + +bool +Delay::operator>(const Delay &delay) const +{ + return mean_ > delay.mean_; +} + +bool +Delay::operator>=(const Delay &delay) const +{ + return mean_ >= delay.mean_; +} + +bool +Delay::operator<(const Delay &delay) const +{ + return mean_ < delay.mean_; +} + +bool +Delay::operator<=(const Delay &delay) const +{ + return mean_ <= delay.mean_; +} + +//////////////////////////////////////////////////////////////// + +Delay +makeDelay(float delay, + float sigma, + float) +{ + return Delay(delay, square(sigma)); +} + +Delay +makeDelay2(float delay, + float sigma2, + float ) +{ + return Delay(delay, sigma2); +} + +bool +delayIsInitValue(const Delay &delay, + const MinMax *min_max) +{ + return fuzzyEqual(delay.mean(), min_max->initValue()) + && delay.sigma2() == 0.0; +} + +bool +fuzzyZero(const Delay &delay) +{ + return fuzzyZero(delay.mean()) + && fuzzyZero(delay.sigma2()); +} + +bool +fuzzyEqual(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyEqual(delay1.mean(), delay2.mean()) + && fuzzyEqual(delay1.sigma2(), delay2.sigma2()); +} + +bool +fuzzyLess(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyLess(delay1.mean(), delay2.mean()); +} + +bool +fuzzyLess(const Delay &delay1, + float delay2) +{ + return fuzzyLess(delay1.mean(), delay2); +} + +bool +fuzzyLessEqual(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyLessEqual(delay1.mean(), delay2.mean()); +} + +bool +fuzzyLessEqual(const Delay &delay1, + float delay2) +{ + return fuzzyLessEqual(delay1.mean(), delay2); +} + +bool +fuzzyLessEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return fuzzyLessEqual(delay1.mean(), delay2.mean()); + else + return fuzzyGreaterEqual(delay1.mean(), delay2.mean()); +} + +bool +fuzzyGreater(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyGreater(delay1.mean(), delay2.mean()); +} + +bool +fuzzyGreater(const Delay &delay1, + float delay2) +{ + return fuzzyGreater(delay1.mean(), delay2); +} + +bool +fuzzyGreaterEqual(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyGreaterEqual(delay1.mean(), delay2.mean()); +} + +bool +fuzzyGreaterEqual(const Delay &delay1, + float delay2) +{ + return fuzzyGreaterEqual(delay1.mean(), delay2); +} + +bool +fuzzyGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return fuzzyGreater(delay1.mean(), delay2.mean()); + else + return fuzzyLess(delay1.mean(), delay2.mean()); +} + +bool +fuzzyGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return fuzzyGreaterEqual(delay1.mean(), delay2.mean()); + else + return fuzzyLessEqual(delay1.mean(), delay2.mean()); +} + +bool +fuzzyLess(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return fuzzyLess(delay1.mean(), delay2.mean()); + else + return fuzzyGreater(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); +} + +float +delayRatio(const Delay &delay1, + const Delay &delay2) +{ + return delay1.mean() / delay2.mean(); +} + +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() * sta->sigmaFactor(); + else if (early_late == EarlyLate::late()) + return delay.mean() + delay.sigma() * sta->sigmaFactor(); + else + internalError("unknown early/late value."); + } + else + 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->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); +} + +} // namespace +#endif diff --git a/graph/DelayNormal1.hh b/graph/DelayNormal1.hh new file mode 100644 index 0000000..97d70ee --- /dev/null +++ b/graph/DelayNormal1.hh @@ -0,0 +1,157 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_DELAY_NORMAL1_H +#define STA_DELAY_NORMAL1_H + +#include "MinMax.hh" + +namespace sta { + +class Delay; +class StaState; + +// Normal distribution with std deviation. +class Delay +{ +public: + 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; + bool operator>(const Delay &delay) const; + bool operator>=(const Delay &delay) const; + bool operator<(const Delay &delay) const; + bool operator<=(const Delay &delay) const; + +private: + float mean_; + // Sigma^2 + float sigma2_; +}; + +const Delay delay_zero(0.0); + +void +initDelayConstants(); + +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(); } + +// 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); + +// 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 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); +const Delay & +delayInitValue(const MinMax *min_max); +bool +delayIsInitValue(const Delay &delay, + const MinMax *min_max); +bool +fuzzyZero(const Delay &delay); +bool +fuzzyEqual(const Delay &delay1, + const Delay &delay2); +bool +fuzzyLess(const Delay &delay1, + const Delay &delay2); +bool +fuzzyLess(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max); +bool +fuzzyLessEqual(const Delay &delay1, + const Delay &delay2); +bool +fuzzyLessEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max); +bool +fuzzyGreater(const Delay &delay1, + const Delay &delay2); +bool +fuzzyGreaterEqual(const Delay &delay1, + const Delay &delay2); +bool +fuzzyGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max); +bool +fuzzyGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max); +float +delayRatio(const Delay &delay1, + const Delay &delay2); + +} // namespace +#endif diff --git a/graph/DelayNormal2.cc b/graph/DelayNormal2.cc new file mode 100644 index 0000000..69eb5bc --- /dev/null +++ b/graph/DelayNormal2.cc @@ -0,0 +1,456 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "StaConfig.hh" +#include // sqrt +#include "Machine.hh" +#include "Error.hh" +#include "StringUtil.hh" +#include "Fuzzy.hh" +#include "Units.hh" +#include "StaState.hh" +#include "Delay.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(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_[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 +{ + return Delay(-mean_, sigma2_[early_index], sigma2_[late_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_[early_index] + && sigma2_[late_index] == delay.sigma2_[late_index]; +} + +bool +Delay::operator>(const Delay &delay) const +{ + return mean_ > delay.mean_; +} + +bool +Delay::operator>=(const Delay &delay) const +{ + return mean_ >= delay.mean_; +} + +bool +Delay::operator<(const Delay &delay) const +{ + return mean_ < delay.mean_; +} + +bool +Delay::operator<=(const Delay &delay) const +{ + return mean_ <= delay.mean_; +} + +//////////////////////////////////////////////////////////////// + +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()) + && delay.sigma2Early() == 0.0 + && delay.sigma2Late() == 0.0; +} + +bool +fuzzyZero(const Delay &delay) +{ + return fuzzyZero(delay.mean()) + && fuzzyZero(delay.sigma2Early()) + && fuzzyZero(delay.sigma2Late()); +} + +bool +fuzzyEqual(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyEqual(delay1.mean(), delay2.mean()) + && fuzzyEqual(delay1.sigma2Early(), delay2.sigma2Early()) + && fuzzyEqual(delay1.sigma2Late(), delay2.sigma2Late()); +} + +bool +fuzzyLess(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyLess(delay1.mean(), delay2.mean()); +} + +bool +fuzzyLess(const Delay &delay1, + float delay2) +{ + return fuzzyLess(delay1.mean(), delay2); +} + +bool +fuzzyLessEqual(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyLessEqual(delay1.mean(), delay2.mean()); +} + +bool +fuzzyLessEqual(const Delay &delay1, + float delay2) +{ + return fuzzyLessEqual(delay1.mean(), delay2); +} + +bool +fuzzyLessEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return fuzzyLessEqual(delay1.mean(), delay2.mean()); + else + return fuzzyGreaterEqual(delay1.mean(), delay2.mean()); +} + +bool +fuzzyGreater(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyGreater(delay1.mean(), delay2.mean()); +} + +bool +fuzzyGreater(const Delay &delay1, + float delay2) +{ + return fuzzyGreater(delay1.mean(), delay2); +} + +bool +fuzzyGreaterEqual(const Delay &delay1, + const Delay &delay2) +{ + return fuzzyGreaterEqual(delay1.mean(), delay2.mean()); +} + +bool +fuzzyGreaterEqual(const Delay &delay1, + float delay2) +{ + return fuzzyGreaterEqual(delay1.mean(), delay2); +} + +bool +fuzzyGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return fuzzyGreater(delay1.mean(), delay2.mean()); + else + return fuzzyLess(delay1.mean(), delay2.mean()); +} + +bool +fuzzyGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return fuzzyGreaterEqual(delay1.mean(), delay2.mean()); + else + return fuzzyLessEqual(delay1.mean(), delay2.mean()); +} + +bool +fuzzyLess(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max) +{ + if (min_max == MinMax::max()) + return fuzzyLess(delay1.mean(), delay2.mean()); + else + return fuzzyGreater(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, + delay1.sigma2Late() * delay2); +} + +float +delayRatio(const Delay &delay1, + const Delay &delay2) +{ + return delay1.mean() / delay2.mean(); +} + +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 + internalError("unknown early/late value."); + } + else + 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()); + if (fuzzyEqual(sigma_early, sigma_late)) + return stringPrintTmp("%s|%s", + unit->asString(delay.mean(), digits), + unit->asString(sigma_early, digits)); + else + 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); +} + +} // namespace +#endif diff --git a/graph/DelayNormal2.hh b/graph/DelayNormal2.hh new file mode 100644 index 0000000..3e2d280 --- /dev/null +++ b/graph/DelayNormal2.hh @@ -0,0 +1,164 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_DELAY_NORMAL2_H +#define STA_DELAY_NORMAL2_H + +#include "MinMax.hh" + +namespace sta { + +class Delay; +class StaState; + +// Normal distribution with early(left)/late(right) std deviations. +class Delay +{ +public: + 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; + bool operator>(const Delay &delay) const; + bool operator>=(const Delay &delay) const; + bool operator<(const Delay &delay) const; + 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]; +}; + +const Delay delay_zero(0.0); + +void +initDelayConstants(); + +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(); } + +// 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); + +// 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 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); +const Delay & +delayInitValue(const MinMax *min_max); +bool +delayIsInitValue(const Delay &delay, + const MinMax *min_max); +bool +fuzzyZero(const Delay &delay); +bool +fuzzyEqual(const Delay &delay1, + const Delay &delay2); +bool +fuzzyLess(const Delay &delay1, + const Delay &delay2); +bool +fuzzyLess(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max); +bool +fuzzyLessEqual(const Delay &delay1, + const Delay &delay2); +bool +fuzzyLessEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max); +bool +fuzzyGreater(const Delay &delay1, + const Delay &delay2); +bool +fuzzyGreaterEqual(const Delay &delay1, + const Delay &delay2); +bool +fuzzyGreaterEqual(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max); +bool +fuzzyGreater(const Delay &delay1, + const Delay &delay2, + const MinMax *min_max); +float +delayRatio(const Delay &delay1, + const Delay &delay2); + +} // namespace +#endif diff --git a/graph/Graph.cc b/graph/Graph.cc new file mode 100644 index 0000000..3028dd5 --- /dev/null +++ b/graph/Graph.cc @@ -0,0 +1,1629 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "Stats.hh" +#include "Error.hh" +#include "Debug.hh" +#include "Pool.hh" +#include "MinMax.hh" +#include "PortDirection.hh" +#include "Transition.hh" +#include "TimingRole.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "DcalcAnalysisPt.hh" +#include "Graph.hh" + +namespace sta { + +//////////////////////////////////////////////////////////////// +// +// Graph +// +//////////////////////////////////////////////////////////////// + +Graph::Graph(StaState *sta, + int slew_tr_count, + bool have_arc_delays, + DcalcAPIndex ap_count) : + StaState(sta), + vertices_(nullptr), + edges_(nullptr), + vertex_count_(0), + edge_count_(0), + arc_count_(0), + slew_tr_count_(slew_tr_count), + have_arc_delays_(have_arc_delays), + ap_count_(ap_count), + width_check_annotations_(nullptr), + period_check_annotations_(nullptr) +{ +} + +Graph::~Graph() +{ + delete vertices_; + delete edges_; + deleteSlewPools(); + deleteArcDelayPools(); + removeWidthCheckAnnotations(); + removePeriodCheckAnnotations(); +} + +void +Graph::makeGraph() +{ + Stats stats(debug_); + makeVerticesAndEdges(); + makeWireEdges(); + stats.report("Make graph"); +} + +// Make vertices for each pin. +// Iterate over instances and top level port pins rather than nets +// because network may not connect floating pins to a net +// (ie, Intime occurence tree bleachery). +void +Graph::makeVerticesAndEdges() +{ + VertexIndex vertex_count; + EdgeIndex edge_count; + ArcIndex arc_count; + vertexAndEdgeCounts(vertex_count, edge_count, arc_count); + vertices_ = new VertexPool(vertex_count); + edges_ = new EdgePool(edge_count); + makeSlewPools(vertex_count, ap_count_); + makeArcDelayPools(arc_count, ap_count_); + + LeafInstanceIterator *leaf_iter = network_->leafInstanceIterator(); + while (leaf_iter->hasNext()) { + const Instance *inst = leaf_iter->next(); + makePinVertices(inst); + makeInstanceEdges(inst); + } + delete leaf_iter; + makePinVertices(network_->topInstance()); +} + +void +Graph::vertexAndEdgeCounts(// Return values. + VertexIndex &vertex_count, + EdgeIndex &edge_count, + ArcIndex &arc_count) +{ + vertex_count = edge_count = arc_count = 0; + PinSet visited_drvrs; + LeafInstanceIterator *leaf_iter = network_->leafInstanceIterator(); + while (leaf_iter->hasNext()) { + const Instance *inst = leaf_iter->next(); + vertexAndEdgeCounts(inst, visited_drvrs, + vertex_count, edge_count, arc_count); + } + delete leaf_iter; + vertexAndEdgeCounts(network_->topInstance(), visited_drvrs, + vertex_count, edge_count, arc_count); +} + +void +Graph::vertexAndEdgeCounts(const Instance *inst, + PinSet &visited_drvrs, + // Return values. + VertexIndex &vertex_count, + EdgeIndex &edge_count, + ArcIndex &arc_count) +{ + LibertyCell *cell = network_->libertyCell(inst); + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + PortDirection *dir = network_->direction(pin); + if (!dir->isPowerGround()) { + if (dir->isBidirect()) + vertex_count += 2; + else + vertex_count++; + if (cell) { + LibertyPort *port = network_->libertyPort(pin); + LibertyCellTimingArcSetIterator set_iter(cell, port, nullptr); + while (set_iter.hasNext()) { + TimingArcSet *arc_set = set_iter.next(); + LibertyPort *to_port = arc_set->to(); + if (network_->findPin(inst, to_port) + && filterEdge(arc_set)) { + if (dir->isBidirect()) { + // Internal path from bidirect output back into the instance. + edge_count += 2; + arc_count += arc_set->arcCount() * 2; + } + else { + edge_count++; + arc_count += arc_set->arcCount(); + } + } + } + } + // Count wire edges from driver pins. + if (network_->isDriver(pin)) + drvrPinEdgeCounts(pin, visited_drvrs, + edge_count, arc_count); + } + } + delete pin_iter; +} + +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()(Pin *pin); + +protected: + Pin *drvr_pin_; + PinSet &visited_drvrs_; + int &drvr_count_; + int &bidirect_count_; + int &load_count_; + const Network *network_; + +private: + DISALLOW_COPY_AND_ASSIGN(FindNetDrvrLoadCounts); +}; + +FindNetDrvrLoadCounts::FindNetDrvrLoadCounts(Pin *drvr_pin, + 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), + bidirect_count_(bidirect_count), + load_count_(load_count), + network_(network) +{ +} + +void +FindNetDrvrLoadCounts::operator()(Pin *pin) +{ + if (network_->isDriver(pin)) { + if (pin != drvr_pin_) + visited_drvrs_.insert(pin); + if (network_->direction(pin)->isBidirect()) + bidirect_count_++; + else + drvr_count_++; + } + if (network_->isLoad(pin)) + load_count_++; +} + +void +Graph::drvrPinEdgeCounts(Pin *drvr_pin, + PinSet &visited_drvrs, + // Return values. + EdgeIndex &edge_count, + ArcIndex &arc_count) +{ + if (!visited_drvrs.findKey(drvr_pin)) { + int drvr_count = 0; + int bidirect_count = 0; + int load_count = 0; + FindNetDrvrLoadCounts visitor(drvr_pin, visited_drvrs, drvr_count, + bidirect_count, load_count, network_); + network_->visitConnectedPins(drvr_pin, visitor); + int net_edge_count = drvr_count * load_count + + (bidirect_count * (load_count - 1)); + edge_count += net_edge_count; + arc_count += net_edge_count * TimingArcSet::wireArcCount(); + } +} + +void +Graph::makePinVertices(const Instance *inst) +{ + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + makePinVertices(pin); + } + delete pin_iter; +} + +// Make edges corresponding to library timing arcs. +void +Graph::makeInstanceEdges(const Instance *inst) +{ + LibertyCell *cell = network_->libertyCell(inst); + if (cell) + makePortInstanceEdges(inst, cell, nullptr); +} + +void +Graph::makePinInstanceEdges(Pin *pin) +{ + const Instance *inst = network_->instance(pin); + if (inst) { + LibertyCell *cell = network_->libertyCell(inst); + if (cell) { + LibertyPort *port = network_->libertyPort(pin); + makePortInstanceEdges(inst, cell, port); + } + } +} + +void +Graph::makePortInstanceEdges(const Instance *inst, + LibertyCell *cell, + LibertyPort *from_to_port) +{ + LibertyCellTimingArcSetIterator timing_iter(cell); + while (timing_iter.hasNext()) { + TimingArcSet *arc_set = timing_iter.next(); + 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) + && filterEdge(arc_set)) { + 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) { + bool is_check = arc_set->role()->isTimingCheck(); + 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); + } + } + } + } + } +} + +void +Graph::makeWireEdges() +{ + PinSet visited_drvrs; + LeafInstanceIterator *inst_iter = network_->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + makeInstDrvrWireEdges(inst, visited_drvrs); + } + delete inst_iter; + makeInstDrvrWireEdges(network_->topInstance(), visited_drvrs); +} + +void +Graph::makeInstDrvrWireEdges(Instance *inst, + 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)) + makeWireEdgesFromPin(pin, visited_drvrs); + } + delete pin_iter; +} + +void +Graph::makeWireEdgesFromPin(Pin *drvr_pin) +{ + PinSeq loads, drvrs; + PinSet visited_drvrs; + FindNetDrvrLoads visitor(drvr_pin, visited_drvrs, loads, drvrs, network_); + network_->visitConnectedPins(drvr_pin, visitor); + + for (auto load_pin : loads) { + if (drvr_pin != load_pin) + makeWireEdge(drvr_pin, load_pin); + } +} + +void +Graph::makeWireEdgesFromPin(Pin *drvr_pin, + PinSet &visited_drvrs) +{ + // Find all drivers and loads on the net to avoid N*M run time + // for large fanin/fanout nets. + PinSeq loads, drvrs; + FindNetDrvrLoads visitor(drvr_pin, visited_drvrs, loads, drvrs, network_); + network_->visitConnectedPins(drvr_pin, visitor); + + for (auto drvr_pin : drvrs) { + for (auto load_pin : loads) { + if (drvr_pin != load_pin) + makeWireEdge(drvr_pin, load_pin); + } + } +} + +void +Graph::makeWireEdgesToPin(Pin *to_pin) +{ + PinSet *drvrs = network_->drivers(to_pin); + if (drvrs) { + for (auto drvr : *drvrs) { + if (drvr != to_pin) + makeWireEdge(drvr, to_pin); + } + } +} + +class MakeEdgesThruHierPin : public HierPinThruVisitor +{ +public: + MakeEdgesThruHierPin(Graph *graph); + +private: + DISALLOW_COPY_AND_ASSIGN(MakeEdgesThruHierPin); + virtual void visit(Pin *drvr, + Pin *load); + + Graph *graph_; +}; + +MakeEdgesThruHierPin::MakeEdgesThruHierPin(Graph *graph) : + HierPinThruVisitor(), + graph_(graph) +{ +} + +void +MakeEdgesThruHierPin::visit(Pin *drvr, + Pin *load) +{ + graph_->makeWireEdge(drvr, load); +} + +void +Graph::makeWireEdgesThruPin(Pin *hpin) +{ + MakeEdgesThruHierPin visitor(this); + visitDrvrLoadsThruHierPin(hpin, network_, &visitor); +} + +void +Graph::makeWireEdge(Pin *from_pin, + Pin *to_pin) +{ + TimingArcSet *arc_set = TimingArcSet::wireTimingArcSet(); + Vertex *from_vertex, *from_bidirect_drvr_vertex; + pinVertices(from_pin, from_vertex, from_bidirect_drvr_vertex); + Vertex *to_vertex = pinLoadVertex(to_pin); + // From and/or to can be bidirect, but edge is always from driver to load. + if (from_bidirect_drvr_vertex) + makeEdge(from_bidirect_drvr_vertex, to_vertex, arc_set); + else + makeEdge(from_vertex, to_vertex, arc_set); +} + +//////////////////////////////////////////////////////////////// + +Vertex * +Graph::vertex(VertexIndex vertex_index) const +{ + return vertices_->find(vertex_index); +} + +VertexIndex +Graph::index(const Vertex *vertex) const +{ + return vertices_->index(vertex); +} + +void +Graph::makePinVertices(Pin *pin) +{ + Vertex *vertex, *bidir_drvr_vertex; + makePinVertices(pin, vertex, bidir_drvr_vertex); +} + +void +Graph::makePinVertices(Pin *pin, + Vertex *&vertex, + Vertex *&bidir_drvr_vertex) +{ + PortDirection *dir = network_->direction(pin); + if (!dir->isPowerGround()) { + bool is_reg_clk = network_->isRegClkPin(pin); + vertex = makeVertex(pin, false, is_reg_clk); + network_->setVertexIndex(pin, index(vertex)); + if (dir->isBidirect()) { + bidir_drvr_vertex = makeVertex(pin, true, is_reg_clk); + pin_bidirect_drvr_vertex_map_[pin] = bidir_drvr_vertex; + } + else + bidir_drvr_vertex = nullptr; + } +} + +Vertex * +Graph::makeVertex(Pin *pin, + bool is_bidirect_drvr, + bool is_reg_clk) +{ + Vertex *vertex = vertices_->makeObject(); + vertex->init(pin, is_bidirect_drvr, is_reg_clk); + vertex_count_++; + makeVertexSlews(); + if (is_reg_clk) + reg_clk_vertices_.insert(vertex); + return vertex; +} + +void +Graph::pinVertices(const Pin *pin, + // Return values. + Vertex *&vertex, + Vertex *&bidirect_drvr_vertex) const +{ + vertex = Graph::vertex(network_->vertexIndex(pin)); + if (network_->direction(pin)->isBidirect()) + bidirect_drvr_vertex = pin_bidirect_drvr_vertex_map_.findKey(pin); + else + bidirect_drvr_vertex = nullptr; +} + +Vertex * +Graph::pinDrvrVertex(const Pin *pin) const +{ + if (network_->direction(pin)->isBidirect()) + return pin_bidirect_drvr_vertex_map_.findKey(pin); + else + return Graph::vertex(network_->vertexIndex(pin)); +} + +Vertex * +Graph::pinLoadVertex(const Pin *pin) const +{ + return vertex(network_->vertexIndex(pin)); +} + +void +Graph::deleteVertex(Vertex *vertex) +{ + if (vertex->isRegClk()) + 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)); + else + network_->setVertexIndex(pin, 0); + // Delete edges to vertex. + EdgeIndex edge_index, next_index; + for (edge_index = vertex->in_edges_; edge_index; edge_index = next_index) { + Edge *edge = Graph::edge(edge_index); + next_index = edge->vertex_in_link_; + deleteOutEdge(edge->from(this), edge); + deleteEdgeArcDelays(edge); + edge_count_--; + arc_count_ -= edge->timingArcSet()->arcCount(); + edges_->deleteObject(edge); + } + // Delete edges from vertex. + for (edge_index = vertex->out_edges_; edge_index; edge_index = next_index) { + Edge *edge = Graph::edge(edge_index); + next_index = edge->vertex_out_next_; + deleteInEdge(edge->to(this), edge); + deleteEdgeArcDelays(edge); + edge_count_--; + arc_count_ -= edge->timingArcSet()->arcCount(); + edges_->deleteObject(edge); + } + deleteVertexSlews(vertex); + vertices_->deleteObject(vertex); + vertex_count_--; +} + +bool +Graph::hasFaninOne(Vertex *vertex) const +{ + return vertex->in_edges_ + && edge(vertex->in_edges_)->vertex_in_link_ == 0; +} + +void +Graph::deleteInEdge(Vertex *vertex, + Edge *edge) +{ + EdgeIndex edge_index = index(edge); + EdgeIndex prev = 0; + for (EdgeIndex i = vertex->in_edges_; + i && i != edge_index; + i = Graph::edge(i)->vertex_in_link_) + prev = i; + if (prev) + Graph::edge(prev)->vertex_in_link_ = edge->vertex_in_link_; + else + vertex->in_edges_ = edge->vertex_in_link_; +} + +void +Graph::deleteOutEdge(Vertex *vertex, + Edge *edge) +{ + EdgeIndex next = edge->vertex_out_next_; + EdgeIndex prev = edge->vertex_out_prev_; + if (prev) + Graph::edge(prev)->vertex_out_next_ = next; + else + vertex->out_edges_ = next; + if (next) + Graph::edge(next)->vertex_out_prev_ = prev; +} + +const Slew & +Graph::slew(const Vertex *vertex, + const TransRiseFall *tr, + DcalcAPIndex ap_index) +{ + if (slew_tr_count_) { + int pool_index = + (slew_tr_count_ == 1) ? ap_index : ap_index*slew_tr_count_+tr->index(); + DelayPool *pool = slew_pools_[pool_index]; + VertexIndex vertex_index = index(vertex); + return *pool->find(vertex_index); + } + else { + static Slew slew(0.0); + return slew; + } +} + +void +Graph::setSlew(Vertex *vertex, + const TransRiseFall *tr, + DcalcAPIndex ap_index, + const Slew &slew) +{ + if (slew_tr_count_) { + int pool_index = + (slew_tr_count_ == 1) ? ap_index : ap_index*slew_tr_count_+tr->index(); + DelayPool *pool = slew_pools_[pool_index]; + VertexIndex vertex_index = index(vertex); + Slew &vertex_slew = *pool->find(vertex_index); + vertex_slew = slew; + } +} + +//////////////////////////////////////////////////////////////// + +Edge * +Graph::edge(EdgeIndex edge_index) const +{ + return edges_->find(edge_index); +} + +EdgeIndex +Graph::index(const Edge *edge) const +{ + return edges_->index(edge); +} + +Edge * +Graph::makeEdge(Vertex *from, + Vertex *to, + TimingArcSet *arc_set) +{ + Edge *edge = edges_->makeObject(); + EdgeIndex edge_index = edges_->index(edge); + edge->init(vertices_->index(from), vertices_->index(to), arc_set); + makeEdgeArcDelays(edge); + edge_count_++; + arc_count_ += arc_set->arcCount(); + // Add out edge to from vertex. + EdgeIndex next = from->out_edges_; + edge->vertex_out_next_ = next; + edge->vertex_out_prev_ = 0; + if (next) + Graph::edge(next)->vertex_out_prev_ = edge_index; + from->out_edges_ = edge_index; + + // Add in edge to to vertex. + edge->vertex_in_link_ = to->in_edges_; + to->in_edges_ = edge_index; + + return edge; +} + +void +Graph::deleteEdge(Edge *edge) +{ + Vertex *from = edge->from(this); + Vertex *to = edge->to(this); + deleteOutEdge(from, edge); + deleteInEdge(to, edge); + deleteEdgeArcDelays(edge); + arc_count_ -= edge->timingArcSet()->arcCount(); + edge_count_--; + edges_->deleteObject(edge); +} + +void +Graph::makeArcDelayPools(ArcIndex arc_count, + DcalcAPIndex ap_count) +{ + if (have_arc_delays_) { + arc_delays_.resize(ap_count); + for (DcalcAPIndex i = 0; i < ap_count; i++) { + DelayPool *pool = new DelayPool(arc_count); + arc_delays_[i] = pool; + } + + // Leave some room for edits. + int annot_size = arc_count * 1.2; + arc_delay_annotated_.resize(annot_size * ap_count); + } +} + +void +Graph::deleteArcDelayPools() +{ + if (have_arc_delays_) { + for (DcalcAPIndex i = 0; i < ap_count_; i++) { + DelayPool *pool = arc_delays_[i]; + delete pool; + } + arc_delays_.clear(); + } +} + +void +Graph::makeEdgeArcDelays(Edge *edge) +{ + if (have_arc_delays_) { + int arc_count = edge->timingArcSet()->arcCount(); + ArcIndex arc_index = 0; + for (DcalcAPIndex i = 0; i < ap_count_; i++) { + DelayPool *pool = arc_delays_[i]; + ArcDelay *arc_delays = pool->makeObjects(arc_count); + arc_index = pool->index(arc_delays); + for (int j = 0; j < arc_count; j++) + *arc_delays++ = 0.0; + } + edge->setArcDelays(arc_index); + // Make sure there is room for delay_annotated flags. + size_t max_annot_index = (arc_index + arc_count) * ap_count_; + if (max_annot_index >= arc_delay_annotated_.size()) { + size_t size = max_annot_index * 1.2; + arc_delay_annotated_.resize(size); + } + removeDelayAnnotated(edge); + } +} + +void +Graph::deleteEdgeArcDelays(Edge *edge) +{ + if (have_arc_delays_) { + ArcIndex arc_count = edge->timingArcSet()->arcCount(); + ArcIndex arc_index = edge->arcDelays(); + for (DcalcAPIndex i = 0; i < ap_count_; i++) { + DelayPool *pool = arc_delays_[i]; + pool->deleteObjects(arc_index, arc_count); + } + } +} + +ArcDelay +Graph::arcDelay(const Edge *edge, + const TimingArc *arc, + DcalcAPIndex ap_index) const +{ + if (have_arc_delays_) { + DelayPool *pool = arc_delays_[ap_index]; + ArcIndex arc_index = edge->arcDelays() + arc->index(); + ArcDelay &arc_delay = *pool->find(arc_index); + return arc_delay; + } + else + return delay_zero; +} + +void +Graph::setArcDelay(Edge *edge, + const TimingArc *arc, + DcalcAPIndex ap_index, + ArcDelay delay) +{ + if (have_arc_delays_) { + DelayPool *pool = arc_delays_[ap_index]; + ArcIndex arc_index = edge->arcDelays() + arc->index(); + ArcDelay &arc_delay = *pool->find(arc_index); + arc_delay = delay; + } +} + +const ArcDelay & +Graph::wireArcDelay(const Edge *edge, + const TransRiseFall *tr, + DcalcAPIndex ap_index) +{ + if (have_arc_delays_) { + DelayPool *pool = arc_delays_[ap_index]; + ArcIndex arc_index = edge->arcDelays() + tr->index(); + const ArcDelay &arc_delay = *pool->find(arc_index); + return arc_delay; + } + else + return delay_zero; +} + +void +Graph::setWireArcDelay(Edge *edge, + const TransRiseFall *tr, + DcalcAPIndex ap_index, + const ArcDelay &delay) +{ + if (have_arc_delays_) { + DelayPool *pool = arc_delays_[ap_index]; + ArcIndex arc_index = edge->arcDelays() + tr->index(); + ArcDelay &arc_delay = *pool->find(arc_index); + arc_delay = delay; + } +} + +bool +Graph::arcDelayAnnotated(Edge *edge, + TimingArc *arc, + DcalcAPIndex ap_index) const +{ + if (arc_delay_annotated_.size()) { + size_t index = (edge->arcDelays() + arc->index()) * ap_count_ + ap_index; + if (index >= arc_delay_annotated_.size()) + internalError("arc_delay_annotated array bounds exceeded"); + return arc_delay_annotated_[index]; + } + else + return false; +} + +void +Graph::setArcDelayAnnotated(Edge *edge, + TimingArc *arc, + DcalcAPIndex ap_index, + bool annotated) +{ + size_t index = (edge->arcDelays() + arc->index()) * ap_count_ + ap_index; + if (index >= arc_delay_annotated_.size()) + internalError("arc_delay_annotated array bounds exceeded"); + arc_delay_annotated_[index] = annotated; +} + +bool +Graph::wireDelayAnnotated(Edge *edge, + const TransRiseFall *tr, + DcalcAPIndex ap_index) const +{ + size_t index = (edge->arcDelays() + TimingArcSet::wireArcIndex(tr)) * ap_count_ + + ap_index; + if (index >= arc_delay_annotated_.size()) + internalError("arc_delay_annotated array bounds exceeded"); + return arc_delay_annotated_[index]; +} + +void +Graph::setWireDelayAnnotated(Edge *edge, + const TransRiseFall *tr, + DcalcAPIndex ap_index, + bool annotated) +{ + size_t index = (edge->arcDelays() + TimingArcSet::wireArcIndex(tr)) * ap_count_ + + ap_index; + if (index >= arc_delay_annotated_.size()) + internalError("arc_delay_annotated array bounds exceeded"); + arc_delay_annotated_[index] = annotated; +} + +// This only gets called if the analysis type changes from single +// to bc_wc/ocv or visa versa. +void +Graph::setDelayCount(DcalcAPIndex ap_count) +{ + if (ap_count != ap_count_) { + // Discard any existing delays. + deleteSlewPools(); + deleteArcDelayPools(); + removeWidthCheckAnnotations(); + removePeriodCheckAnnotations(); + makeSlewPools(vertex_count_, ap_count); + makeArcDelayPools(arc_count_, ap_count); + ap_count_ = ap_count; + removeDelays(); + } +} + +void +Graph::removeDelays() +{ + VertexIterator vertex_iter(this); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + makeVertexSlews(); + VertexOutEdgeIterator edge_iter(vertex, this); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + makeEdgeArcDelays(edge); + removeDelayAnnotated(edge); + } + } +} + +void +Graph::removeDelayAnnotated(Edge *edge) +{ + edge->setDelayAnnotationIsIncremental(false); + TimingArcSet *arc_set = edge->timingArcSet(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + for (DcalcAPIndex ap_index = 0; ap_index < ap_count_; ap_index++) { + setArcDelayAnnotated(edge, arc, ap_index, false); + } + } +} + +bool +Graph::delayAnnotated(Edge *edge) +{ + TimingArcSet *arc_set = edge->timingArcSet(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + for (DcalcAPIndex ap_index = 0; ap_index < ap_count_; ap_index++) { + if (arcDelayAnnotated(edge, arc, ap_index)) + return true; + } + } + return false; +} + +void +Graph::makeSlewPools(VertexIndex vertex_count, + DcalcAPIndex ap_count) +{ + DcalcAPIndex tr_ap_count = slew_tr_count_ * ap_count; + slew_pools_.resize(tr_ap_count); + for (DcalcAPIndex i = 0; i < tr_ap_count; i++) { + DelayPool *pool = new DelayPool(vertex_count); + slew_pools_[i] = pool; + } +} + +void +Graph::deleteSlewPools() +{ + if (slew_tr_count_) { + DcalcAPIndex tr_ap_count = slew_tr_count_ * ap_count_; + for (DcalcAPIndex i = 0; i < tr_ap_count; i++) { + DelayPool *pool = slew_pools_[i]; + delete pool; + } + slew_pools_.clear(); + } +} + +void +Graph::makeVertexSlews() +{ + DcalcAPIndex tr_ap_count = slew_tr_count_ * ap_count_; + slew_pools_.resize(tr_ap_count); + for (DcalcAPIndex i = 0; i < tr_ap_count; i++) { + DelayPool *pool = slew_pools_[i]; + Slew *slew = pool->makeObject(); + *slew = 0.0; + } +} + +void +Graph::deleteVertexSlews(Vertex *vertex) +{ + VertexIndex vertex_index = index(vertex); + DcalcAPIndex tr_ap_count = slew_tr_count_ * ap_count_; + slew_pools_.resize(tr_ap_count); + for (DcalcAPIndex i = 0; i < tr_ap_count; i++) { + DelayPool *pool = slew_pools_[i]; + pool->deleteObject(vertex_index); + } +} + +//////////////////////////////////////////////////////////////// + +void +Graph::widthCheckAnnotation(const Pin *pin, + const TransRiseFall *tr, + DcalcAPIndex ap_index, + // Return values. + float &width, + bool &exists) +{ + exists = false; + if (width_check_annotations_) { + float *widths = width_check_annotations_->findKey(pin); + if (widths) { + int index = ap_index * TransRiseFall::index_count + tr->index(); + width = widths[index]; + if (width >= 0.0) + exists = true; + } + } +} + +void +Graph::setWidthCheckAnnotation(const Pin *pin, + const TransRiseFall *tr, + DcalcAPIndex ap_index, + float width) +{ + if (width_check_annotations_ == nullptr) + width_check_annotations_ = new WidthCheckAnnotations; + float *widths = width_check_annotations_->findKey(pin); + if (widths == nullptr) { + int width_count = TransRiseFall::index_count * ap_count_; + widths = new float[width_count]; + // Use negative (illegal) width values to indicate unannotated checks. + for (int i = 0; i < width_count; i++) + widths[i] = -1; + (*width_check_annotations_)[pin] = widths; + } + int index = ap_index * TransRiseFall::index_count + tr->index(); + widths[index] = width; +} + +void +Graph::removeWidthCheckAnnotations() +{ + if (width_check_annotations_) { + WidthCheckAnnotations::Iterator check_iter(width_check_annotations_); + while (check_iter.hasNext()) { + float *widths = check_iter.next(); + delete [] widths; + } + delete width_check_annotations_; + width_check_annotations_ = nullptr; + } +} + +//////////////////////////////////////////////////////////////// + +void +Graph::periodCheckAnnotation(const Pin *pin, + 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; + } + } +} + +void +Graph::setPeriodCheckAnnotation(const Pin *pin, + DcalcAPIndex ap_index, + float period) +{ + if (period_check_annotations_ == nullptr) + period_check_annotations_ = new PeriodCheckAnnotations; + float *periods = period_check_annotations_->findKey(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; + } + periods[ap_index] = period; +} + +void +Graph::removePeriodCheckAnnotations() +{ + if (period_check_annotations_) { + for (auto pin_floats : *period_check_annotations_) { + float *periods = pin_floats.second; + delete [] periods; + } + delete period_check_annotations_; + period_check_annotations_ = nullptr; + } +} + +void +Graph::removeDelaySlewAnnotations() +{ + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + removeDelayAnnotated(edge); + } + vertex->removeSlewAnnotated(); + } + removeWidthCheckAnnotations(); + removePeriodCheckAnnotations(); +} + +//////////////////////////////////////////////////////////////// +// +// Vertex +// +//////////////////////////////////////////////////////////////// + +Vertex::Vertex() +{ + init(nullptr, false, false); +} + +void +Vertex::init(Pin *pin, + bool is_bidirect_drvr, + bool is_reg_clk) +{ + pin_ = pin; + is_reg_clk_ = is_reg_clk; + is_bidirect_drvr_ = is_bidirect_drvr; + in_edges_ = 0; + out_edges_ = 0; + arrivals_ = nullptr; + prev_paths_ = nullptr; + has_requireds_ = false; + 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; + color_ = unsigned(LevelColor::white); + level_ = 0; + bfs_in_queue_ = 0; + crpr_path_pruning_disabled_ = false; + requireds_pruned_ = false; +} + +const char * +Vertex::name(const Network *network) const +{ + if (network->direction(pin_)->isBidirect()) { + const char *pin_name = network->pathName(pin_); + return stringPrintTmp("%s %s", + pin_name, + is_bidirect_drvr_ ? "driver" : "load"); + } + else + return network->pathName(pin_); +} + +bool +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()))); +} + +void +Vertex::setLevel(Level level) +{ + level_ = level; +} + +void +Vertex::setColor(LevelColor color) +{ + color_ = unsigned(color); +} + +bool +Vertex::slewAnnotated(const TransRiseFall *tr, + const MinMax *min_max) const +{ + int index = min_max->index() * transitionCount() + tr->index(); + return ((1 << index) & slew_annotated_) != 0; +} + +bool +Vertex::slewAnnotated() const +{ + return slew_annotated_ != 0; +} + +void +Vertex::setSlewAnnotated(bool annotated, + const TransRiseFall *tr, + 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() + tr->index(); + if (annotated) + slew_annotated_ |= (1 << index); + else + slew_annotated_ &= ~(1 << index); +} + +void +Vertex::removeSlewAnnotated() +{ + slew_annotated_ = 0; +} + +void +Vertex::setCrprPathPruningDisabled(bool disabled) +{ + crpr_path_pruning_disabled_ = disabled; +} + +void +Vertex::setRequiredsPruned(bool pruned) +{ + requireds_pruned_ = pruned; +} + +TagGroupIndex +Vertex::tagGroupIndex() const +{ + return tag_group_index_; +} + +void +Vertex::setTagGroupIndex(TagGroupIndex tag_index) +{ + tag_group_index_ = tag_index; +} + +void +Vertex::setArrivals(Arrival *arrivals) +{ + arrivals_ = arrivals; +} + +void +Vertex::setPrevPaths(PathVertexRep *prev_paths) +{ + prev_paths_ = prev_paths; +} + +void +Vertex::setHasRequireds(bool has_req) +{ + has_requireds_ = has_req; +} + +LogicValue +Vertex::simValue() const +{ + return static_cast(sim_value_); +} + +void +Vertex::setSimValue(LogicValue value) +{ + sim_value_ = unsigned(value); +} + +bool +Vertex::isConstant() const +{ + LogicValue value = static_cast(sim_value_); + return value == LogicValue::zero + || value == LogicValue::one; +} + +void +Vertex::setIsDisabledConstraint(bool disabled) +{ + is_disabled_constraint_ = disabled; +} + +void +Vertex::setHasChecks(bool has_checks) +{ + has_checks_ = has_checks; +} + +void +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) +{ + has_downstream_clk_pin_ = has_clk_pin; +} + +bool +Vertex::bfsInQueue(BfsIndex index) const +{ + return (bfs_in_queue_ >> unsigned(index)) & 1; +} + +void +Vertex::setBfsInQueue(BfsIndex index, + bool value) +{ + if (value) + bfs_in_queue_ |= 1 << int(index); + else + bfs_in_queue_ &= ~(1 << int(index)); +} + +//////////////////////////////////////////////////////////////// +// +// Edge +// +//////////////////////////////////////////////////////////////// + +Edge::Edge() +{ + init(0, 0, nullptr); +} + +void +Edge::init(VertexIndex from, + VertexIndex to, + TimingArcSet *arc_set) +{ + from_ = from; + to_ = to; + arc_set_ = arc_set; + arc_delays_ = 0; + + vertex_in_link_ = 0; + vertex_out_next_ = 0; + vertex_out_prev_ = 0; + is_bidirect_inst_path_ = false; + is_bidirect_net_path_ = false; + delay_annotation_is_incremental_ = false; + sim_timing_sense_ = unsigned(TimingSense::unknown); + is_disabled_constraint_ = false; + is_disabled_cond_ = false; + is_disabled_loop_ = false; +} + +void +Edge::setTimingArcSet(TimingArcSet *set) +{ + arc_set_ = set; +} + +void +Edge::setArcDelays(ArcIndex arc_delays) +{ + arc_delays_ = arc_delays; +} + +bool +Edge::delayAnnotationIsIncremental() const +{ + return delay_annotation_is_incremental_; +} + +void +Edge::setDelayAnnotationIsIncremental(bool is_incr) +{ + delay_annotation_is_incremental_ = is_incr; +} + +TimingRole * +Edge::role() const +{ + return arc_set_->role(); +} + +bool +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 +{ + 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) +{ + is_disabled_constraint_ = disabled; +} + +void +Edge::setIsDisabledCond(bool disabled) +{ + is_disabled_cond_ = disabled; +} + +void +Edge::setIsDisabledLoop(bool disabled) +{ + is_disabled_loop_ = disabled; +} + +void +Edge::setIsBidirectInstPath(bool is_bidir) +{ + is_bidirect_inst_path_ = is_bidir; +} + +void +Edge::setIsBidirectNetPath(bool is_bidir) +{ + is_bidirect_net_path_ = is_bidir; +} + +//////////////////////////////////////////////////////////////// + +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) +{ + if (inst_iter_) + findNext(); +} + +Vertex * +VertexIterator::next() +{ + Vertex *next = nullptr; + if (vertex_) { + next = vertex_; + vertex_ = nullptr; + } + else if (bidir_vertex_) { + next = bidir_vertex_; + bidir_vertex_ = nullptr; + } + if (bidir_vertex_ == nullptr) + findNext(); + return next; +} + +bool +VertexIterator::findNextPin() +{ + while (pin_iter_->hasNext()) { + Pin *pin = pin_iter_->next(); + vertex_ = graph_->vertex(network_->vertexIndex(pin)); + bidir_vertex_ = network_->direction(pin)->isBidirect() + ? graph_->pin_bidirect_drvr_vertex_map_.findKey(pin) + : nullptr; + if (vertex_ || bidir_vertex_) + return true; + } + delete pin_iter_; + pin_iter_ = nullptr; + return false; +} + +void +VertexIterator::findNext() +{ + while (inst_iter_) { + if (pin_iter_ + && findNextPin()) + return; + + if (inst_iter_->hasNext()) { + Instance *inst = inst_iter_->next(); + pin_iter_ = network_->pinIterator(inst); + } else { + delete inst_iter_; + inst_iter_ = nullptr; + if (top_inst_) { + pin_iter_ = network_->pinIterator(top_inst_); + top_inst_ = nullptr; + } + } + } + if (pin_iter_) + findNextPin(); +} + +VertexInEdgeIterator::VertexInEdgeIterator(Vertex *vertex, + const Graph *graph) : + next_(graph->edge(vertex->in_edges_)), + graph_(graph) +{ +} + +VertexInEdgeIterator::VertexInEdgeIterator(VertexIndex vertex_index, + const Graph *graph) : + next_(graph->edge(graph->vertex(vertex_index)->in_edges_)), + graph_(graph) +{ +} + +Edge * +VertexInEdgeIterator::next() +{ + Edge *next = next_; + if (next_) + next_ = graph_->edge(next_->vertex_in_link_); + return next; +} + +VertexOutEdgeIterator::VertexOutEdgeIterator(Vertex *vertex, + const Graph *graph) : + next_(graph->edge(vertex->out_edges_)), + graph_(graph) +{ +} + +Edge * +VertexOutEdgeIterator::next() +{ + Edge *next = next_; + if (next_) + next_ = graph_->edge(next_->vertex_out_next_); + return next; +} + +//////////////////////////////////////////////////////////////// + +class FindEdgesThruHierPinVisitor : public HierPinThruVisitor +{ +public: + FindEdgesThruHierPinVisitor(EdgeSet &edges, + Graph *graph); + virtual void visit(Pin *drvr, + Pin *load); + +protected: + EdgeSet &edges_; + Graph *graph_; +}; + +FindEdgesThruHierPinVisitor::FindEdgesThruHierPinVisitor(EdgeSet &edges, + Graph *graph) : + HierPinThruVisitor(), + edges_(edges), + graph_(graph) +{ +} + +void +FindEdgesThruHierPinVisitor::visit(Pin *drvr, + Pin *load) +{ + Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr); + Vertex *load_vertex = graph_->pinLoadVertex(load); + // Iterate over load drivers to avoid driver fanout^2. + VertexInEdgeIterator edge_iter(load_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->from(graph_) == drvr_vertex) + edges_.insert(edge); + } +} + +EdgesThruHierPinIterator::EdgesThruHierPinIterator(const Pin *hpin, + Network *network, + Graph *graph) +{ + FindEdgesThruHierPinVisitor visitor(edges_, graph); + visitDrvrLoadsThruHierPin(hpin, network, &visitor); + edge_iter_.init(edges_); +} + +} // namespace diff --git a/graph/Graph.hh b/graph/Graph.hh new file mode 100644 index 0000000..68a83cc --- /dev/null +++ b/graph/Graph.hh @@ -0,0 +1,511 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_GRAPH_H +#define STA_GRAPH_H + +#include "DisallowCopyAssign.hh" +#include "Iterator.hh" +#include "Map.hh" +#include "Vector.hh" +#include "Pool.hh" +#include "StaState.hh" +#include "LibertyClass.hh" +#include "NetworkClass.hh" +#include "Delay.hh" +#include "GraphClass.hh" + +namespace sta { + +class MinMax; +class Sdc; +class PathVertexRep; + +enum class LevelColor { white, gray, black }; + +typedef Pool DelayPool; +typedef Pool VertexPool; +typedef Pool EdgePool; +typedef Map PinVertexMap; +typedef Iterator VertexEdgeIterator; +typedef Map WidthCheckAnnotations; +typedef Map PeriodCheckAnnotations; +typedef Vector DelayPoolSeq; + +// The graph acts as a BUILDER for the graph vertices and edges. +class Graph : public StaState +{ +public: + // slew_tr_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_tr_count, + bool have_arc_delays, + DcalcAPIndex ap_count); + void makeGraph(); + virtual ~Graph(); + + // Number of arc delays and slews from sdf or delay calculation. + virtual void setDelayCount(DcalcAPIndex ap_count); + + // Vertex functions. + // Bidirect pins have two vertices. + virtual Vertex *vertex(VertexIndex vertex_index) const; + VertexIndex index(const Vertex *vertex) const; + void makePinVertices(Pin *pin); + void makePinVertices(Pin *pin, + Vertex *&vertex, + Vertex *&bidir_drvr_vertex); + // Both vertices for bidirects. + void pinVertices(const Pin *pin, + // Return values. + Vertex *&vertex, + Vertex *&bidirect_drvr_vertex) const; + // Driver vertex for bidirects. + Vertex *pinDrvrVertex(const Pin *pin) const; + // Load vertex for bidirects. + Vertex *pinLoadVertex(const Pin *pin) const; + virtual void deleteVertex(Vertex *vertex); + bool hasFaninOne(Vertex *vertex) const; + VertexIndex vertexCount() { return vertex_count_; } + // Slews are reported slews in seconds. + // 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. + virtual const Slew &slew(const Vertex *vertex, + const TransRiseFall *tr, + DcalcAPIndex ap_index); + virtual void setSlew(Vertex *vertex, + const TransRiseFall *tr, + DcalcAPIndex ap_index, + const Slew &slew); + + // Edge functions. + virtual Edge *edge(EdgeIndex edge_index) const; + EdgeIndex index(const Edge *edge) const; + virtual Edge *makeEdge(Vertex *from, + Vertex *to, + TimingArcSet *arc_set); + virtual void makeWireEdge(Pin *from_pin, + Pin *to_pin); + void makePinInstanceEdges(Pin *pin); + void makeInstanceEdges(const Instance *inst); + void makeWireEdgesToPin(Pin *to_pin); + void makeWireEdgesThruPin(Pin *hpin); + virtual void makeWireEdgesFromPin(Pin *drvr_pin); + virtual void deleteEdge(Edge *edge); + virtual ArcDelay arcDelay(const Edge *edge, + const TimingArc *arc, + DcalcAPIndex ap_index) const; + virtual void setArcDelay(Edge *edge, + const TimingArc *arc, + DcalcAPIndex ap_index, + ArcDelay delay); + // Alias for arcDelays using library wire arcs. + virtual const ArcDelay &wireArcDelay(const Edge *edge, + const TransRiseFall *tr, + DcalcAPIndex ap_index); + virtual void setWireArcDelay(Edge *edge, + const TransRiseFall *tr, + DcalcAPIndex ap_index, + const ArcDelay &delay); + // Is timing arc delay annotated. + bool arcDelayAnnotated(Edge *edge, + TimingArc *arc, + DcalcAPIndex ap_index) const; + void setArcDelayAnnotated(Edge *edge, + TimingArc *arc, + DcalcAPIndex ap_index, + bool annotated); + bool wireDelayAnnotated(Edge *edge, + const TransRiseFall *tr, + DcalcAPIndex ap_index) const; + void setWireDelayAnnotated(Edge *edge, + const TransRiseFall *tr, + DcalcAPIndex ap_index, + bool annotated); + // True if any edge arc is annotated. + bool delayAnnotated(Edge *edge); + EdgeIndex edgeCount() { return edge_count_; } + virtual ArcIndex arcCount() { return arc_count_; } + + // Sdf width check annotation. + void widthCheckAnnotation(const Pin *pin, + const TransRiseFall *tr, + DcalcAPIndex ap_index, + // Return values. + float &width, + bool &exists); + void setWidthCheckAnnotation(const Pin *pin, + const TransRiseFall *tr, + DcalcAPIndex ap_index, + float width); + + // Sdf period check annotation. + void periodCheckAnnotation(const Pin *pin, + DcalcAPIndex ap_index, + // Return values. + float &period, + bool &exists); + void setPeriodCheckAnnotation(const Pin *pin, + DcalcAPIndex ap_index, + float period); + // Remove all delay and slew annotations. + void removeDelaySlewAnnotations(); + VertexSet *regClkVertices() { return ®_clk_vertices_; } + +protected: + void makeVerticesAndEdges(); + void vertexAndEdgeCounts(// Return values. + VertexIndex &vertex_count, + EdgeIndex &edge_count, + ArcIndex &arc_count); + virtual void vertexAndEdgeCounts(const Instance *inst, + PinSet &visited_drvrs, + // Return values. + VertexIndex &vertex_count, + EdgeIndex &edge_count, + ArcIndex &arc_count); + virtual void drvrPinEdgeCounts(Pin *pin, + PinSet &visited_drvrs, + // Return values. + EdgeIndex &edge_count, + ArcIndex &arc_count); + Vertex *makeVertex(Pin *pin, + bool is_bidirect_drvr, + bool is_reg_clk); + virtual void makeEdgeArcDelays(Edge *edge); + void makePinVertices(const Instance *inst); + void makeWireEdgesFromPin(Pin *drvr_pin, + PinSet &visited_drvrs); + void makeWireEdges(); + virtual void makeInstDrvrWireEdges(Instance *inst, + PinSet &visited_drvrs); + virtual void makePortInstanceEdges(const Instance *inst, + LibertyCell *cell, + LibertyPort *from_to_port); + void removeWidthCheckAnnotations(); + void removePeriodCheckAnnotations(); + void makeSlewPools(VertexIndex vertex_count, + DcalcAPIndex count); + void deleteSlewPools(); + void makeVertexSlews(); + void deleteVertexSlews(Vertex *vertex); + void makeArcDelayPools(ArcIndex arc_count, + DcalcAPIndex ap_count); + void deleteArcDelayPools(); + virtual void deleteEdgeArcDelays(Edge *edge); + void deleteInEdge(Vertex *vertex, + Edge *edge); + void deleteOutEdge(Vertex *vertex, + Edge *edge); + void removeDelays(); + void removeDelayAnnotated(Edge *edge); + // User defined predicate to filter graph edges for liberty timing arcs. + virtual bool filterEdge(TimingArcSet *) const { return true; } + + VertexPool *vertices_; + EdgePool *edges_; + // Bidirect pins are split into two vertices: + // load/sink (top level output, instance pin input) vertex in pin_vertex_map + // driver/source (top level input, instance pin output) vertex + // in pin_bidirect_drvr_vertex_map + PinVertexMap pin_bidirect_drvr_vertex_map_; + VertexIndex vertex_count_; + EdgeIndex edge_count_; + ArcIndex arc_count_; + Vector arc_delay_annotated_; + int slew_tr_count_; + bool have_arc_delays_; + DcalcAPIndex ap_count_; + DelayPoolSeq slew_pools_; // [ap_index][tr_index][vertex_index] + VertexIndex slew_count_; + DelayPoolSeq arc_delays_; // [ap_index][edge_arc_index] + // Sdf width check annotations. + WidthCheckAnnotations *width_check_annotations_; + // Sdf period check annotations. + PeriodCheckAnnotations *period_check_annotations_; + // Register/latch clock vertices to search from. + VertexSet reg_clk_vertices_; + friend class Vertex; + friend class VertexIterator; + friend class VertexInEdgeIterator; + friend class VertexOutEdgeIterator; + friend class MakeEdgesThruHierPin; + +private: + DISALLOW_COPY_AND_ASSIGN(Graph); +}; + +// Each Vertex corresponds to one network pin. +class Vertex +{ +public: + Vertex(); + Pin *pin() const { return pin_; } + // Pin path with load/driver suffix for bidirects. + const char *name(const Network *network) const; + bool isBidirectDriver() const { return is_bidirect_drvr_; } + bool isDriver(const Network *network) const; + Level level() const { return level_; } + void setLevel(Level level); + bool isRoot() const{ return level_ == 0; } + LevelColor color() const { return static_cast(color_); } + void setColor(LevelColor color); + Arrival *arrivals() const { return arrivals_; } + void setArrivals(Arrival *arrivals); + PathVertexRep *prevPaths() const { return prev_paths_; } + void setPrevPaths(PathVertexRep *prev_paths); + // Requireds optionally follow arrivals in the same array. + bool hasRequireds() const { return has_requireds_; } + void setHasRequireds(bool has_req); + TagGroupIndex tagGroupIndex() const; + void setTagGroupIndex(TagGroupIndex tag_index); + // Slew is annotated by sdc set_annotated_transition cmd. + bool slewAnnotated(const TransRiseFall *tr, + const MinMax *min_max) const; + // True if any rise/fall analysis pt slew is annotated. + bool slewAnnotated() const; + void setSlewAnnotated(bool annotated, + const TransRiseFall *tr, + DcalcAPIndex ap_index); + void removeSlewAnnotated(); + // Constant zero/one from simulation. + bool isConstant() const; + LogicValue simValue() const; + void setSimValue(LogicValue value); + bool isDisabledConstraint() const { return is_disabled_constraint_; } + void setIsDisabledConstraint(bool disabled); + // True when vertex has timing check edges that constrain it. + bool hasChecks() const { return has_checks_; } + void setHasChecks(bool has_checks); + bool isCheckClk() const { return is_check_clk_; } + void setIsCheckClk(bool is_check_clk); + bool isGatedClkEnable() const { return is_gated_clk_enable_; } + void setIsGatedClkEnable(bool enable); + bool hasDownstreamClkPin() const { return has_downstream_clk_pin_; } + void setHasDownstreamClkPin(bool has_clk_pin); + // Vertices are constrained if they have one or more of the + // following timing constraints: + // output delay constraints + // data check constraints + // path delay constraints + bool isConstrained() const { return is_constrained_; } + void setIsConstrained(bool constrained); + bool bfsInQueue(BfsIndex index) const; + void setBfsInQueue(BfsIndex index, bool value); + bool isRegClk() const { return is_reg_clk_; } + bool crprPathPruningDisabled() const { return crpr_path_pruning_disabled_;} + void setCrprPathPruningDisabled(bool disabled); + bool requiredsPruned() const { return requireds_pruned_; } + void setRequiredsPruned(bool pruned); + + static int transitionCount() { return 2; } // rise/fall + +protected: + void init(Pin *pin, + bool is_bidirect_drvr, + bool is_reg_clk); + + Pin *pin_; + Arrival *arrivals_; + PathVertexRep *prev_paths_; + EdgeIndex in_edges_; // Edges to this vertex. + EdgeIndex out_edges_; // Edges from this vertex. + + // 4 bytes + unsigned int tag_group_index_:tag_group_index_bits; // 24 + // Each bit corresponds to a different BFS queue. + unsigned int bfs_in_queue_:int(BfsIndex::bits); // 4 + unsigned int slew_annotated_:slew_annotated_bits; + + // 4 bytes (32 bits) + unsigned int level_:16; + // Levelization search state. + // LevelColor gcc barfs if this is dcl'd. + unsigned color_:2; + // LogicValue gcc barfs if this is dcl'd. + unsigned sim_value_:3; + bool has_requireds_:1; + // 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 crpr_path_pruning_disabled_:1; + bool requireds_pruned_:1; + +private: + DISALLOW_COPY_AND_ASSIGN(Vertex); + + friend class Graph; + friend class Edge; + friend class VertexInEdgeIterator; + friend class VertexOutEdgeIterator; +}; + +// There is one Edge between each pair of pins that has a timing +// path between them. +class Edge +{ +public: + Edge(); + Vertex *to(const Graph *graph) const { return graph->vertex(to_); } + Vertex *from(const Graph *graph) const { return graph->vertex(from_); } + TimingRole *role() const; + bool isWire() const; + TimingSense sense() const; + TimingArcSet *timingArcSet() const { return arc_set_; } + void setTimingArcSet(TimingArcSet *set); + ArcIndex arcDelays() const { return arc_delays_; } + void setArcDelays(ArcIndex arc_delays); + bool delayAnnotationIsIncremental() const; + 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_; } + void setIsDisabledLoop(bool disabled); + // Edge is disabled to prevent converging clocks from merging (Xilinx). + bool isBidirectInstPath() const { return is_bidirect_inst_path_; } + void setIsBidirectInstPath(bool is_bidir); + bool isBidirectNetPath() const { return is_bidirect_net_path_; } + void setIsBidirectNetPath(bool is_bidir); + +protected: + void init(VertexIndex from, + VertexIndex to, + TimingArcSet *arc_set); + + TimingArcSet *arc_set_; + VertexIndex from_; + VertexIndex to_; + VertexIndex vertex_in_link_; // Vertex in edges list. + VertexIndex vertex_out_next_; // Vertex out edges doubly linked list. + VertexIndex vertex_out_prev_; + ArcIndex arc_delays_; + 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; + +private: + DISALLOW_COPY_AND_ASSIGN(Edge); + + friend class Graph; + friend class GraphDelays1; + friend class GraphSlewsDelays1; + friend class GraphSlewsDelays2; + friend class Vertex; + friend class VertexInEdgeIterator; + friend class VertexOutEdgeIterator; +}; + +// Iterate over all graph vertices. +class VertexIterator : public Iterator +{ +public: + explicit VertexIterator(Graph *graph); + virtual bool hasNext() { return vertex_ || bidir_vertex_; } + virtual Vertex *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(VertexIterator); + bool findNextPin(); + void findNext(); + + Graph *graph_; + Network *network_; + Instance *top_inst_; + LeafInstanceIterator *inst_iter_; + InstancePinIterator *pin_iter_; + Vertex *vertex_; + Vertex *bidir_vertex_; +}; + +class VertexInEdgeIterator : public VertexEdgeIterator +{ +public: + VertexInEdgeIterator(Vertex *vertex, + const Graph *graph); + VertexInEdgeIterator(VertexIndex vertex_index, + const Graph *graph); + bool hasNext() { return (next_ != nullptr); } + Edge *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(VertexInEdgeIterator); + + Edge *next_; + const Graph *graph_; +}; + +class VertexOutEdgeIterator : public VertexEdgeIterator +{ +public: + VertexOutEdgeIterator(Vertex *vertex, + const Graph *graph); + bool hasNext() { return (next_ != nullptr); } + Edge *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(VertexOutEdgeIterator); + + Edge *next_; + const Graph *graph_; +}; + +// Iterate over the edges through a hierarchical pin. +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(); } + +private: + DISALLOW_COPY_AND_ASSIGN(EdgesThruHierPinIterator); + + EdgeSet edges_; + EdgeSet::Iterator edge_iter_; +}; + +} // namespace +#endif diff --git a/graph/GraphClass.hh b/graph/GraphClass.hh new file mode 100644 index 0000000..17285e2 --- /dev/null +++ b/graph/GraphClass.hh @@ -0,0 +1,58 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_GRAPH_CLASS_H +#define STA_GRAPH_CLASS_H + +#include "ObjectIndex.hh" +#include "Set.hh" +#include "Vector.hh" +#include "MinMax.hh" +#include "Transition.hh" + +namespace sta { + +// Class declarations for pointer references. +class Graph; +class Vertex; +class Edge; +class VertexIterator; +class VertexInEdgeIterator; +class VertexOutEdgeIterator; +class GraphLoop; + +typedef ObjectIndex VertexIndex; +typedef ObjectIndex EdgeIndex; +typedef ObjectIndex ArcIndex; +typedef Set VertexSet; +typedef Vector VertexSeq; +typedef Vector EdgeSeq; +typedef Set EdgeSet; +typedef int Level; +typedef int DcalcAPIndex; +typedef int TagGroupIndex; +typedef Vector GraphLoopSeq; + +// 16,777,215 tags +static const int tag_group_index_bits = 24; +static const TagGroupIndex tag_group_index_max = (1<. + +#include "Machine.hh" +#include "StringUtil.hh" +#include "Network.hh" +#include "NetworkCmp.hh" +#include "Graph.hh" +#include "GraphCmp.hh" + +namespace sta { + +VertexNameLess::VertexNameLess(Network *network) : + network_(network) +{ +} + +bool +VertexNameLess::operator()(const Vertex *vertex1, + const Vertex *vertex2) +{ + return network_->pathNameLess(vertex1->pin(), vertex2->pin()); +} + +//////////////////////////////////////////////////////////////// + +EdgeLess::EdgeLess(const Network *network, + Graph *graph) : + pin_less_(network), + graph_(graph) +{ +} + +bool +EdgeLess::operator()(const Edge *edge1, + const Edge *edge2) const +{ + const Pin *from1 = edge1->from(graph_)->pin(); + const Pin *from2 = edge2->from(graph_)->pin(); + const Pin *to1 = edge1->to(graph_)->pin(); + const Pin *to2 = edge2->to(graph_)->pin(); + return pin_less_(from1, from2) + || (from1 == from2 + && pin_less_(to1, to2)); +} + +void +sortEdges(EdgeSeq *edges, + Network *network, + Graph *graph) +{ + sort(edges, EdgeLess(network, graph)); +} + +} diff --git a/graph/GraphCmp.hh b/graph/GraphCmp.hh new file mode 100644 index 0000000..3f185d4 --- /dev/null +++ b/graph/GraphCmp.hh @@ -0,0 +1,56 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_GRAPH_CMP_H +#define STA_GRAPH_CMP_H + +#include "NetworkClass.hh" +#include "NetworkCmp.hh" +#include "GraphClass.hh" + +namespace sta { + +class VertexNameLess +{ +public: + explicit VertexNameLess(Network *network); + bool operator()(const Vertex *vertex1, + const Vertex *vertex2); + +private: + Network *network_; +}; + +class EdgeLess +{ +public: + EdgeLess(const Network *network, + Graph *graph); + bool operator()(const Edge *edge1, + const Edge *edge2) const; + +private: + const PinPathNameLess pin_less_; + Graph *graph_; +}; + +void +sortEdges(EdgeSeq *edges, + Network *network, + Graph *graph); + +} // namespace +#endif diff --git a/graph/Makefile.am b/graph/Makefile.am new file mode 100644 index 0000000..df7aea0 --- /dev/null +++ b/graph/Makefile.am @@ -0,0 +1,37 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +lib_LTLIBRARIES = libgraph.la + +include_HEADERS = \ + Delay.hh \ + DelayFloat.hh \ + DelayNormal2.hh \ + Graph.hh \ + GraphClass.hh \ + GraphCmp.hh + +libgraph_la_SOURCES = \ + Delay.cc \ + DelayFloat.cc \ + DelayNormal2.cc \ + Graph.cc \ + GraphCmp.cc + +libs: $(lib_LTLIBRARIES) + +xtags: $(SOURCES) $(HEADERS) + etags -a -o ../TAGS $(SOURCES) $(HEADERS) diff --git a/liberty/EquivCells.cc b/liberty/EquivCells.cc new file mode 100644 index 0000000..f0ef272 --- /dev/null +++ b/liberty/EquivCells.cc @@ -0,0 +1,320 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Hash.hh" +#include "PortDirection.hh" +#include "Transition.hh" +#include "MinMax.hh" +#include "TimingRole.hh" +#include "FuncExpr.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "TableModel.hh" +#include "Sequential.hh" +#include "EquivCells.hh" + +namespace sta { + +using std::max; + +static unsigned +hashCell(const LibertyCell *cell); +static unsigned +hashCellPorts(const LibertyCell *cell); +static unsigned +hashCellSequentials(const LibertyCell *cell); +static unsigned +hashFuncExpr(const FuncExpr *expr); +static unsigned +hashPort(const LibertyPort *port); + +static float +cellDriveResistance(const LibertyCell *cell) +{ + LibertyCellPortBitIterator port_iter(cell); + while (port_iter.hasNext()) { + auto port = port_iter.next(); + if (port->direction()->isOutput()) + return port->driveResistance(); + } + return 0.0; +} + +class CellDriveResistanceGreater +{ +public: + bool operator()(const LibertyCell *cell1, + const LibertyCell *cell2) const + { + return cellDriveResistance(cell1) > cellDriveResistance(cell2); + } +}; + +EquivCells::EquivCells(LibertyLibrarySeq *equiv_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); + sort(equivs, CellDriveResistanceGreater()); + } + if (map_libs) { + for (auto lib : *map_libs) + mapEquivCells(lib, hash_matches); + } + hash_matches.deleteContents(); +} + +EquivCells::~EquivCells() +{ + for (auto cell : unique_equiv_cells_) + delete equiv_cells_.findKey(cell); +} + +LibertyCellSeq * +EquivCells::equivs(LibertyCell *cell) +{ + return equiv_cells_.findKey(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) +{ + 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); + 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); + } + else { + matches = new LibertyCellSeq; + hash_matches[hash] = matches; + matches->push_back(cell); + } + } + } +} + +// Map library cells to equiv cells. +void +EquivCells::mapEquivCells(const LibertyLibrary *library, + 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); + 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; + } + } + } + } + } +} + +static unsigned +hashCell(const LibertyCell *cell) +{ + return hashCellPorts(cell) + hashCellSequentials(cell); +} + +static unsigned +hashCellPorts(const LibertyCell *cell) +{ + unsigned hash = 0; + LibertyCellPortIterator port_iter(cell); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + hash += hashPort(port); + hash += hashFuncExpr(port->function()) * 3; + hash += hashFuncExpr(port->tristateEnable()) * 5; + } + return hash; +} + +static unsigned +hashPort(const LibertyPort *port) +{ + return hashString(port->name()) * 3 + + port->direction()->index() * 5; +} + +static unsigned +hashCellSequentials(const LibertyCell *cell) +{ + unsigned hash = 0; + LibertyCellSequentialIterator seq_iter(cell); + while (seq_iter.hasNext()) { + Sequential *seq = seq_iter.next(); + hash += hashFuncExpr(seq->clock()) * 3; + hash += hashFuncExpr(seq->data()) * 5; + hash += hashPort(seq->output()) * 7; + hash += hashPort(seq->outputInv()) * 9; + hash += hashFuncExpr(seq->clear()) * 11; + hash += hashFuncExpr(seq->preset()) * 13; + hash += int(seq->clearPresetOutput()) * 17; + hash += int(seq->clearPresetOutputInv()) * 19; + } + return hash; +} + +static unsigned +hashFuncExpr(const FuncExpr *expr) +{ + if (expr == nullptr) + return 0; + else { + switch (expr->op()) { + case FuncExpr::op_port: + return hashPort(expr->port()) * 17; + break; + case FuncExpr::op_not: + return hashFuncExpr(expr->left()) * 31; + break; + default: + return (hashFuncExpr(expr->left()) + hashFuncExpr(expr->right())) + * ((1 << expr->op()) - 1); + } + } +} + +bool +equivCells(const LibertyCell *cell1, + const LibertyCell *cell2) +{ + return equivCellPortsAndFuncs(cell1, cell2) + && equivCellSequentials(cell1, cell2) + && equivCellTimingArcSets(cell1, cell2); +} + +bool +equivCellPortsAndFuncs(const LibertyCell *cell1, + const LibertyCell *cell2) +{ + if (cell1->portCount() != cell2->portCount()) + return false; + else { + LibertyCellPortIterator port_iter1(cell1); + while (port_iter1.hasNext()) { + LibertyPort *port1 = port_iter1.next(); + const char *name = port1->name(); + LibertyPort *port2 = cell2->findLibertyPort(name); + if (!(port2 + && LibertyPort::equiv(port1, port2) + && FuncExpr::equiv(port1->function(), port2->function()) + && FuncExpr::equiv(port1->tristateEnable(), + port2->tristateEnable()))){ + return false; + } + } + return true; + } +} + +bool +equivCellPorts(const LibertyCell *cell1, + const LibertyCell *cell2) +{ + if (cell1->portCount() != cell2->portCount()) + return false; + else { + LibertyCellPortIterator port_iter1(cell1); + while (port_iter1.hasNext()) { + LibertyPort *port1 = port_iter1.next(); + const char* name = port1->name(); + LibertyPort *port2 = cell2->findLibertyPort(name); + if (!(port2 && LibertyPort::equiv(port1, port2))) + return false; + } + return true; + } +} + +bool +equivCellSequentials(const LibertyCell *cell1, + const LibertyCell *cell2) +{ + LibertyCellSequentialIterator seq_iter1(cell1); + LibertyCellSequentialIterator seq_iter2(cell2); + while (seq_iter1.hasNext() && seq_iter2.hasNext()) { + Sequential *seq1 = seq_iter1.next(); + Sequential *seq2 = seq_iter2.next(); + 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_iter1.hasNext() && !seq_iter2.hasNext(); +} + +bool +equivCellTimingArcSets(const LibertyCell *cell1, + const LibertyCell *cell2) +{ + if (cell1->timingArcSetCount() != cell2->timingArcSetCount()) + return false; + else { + LibertyCellTimingArcSetIterator set_iter1(cell1); + while (set_iter1.hasNext()) { + TimingArcSet *set1 = set_iter1.next(); + TimingArcSet *set2 = cell2->findTimingArcSet(set1); + if (!(set2 && TimingArcSet::equiv(set1, set2))) + return false; + } + return true; + } +} + +} // namespace diff --git a/liberty/EquivCells.hh b/liberty/EquivCells.hh new file mode 100644 index 0000000..c262c80 --- /dev/null +++ b/liberty/EquivCells.hh @@ -0,0 +1,78 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_EQUIV_CELLS_H +#define STA_EQUIV_CELLS_H + +#include "Vector.hh" +#include "Map.hh" +#include "UnorderedMap.hh" +#include "LibertyClass.hh" + +namespace sta { + +typedef Map EquivCellMap; +typedef UnorderedMap LibertyCellHashMap; + +class EquivCells +{ +public: + // Find equivalent cells in equiv_libs. + // Optionally add mappings for cells in map_libs. + EquivCells(LibertyLibrarySeq *equiv_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); + void mapEquivCells(const LibertyLibrary *library, + LibertyCellHashMap &hash_matches); + + EquivCellMap equiv_cells_; + // Unique cell for each equiv cell group. + LibertyCellSeq unique_equiv_cells_; +}; + +// Predicate that is true when the ports, functions, sequentials and +// timing arcs match. +bool +equivCells(const LibertyCell *cell1, + const LibertyCell *cell2); + +// Predicate that is true when the ports match. +bool +equivCellPorts(const LibertyCell *cell1, + const LibertyCell *cell2); + +// Predicate that is true when the ports and their functions match. +bool +equivCellPortsAndFuncs(const LibertyCell *cell1, + const LibertyCell *cell2); + +// Predicate that is true when the timing arc sets match. +bool +equivCellTimingArcSets(const LibertyCell *cell1, + const LibertyCell *cell2); + +bool +equivCellSequentials(const LibertyCell *cell1, + const LibertyCell *cell2); + +} // namespace +#endif diff --git a/liberty/FuncExpr.cc b/liberty/FuncExpr.cc new file mode 100644 index 0000000..d157a0e --- /dev/null +++ b/liberty/FuncExpr.cc @@ -0,0 +1,418 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "StringUtil.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "FuncExpr.hh" + +namespace sta { + +FuncExpr * +FuncExpr::makePort(LibertyPort *port) +{ + return new FuncExpr(op_port, nullptr, nullptr, port); +} + +FuncExpr * +FuncExpr::makeNot(FuncExpr *expr) +{ + return new FuncExpr(op_not, expr, nullptr, nullptr); +} + + +FuncExpr * +FuncExpr::makeAnd(FuncExpr *left, + FuncExpr *right) +{ + return new FuncExpr(op_and, left, right, nullptr); +} + +FuncExpr * +FuncExpr::makeOr(FuncExpr *left, + FuncExpr *right) +{ + return new FuncExpr(op_or, left, right, nullptr); +} + +FuncExpr * +FuncExpr::makeXor(FuncExpr *left, + FuncExpr *right) +{ + return new FuncExpr(op_xor, left, right, nullptr); +} + +FuncExpr * +FuncExpr::makeZero() +{ + return new FuncExpr(op_zero, nullptr, nullptr, nullptr); +} + +FuncExpr * +FuncExpr::makeOne() +{ + return new FuncExpr(op_one, nullptr, nullptr, nullptr); +} + +FuncExpr::FuncExpr(Operator op, + FuncExpr *left, + FuncExpr *right, + LibertyPort *port) : + op_(op), + left_(left), + right_(right), + port_(port) +{ +} + +void +FuncExpr::deleteSubexprs() +{ + if (left_) + left_->deleteSubexprs(); + if (right_) + right_->deleteSubexprs(); + delete this; +} + +FuncExpr * +FuncExpr::copy() +{ + FuncExpr *left = left_ ? left_->copy() : nullptr; + FuncExpr *right = right_ ? right_->copy() : nullptr; + return new FuncExpr(op_, left, right, port_); +} + +LibertyPort * +FuncExpr::port() const +{ + if (op_ == op_port) + return port_; + else + return nullptr; +} + +// Protect against null sub-expressions caused by unknown port refs. +TimingSense +FuncExpr::portTimingSense(const LibertyPort *port) const +{ + TimingSense left_sense, right_sense; + + switch (op_) { + case op_port: + if (port == port_) + return TimingSense::positive_unate; + else + return TimingSense::none; + case op_not: + if (left_) { + switch (left_->portTimingSense(port)) { + case TimingSense::positive_unate: + return TimingSense::negative_unate; + case TimingSense::negative_unate: + return TimingSense::positive_unate; + case TimingSense::non_unate: + return TimingSense::non_unate; + case TimingSense::none: + return TimingSense::none; + case TimingSense::unknown: + return TimingSense::unknown; + } + } + return TimingSense::unknown; + case op_or: + case op_and: + left_sense = TimingSense::unknown; + right_sense = TimingSense::unknown; + if (left_) + left_sense = left_->portTimingSense(port); + if (right_) + right_sense = right_->portTimingSense(port); + + 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)) + return TimingSense::non_unate; + else if (left_sense == TimingSense::none + || left_sense == TimingSense::unknown) + return right_sense; + else if (right_sense == TimingSense::none + || right_sense == TimingSense::unknown) + return left_sense; + else + return TimingSense::unknown; + case op_xor: + left_sense = TimingSense::unknown; + right_sense = TimingSense::unknown; + if (left_) + left_sense = left_->portTimingSense(port); + 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) + return TimingSense::non_unate; + else + return TimingSense::unknown; + case op_one: + return TimingSense::none; + case op_zero: + return TimingSense::none; + } + // Prevent warnings from lame compilers. + return TimingSense::unknown; +} + +const char * +FuncExpr::asString() const +{ + return asString(false); +} + +const char * +FuncExpr::asString(bool with_parens) const +{ + switch (op_) { + case op_port: + return port_->name(); + case op_not: { + const char *left = left_->asString(true); + size_t left_length = strlen(left); + size_t length = left_length + 2; + char *result = makeTmpString(length); + char *ptr = result; + *ptr++ = '!'; + strcpy(ptr, left); + return result; + } + case op_or: + return asStringSubexpr(with_parens, '+'); + case op_and: + return asStringSubexpr(with_parens, '*'); + case op_xor: + return asStringSubexpr(with_parens, '^'); + case op_one: + return "1"; + case op_zero: + return "0"; + default: + return "?"; + } +} + +const char * +FuncExpr::asStringSubexpr(bool with_parens, + char op) const +{ + const char *left = left_->asString(true); + const char *right = right_->asString(true); + size_t length = strlen(left) + 1 + strlen(right) + 1; + if (with_parens) + length += 2; + char *result = makeTmpString(length); + char *r = result; + if (with_parens) + *r++= '('; + stringAppend(r, left); + *r++ = op; + stringAppend(r, right); + if (with_parens) + *r++ = ')'; + *r = '\0'; + return result; +} + +FuncExpr * +FuncExpr::bitSubExpr(int bit_offset) +{ + switch (op_) { + case op_port: + if (port_->hasMembers()) { + if (port_->size() == 1) { + LibertyPort *port = port_->findLibertyMember(0); + return makePort(port); + } + else { + LibertyPort *port = port_->findLibertyMember(bit_offset); + return makePort(port); + } + } + else + // Always copy so the subexpr doesn't share memory. + return makePort(port_); + case op_not: + return makeNot(left_->bitSubExpr(bit_offset)); + case op_or: + return makeOr(left_->bitSubExpr(bit_offset), + right_->bitSubExpr(bit_offset)); + case op_and: + return makeAnd(left_->bitSubExpr(bit_offset), + 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; + } + // Prevent warnings from lame compilers. + return nullptr; +} + +bool +FuncExpr::hasPort(const LibertyPort *port) const +{ + switch (op_) { + case op_port: + return (port_ == port); + case op_not: + return left_ && left_->hasPort(port); + case op_or: + case op_and: + case op_xor: + return (left_ && left_->hasPort(port)) + || (right_ && right_->hasPort(port)); + case op_one: + case op_zero: + return false; + } + // Prevent warnings from lame compilers. + return false; +} + +bool +FuncExpr::checkSize(LibertyPort *port) +{ + return checkSize(port->size()); +} + +bool +FuncExpr::checkSize(size_t size) +{ + size_t port_size; + switch (op_) { + case op_port: + port_size = port_->size(); + return !(port_size == size + || port_size == 1); + case op_not: + return left_->checkSize(size); + case op_or: + case op_and: + case op_xor: + return left_->checkSize(size) || right_->checkSize(size); + case op_one: + case op_zero: + return false; + } + // Prevent warnings from lame compilers. + return false; +} + +FuncExpr * +funcExprNot(FuncExpr *expr) +{ + if (expr->op() == FuncExpr::op_not) { + FuncExpr *not_expr = expr->left(); + delete expr; + return not_expr; + } + else + return FuncExpr::makeNot(expr); +} + +//////////////////////////////////////////////////////////////// + +FuncExprPortIterator::FuncExprPortIterator(FuncExpr *expr) +{ + findPorts(expr); + iter_.init(ports_); +} + +void +FuncExprPortIterator::findPorts(FuncExpr *expr) +{ + if (expr) { + if (expr->op() == FuncExpr::op_port) + ports_.insert(expr->port()); + else { + findPorts(expr->left()); + findPorts(expr->right()); + } + } +} + +bool +FuncExpr::equiv(const FuncExpr *expr1, + const FuncExpr *expr2) +{ + if (expr1 == nullptr && expr2 == nullptr) + return true; + else if (expr1 != nullptr && expr2 != nullptr + && expr1->op() == expr2->op()) { + switch (expr1->op()) { + case FuncExpr::op_port: + return LibertyPort::equiv(expr1->port(), expr2->port()); + case FuncExpr::op_not: + return equiv(expr1->left(), expr2->left()); + default: + return equiv(expr1->left(), expr2->left()) + && equiv(expr1->right(), expr2->right()); + } + } + else + return false; +} + +bool +FuncExpr::less(const FuncExpr *expr1, + const FuncExpr *expr2) +{ + if (expr1 != nullptr && expr2 != nullptr) { + Operator op1 = expr1->op(); + Operator 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()); + default: + if (equiv(expr1->left(), expr2->left())) + return less(expr1->right(), expr2->right()); + else + return less(expr1->left(), expr2->left()); + } + } + else + return op1 < op2; + } + else if (expr1 == nullptr && expr2 == nullptr) + return false; + else + return (expr1 == nullptr && expr2 != nullptr); +} + +} // namespace diff --git a/liberty/FuncExpr.hh b/liberty/FuncExpr.hh new file mode 100644 index 0000000..c22ef60 --- /dev/null +++ b/liberty/FuncExpr.hh @@ -0,0 +1,112 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_FUNC_EXPR_H +#define STA_FUNC_EXPR_H + +#include "DisallowCopyAssign.hh" +#include "Set.hh" +#include "NetworkClass.hh" +#include "LibertyClass.hh" + +namespace sta { + +class FuncExpr +{ +public: + enum Operator {op_port, + op_not, + op_or, + op_and, + op_xor, + op_one, + op_zero}; + + // Constructors. + static FuncExpr *makePort(LibertyPort *port); + static FuncExpr *makeNot(FuncExpr *expr); + static FuncExpr *makeAnd(FuncExpr *left, + FuncExpr *right); + static FuncExpr *makeOr(FuncExpr *left, + FuncExpr *right); + static FuncExpr *makeXor(FuncExpr *left, + FuncExpr *right); + static FuncExpr *makeZero(); + static FuncExpr *makeOne(); + static bool equiv(const FuncExpr *expr1, + const FuncExpr *expr2); + static bool less(const FuncExpr *expr1, + const FuncExpr *expr2); + + // Deep copy. + FuncExpr *copy(); + // Delete expression and all of its subexpressions. + void deleteSubexprs(); + // op == op_port + LibertyPort *port() const; + Operator op() const { return op_; } + // When operator is NOT left is the only operand. + FuncExpr *left() const { return left_; } + // nullptr when op == op_not + FuncExpr *right() const { return right_; } + TimingSense portTimingSense(const LibertyPort *port) const; + // Return true if expression has port as an input. + bool hasPort(const LibertyPort *port) const; + const char *asString() const; + // Sub expression for a bus function (bit_offset is 0 to bus->size()-1). + FuncExpr *bitSubExpr(int bit_offset); + // Check to make sure the function and port size are compatible. + // Return true if there is a mismatch. + bool checkSize(size_t size); + bool checkSize(LibertyPort *port); + +private: + DISALLOW_COPY_AND_ASSIGN(FuncExpr); + FuncExpr(Operator op, + FuncExpr *left, + FuncExpr *right, + LibertyPort *port); + const char *asString(bool with_parens) const; + const char *asStringSubexpr(bool with_parens, + char op) const; + + Operator op_; + FuncExpr *left_; + FuncExpr *right_; + LibertyPort *port_; +}; + +// Negate an expression. +FuncExpr * +funcExprNot(FuncExpr *expr); + +class FuncExprPortIterator : public Iterator +{ +public: + explicit FuncExprPortIterator(FuncExpr *expr); + virtual bool hasNext() { return iter_.hasNext(); } + virtual LibertyPort *next() { return iter_.next(); } + +private: + DISALLOW_COPY_AND_ASSIGN(FuncExprPortIterator); + void findPorts(FuncExpr *expr); + + LibertyPortSet ports_; + LibertyPortSet::ConstIterator iter_; +}; + +} // namespace +#endif diff --git a/liberty/InternalPower.cc b/liberty/InternalPower.cc new file mode 100644 index 0000000..6257e82 --- /dev/null +++ b/liberty/InternalPower.cc @@ -0,0 +1,233 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "FuncExpr.hh" +#include "TableModel.hh" +#include "Liberty.hh" +#include "InternalPower.hh" + +namespace sta { + +InternalPowerAttrs::InternalPowerAttrs() : + when_(nullptr), + models_{nullptr, nullptr}, + related_pg_pin_(nullptr) +{ +} + +InternalPowerAttrs::~InternalPowerAttrs() +{ +} + +void +InternalPowerAttrs::deleteContents() +{ + for (auto tr_index : TransRiseFall::rangeIndex()) { + InternalPowerModel *model = models_[tr_index]; + if (model) + delete model; + } + if (when_) + when_->deleteSubexprs(); + stringDelete(related_pg_pin_); +} + +InternalPowerModel * +InternalPowerAttrs::model(TransRiseFall *tr) const +{ + return models_[tr->index()]; +} + +void +InternalPowerAttrs::setModel(TransRiseFall *tr, + InternalPowerModel *model) +{ + models_[tr->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) : + port_(port), + related_port_(related_port), + when_(attrs->when()), + related_pg_pin_(attrs->relatedPgPin()) +{ + for (auto tr : TransRiseFall::range()) { + int tr_index = tr->index(); + models_[tr_index] = attrs->model(tr); + } + cell->addInternalPower(this); +} + +InternalPower::~InternalPower() +{ + // models_, when_ and related_pg_pin_ are owned by InternalPowerAttrs. +} + +LibertyCell * +InternalPower::libertyCell() const +{ + return port_->libertyCell(); +} + +float +InternalPower::power(TransRiseFall *tr, + const Pvt *pvt, + float in_slew, + float load_cap) +{ + InternalPowerModel *model = models_[tr->index()]; + if (model) + return model->power(libertyCell(), pvt, in_slew, load_cap); + else + return 0.0; +} + +//////////////////////////////////////////////////////////////// + +InternalPowerModel::InternalPowerModel(TableModel *model) : + model_(model) +{ +} + +InternalPowerModel::~InternalPowerModel() +{ + delete model_; +} + +float +InternalPowerModel::power(const LibertyCell *cell, + 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); + const LibertyLibrary *library = cell->libertyLibrary(); + return model_->findValue(library, cell, pvt, + axis_value1, axis_value2, axis_value3); + } + else + return 0.0; +} + +void +InternalPowerModel::reportPower(const LibertyCell *cell, + const Pvt *pvt, + float in_slew, + float load_cap, + int digits, + string *result) const +{ + if (model_) { + float axis_value1, axis_value2, axis_value3; + findAxisValues(in_slew, load_cap, + axis_value1, axis_value2, axis_value3); + const LibertyLibrary *library = cell->libertyLibrary(); + model_->reportValue("Power", library, cell, pvt, + axis_value1, nullptr, axis_value2, axis_value3, digits, result); + } +} + +void +InternalPowerModel::findAxisValues(float in_slew, + float load_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); + axis_value2 = 0.0; + axis_value3 = 0.0; + break; + case 2: + axis_value1 = axisValue(model_->axis1(), in_slew, load_cap); + axis_value2 = axisValue(model_->axis2(), in_slew, load_cap); + axis_value3 = 0.0; + break; + case 3: + axis_value1 = axisValue(model_->axis1(), in_slew, load_cap); + axis_value2 = axisValue(model_->axis2(), in_slew, load_cap); + axis_value3 = axisValue(model_->axis3(), in_slew, load_cap); + break; + default: + internalError("unsupported table order"); + } +} + +float +InternalPowerModel::axisValue(TableAxis *axis, + float in_slew, + float load_cap) const +{ + TableAxisVariable var = axis->variable(); + if (var == TableAxisVariable::input_transition_time) + return in_slew; + else if (var == TableAxisVariable::total_output_net_capacitance) + return load_cap; + else { + internalError("unsupported table axes"); + return 0.0; + } +} + +bool +InternalPowerModel::checkAxes(const TableModel *model) +{ + TableAxis *axis1 = model->axis1(); + TableAxis *axis2 = model->axis2(); + TableAxis *axis3 = model->axis3(); + bool axis_ok = true; + if (axis1) + axis_ok &= checkAxis(model->axis1()); + if (axis2) + axis_ok &= checkAxis(model->axis2()); + axis_ok &= (axis3 == nullptr); + return axis_ok; +} + +bool +InternalPowerModel::checkAxis(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; +} + +} // namespace diff --git a/liberty/InternalPower.hh b/liberty/InternalPower.hh new file mode 100644 index 0000000..13ccc1f --- /dev/null +++ b/liberty/InternalPower.hh @@ -0,0 +1,117 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_INTERNAL_POWER_H +#define STA_INTERNAL_POWER_H + +#include "DisallowCopyAssign.hh" +#include "Transition.hh" +#include "LibertyClass.hh" + +namespace sta { + +class InternalPowerAttrs; +class InternalPowerModel; + +class InternalPowerAttrs +{ +public: + InternalPowerAttrs(); + virtual ~InternalPowerAttrs(); + void deleteContents(); + FuncExpr *when() const { return when_; } + FuncExpr *&whenRef() { return when_; } + void setModel(TransRiseFall *tr, + InternalPowerModel *model); + InternalPowerModel *model(TransRiseFall *tr) const; + const char *relatedPgPin() const { return related_pg_pin_; } + void setRelatedPgPin(const char *related_pg_pin); + +protected: + FuncExpr *when_; + InternalPowerModel *models_[TransRiseFall::index_count]; + const char *related_pg_pin_; + +private: + DISALLOW_COPY_AND_ASSIGN(InternalPowerAttrs); +}; + +class InternalPower +{ +public: + InternalPower(LibertyCell *cell, + LibertyPort *port, + LibertyPort *related_port, + InternalPowerAttrs *attrs); + ~InternalPower(); + 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_; } + float power(TransRiseFall *tr, + const Pvt *pvt, + float in_slew, + float load_cap); + +protected: + LibertyPort *port_; + LibertyPort *related_port_; + FuncExpr *when_; + const char *related_pg_pin_; + InternalPowerModel *models_[TransRiseFall::index_count]; + +private: + DISALLOW_COPY_AND_ASSIGN(InternalPower); +}; + +class InternalPowerModel +{ +public: + explicit InternalPowerModel(TableModel *model); + ~InternalPowerModel(); + float power(const LibertyCell *cell, + const Pvt *pvt, + float in_slew, + float load_cap) const; + void reportPower(const LibertyCell *cell, + const Pvt *pvt, + float in_slew, + float load_cap, + int digits, + string *result) const; + +protected: + void findAxisValues(float in_slew, + float load_cap, + // Return values. + float &axis_value1, + float &axis_value2, + float &axis_value3) const; + float axisValue(TableAxis *axis, + float in_slew, + float load_cap) const; + bool checkAxes(const TableModel *model); + bool checkAxis(TableAxis *axis); + + TableModel *model_; + +private: + DISALLOW_COPY_AND_ASSIGN(InternalPowerModel); +}; + +} // namespace +#endif diff --git a/liberty/LeakagePower.cc b/liberty/LeakagePower.cc new file mode 100644 index 0000000..25be739 --- /dev/null +++ b/liberty/LeakagePower.cc @@ -0,0 +1,54 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "FuncExpr.hh" +#include "TableModel.hh" +#include "Liberty.hh" +#include "LeakagePower.hh" + +namespace sta { + +LeakagePowerAttrs::LeakagePowerAttrs() : + when_(nullptr), + power_(0.0) +{ +} + +void +LeakagePowerAttrs::setPower(float power) +{ + power_ = power; +} + +//////////////////////////////////////////////////////////////// + +LeakagePower::LeakagePower(LibertyCell *cell, + LeakagePowerAttrs *attrs) : + cell_(cell), + when_(attrs->when()), + power_(attrs->power()) +{ + cell->addLeakagePower(this); +} + +LeakagePower::~LeakagePower() +{ + if (when_) + when_->deleteSubexprs(); +} + +} // namespace diff --git a/liberty/LeakagePower.hh b/liberty/LeakagePower.hh new file mode 100644 index 0000000..c65e696 --- /dev/null +++ b/liberty/LeakagePower.hh @@ -0,0 +1,64 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_LEAKAGE_POWER_H +#define STA_LEAKAGE_POWER_H + +#include "DisallowCopyAssign.hh" +#include "LibertyClass.hh" + +namespace sta { + +class LeakagePowerAttrs; + +class LeakagePowerAttrs +{ +public: + LeakagePowerAttrs(); + FuncExpr *when() const { return when_; } + FuncExpr *&whenRef() { return when_; } + float power() { return power_; } + void setPower(float power); + +protected: + FuncExpr *when_; + float power_; + +private: + DISALLOW_COPY_AND_ASSIGN(LeakagePowerAttrs); +}; + +class LeakagePower +{ +public: + LeakagePower(LibertyCell *cell, + LeakagePowerAttrs *attrs); + ~LeakagePower(); + LibertyCell *libertyCell() const { return cell_; } + FuncExpr *when() const { return when_; } + float power() { return power_; } + +protected: + LibertyCell *cell_; + FuncExpr *when_; + float power_; + +private: + DISALLOW_COPY_AND_ASSIGN(LeakagePower); +}; + +} // namespace +#endif diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc new file mode 100644 index 0000000..57f9f38 --- /dev/null +++ b/liberty/Liberty.cc @@ -0,0 +1,2782 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "EnumNameMap.hh" +#include "Report.hh" +#include "Debug.hh" +#include "Error.hh" +#include "StringUtil.hh" +#include "StringSet.hh" +#include "PatternMatch.hh" +#include "Units.hh" +#include "PortDirection.hh" +#include "Transition.hh" +#include "TimingRole.hh" +#include "FuncExpr.hh" +#include "TableModel.hh" +#include "TimingArc.hh" +#include "InternalPower.hh" +#include "LeakagePower.hh" +#include "Sequential.hh" +#include "Wireload.hh" +#include "EquivCells.hh" +#include "Network.hh" +#include "Liberty.hh" + +namespace sta { + +typedef Set TimingModelSet; +typedef Set FuncExprSet; +typedef Set LatchEnableSet; + +bool LibertyLibrary::found_rise_fall_caps_ = false; + +void +initLiberty() +{ + TimingArcSet::init(); +} + +void +deleteLiberty() +{ + TimingArcSet::destroy(); +} + +LibertyLibrary::LibertyLibrary(const char *name, + const char *filename) : + ConcreteLibrary(name, filename, true), + units_(new Units()), + delay_model_type_(DelayModelType::cmos_linear), // 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_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) +{ + // Scalar templates are builtin. + for (int i = 0; i != int(TableTemplateType::count); i++) { + TableTemplateType type = static_cast(i); + TableTemplate *scalar_template = new TableTemplate("scalar", nullptr, + nullptr, nullptr); + addTableTemplate(scalar_template, type); + } + + for (auto tr_index : TransRiseFall::rangeIndex()) { + wire_slew_degradation_tbls_[tr_index] = nullptr; + input_threshold_[tr_index] = input_threshold_default_; + output_threshold_[tr_index] = output_threshold_default_; + slew_lower_threshold_[tr_index] = slew_lower_threshold_default_; + slew_upper_threshold_[tr_index] = slew_upper_threshold_default_; + } +} + +LibertyLibrary::~LibertyLibrary() +{ + bus_dcls_.deleteContents(); + for (int i = 0; i < int(TableTemplateType::count); i++) + template_maps_[i].deleteContents(); + scale_factors_map_.deleteContents(); + delete scale_factors_; + + for (auto tr_index : TransRiseFall::rangeIndex()) { + TableModel *model = wire_slew_degradation_tbls_[tr_index]; + delete model; + } + operating_conditions_.deleteContents(); + wireloads_.deleteContents(); + wire_load_selections_.deleteContents(); + delete units_; + ocv_derate_map_.deleteContents(); + + for (auto name_volt : supply_voltage_map_) { + const char *supply_name = name_volt.first; + stringDelete(supply_name); + } + delete buffers_; +} + +LibertyCell * +LibertyLibrary::findLibertyCell(const char *name) const +{ + return static_cast(findCell(name)); +} + +void +LibertyLibrary::findLibertyCellsMatching(PatternMatch *pattern, + LibertyCellSeq *cells) +{ + LibertyCellIterator cell_iter(this); + while (cell_iter.hasNext()) { + LibertyCell *cell = cell_iter.next(); + if (pattern->match(cell->name())) + cells->push_back(cell); + } +} + +LibertyCellSeq * +LibertyLibrary::buffers() +{ + if (buffers_ == nullptr) { + buffers_ = new LibertyCellSeq; + LibertyCellIterator cell_iter(this); + while (cell_iter.hasNext()) { + LibertyCell *cell = cell_iter.next(); + if (!cell->dontUse() + && cell->isBuffer()) + buffers_->push_back(cell); + } + } + return buffers_; +} + +void +LibertyLibrary::setDelayModelType(DelayModelType type) +{ + delay_model_type_ = type; +} + +void +LibertyLibrary::addBusDcl(BusDcl *bus_dcl) +{ + bus_dcls_[bus_dcl->name()] = bus_dcl; +} + +BusDcl * +LibertyLibrary::findBusDcl(const char *name) const +{ + return bus_dcls_.findKey(name); +} + +void +LibertyLibrary::addTableTemplate(TableTemplate *tbl_template, + TableTemplateType type) +{ + template_maps_[int(type)][tbl_template->name()] = tbl_template; +} + +TableTemplate * +LibertyLibrary::findTableTemplate(const char *name, + TableTemplateType type) +{ + return template_maps_[int(type)][name]; +} + +void +LibertyLibrary::setNominalProcess(float process) +{ + nominal_process_ = process; +} + +void +LibertyLibrary::setNominalVoltage(float voltage) +{ + nominal_voltage_ = voltage; +} + +void +LibertyLibrary::setNominalTemperature(float temperature) +{ + nominal_temperature_ = temperature; +} + +void +LibertyLibrary::setScaleFactors(ScaleFactors *scales) +{ + scale_factors_ = scales; +} + +void +LibertyLibrary::addScaleFactors(ScaleFactors *scales) +{ + scale_factors_map_[scales->name()] = scales; +} + +ScaleFactors * +LibertyLibrary::findScaleFactors(const char *name) +{ + return scale_factors_map_[name]; +} + +float +LibertyLibrary::scaleFactor(ScaleFactorType type, + const Pvt *pvt) const +{ + return scaleFactor(type, 0, nullptr, pvt); +} + +float +LibertyLibrary::scaleFactor(ScaleFactorType type, + const LibertyCell *cell, + const Pvt *pvt) const +{ + return scaleFactor(type, 0, cell, pvt); +} + +float +LibertyLibrary::scaleFactor(ScaleFactorType type, + int tr_index, + const LibertyCell *cell, + const Pvt *pvt) const +{ + if (pvt == nullptr) + pvt = default_operating_conditions_; + // If there is no operating condition, nominal pvt values are used. + // All scale factors are unity for nominal pvt. + if (pvt) { + ScaleFactors *scale_factors = nullptr; + // Cell level scale factors have precidence over library scale factors. + if (cell) + scale_factors = cell->scaleFactors(); + if (scale_factors == nullptr) + scale_factors = scale_factors_; + if (scale_factors) { + float process_scale = 1.0F + (pvt->process() - nominal_process_) + * scale_factors->scale(type, ScaleFactorPvt::process, tr_index); + float temp_scale = 1.0F + (pvt->temperature() - nominal_temperature_) + * scale_factors->scale(type, ScaleFactorPvt::temp, tr_index); + float volt_scale = 1.0F + (pvt->voltage() - nominal_voltage_) + * scale_factors->scale(type, ScaleFactorPvt::volt, tr_index); + float scale = process_scale * temp_scale * volt_scale; + return scale; + } + } + return 1.0F; +} + +void +LibertyLibrary::setWireSlewDegradationTable(TableModel *model, + TransRiseFall *tr) +{ + int tr_index = tr->index(); + if (wire_slew_degradation_tbls_[tr_index]) + delete wire_slew_degradation_tbls_[tr_index]; + wire_slew_degradation_tbls_[tr_index] = model; +} + +TableModel * +LibertyLibrary::wireSlewDegradationTable(const TransRiseFall *tr) const +{ + return wire_slew_degradation_tbls_[tr->index()]; +} + +float +LibertyLibrary::degradeWireSlew(const LibertyCell *cell, + const TransRiseFall *tr, + const Pvt *pvt, + float in_slew, + float wire_delay) const +{ + const TableModel *model = wireSlewDegradationTable(tr); + if (model) + return degradeWireSlew(cell, pvt, model, in_slew, wire_delay); + else + return in_slew; +} + +float +LibertyLibrary::degradeWireSlew(const LibertyCell *cell, + const Pvt *pvt, + const TableModel *model, + float in_slew, + float wire_delay) const +{ + switch (model->order()) { + case 0: + return model->findValue(this, cell, pvt, 0.0, 0.0, 0.0); + case 1: { + TableAxis *axis1 = model->axis1(); + TableAxisVariable var1 = axis1->variable(); + if (var1 == TableAxisVariable::output_pin_transition) + return model->findValue(this, cell, pvt, in_slew, 0.0, 0.0); + else if (var1 == TableAxisVariable::connect_delay) + return model->findValue(this, cell, pvt, wire_delay, 0.0, 0.0); + else { + internalError("unsupported slew degradation table axes"); + return 0.0; + } + } + case 2: { + TableAxis *axis1 = model->axis1(); + TableAxis *axis2 = model->axis2(); + TableAxisVariable var1 = axis1->variable(); + TableAxisVariable var2 = axis2->variable(); + if (var1 == TableAxisVariable::output_pin_transition + && var2 == TableAxisVariable::connect_delay) + return model->findValue(this, cell, pvt, in_slew, wire_delay, 0.0); + else if (var1 == TableAxisVariable::connect_delay + && var2 == TableAxisVariable::output_pin_transition) + return model->findValue(this, cell, pvt, wire_delay, in_slew, 0.0); + else { + internalError("unsupported slew degradation table axes"); + return 0.0; + } + } + default: + internalError("unsupported slew degradation table order"); + return 0.0; + } +} + +// Check for supported axis variables. +// Return true if axes are supported. +bool +LibertyLibrary::checkSlewDegradationAxes(Table *table) +{ + switch (table->order()) { + case 0: + return true; + case 1: { + TableAxis *axis1 = table->axis1(); + TableAxisVariable var1 = axis1->variable(); + return var1 == TableAxisVariable::output_pin_transition + || var1 == TableAxisVariable::connect_delay; + } + case 2: { + TableAxis *axis1 = table->axis1(); + TableAxis *axis2 = table->axis2(); + TableAxisVariable var1 = axis1->variable(); + TableAxisVariable var2 = axis2->variable(); + return (var1 == TableAxisVariable::output_pin_transition + && var2 == TableAxisVariable::connect_delay) + || (var1 == TableAxisVariable::connect_delay + && var2 == TableAxisVariable::output_pin_transition); + } + default: + internalError("unsupported slew degradation table axes"); + return 0.0; + } +} + +void +LibertyLibrary::defaultMaxFanout(float &fanout, + bool &exists) const +{ + fanout = default_max_fanout_; + exists = default_max_fanout_exists_; +} + +void +LibertyLibrary::setDefaultMaxFanout(float fanout) +{ + default_max_fanout_ = fanout; + default_max_fanout_exists_ = true; +} + +void +LibertyLibrary::defaultMaxSlew(float &slew, + bool &exists) const +{ + slew = default_max_slew_; + exists = default_max_slew_exists_; +} + +void +LibertyLibrary::setDefaultMaxSlew(float slew) +{ + default_max_slew_ = slew; + default_max_slew_exists_ = true; +} + +void +LibertyLibrary::defaultMaxCapacitance(float &cap, + bool &exists) const +{ + cap = default_max_cap_; + exists = default_max_cap_exists_; +} + +void +LibertyLibrary::setDefaultMaxCapacitance(float cap) +{ + default_max_cap_ = cap; + default_max_cap_exists_ = true; +} + +void +LibertyLibrary::setDefaultFanoutLoad(float load) +{ + default_fanout_load_ = load; +} + +void +LibertyLibrary::setDefaultBidirectPinCap(float cap) +{ + default_bidirect_pin_cap_ = cap; +} + +void +LibertyLibrary::setDefaultInputPinCap(float cap) +{ + default_input_pin_cap_ = cap; +} + +void +LibertyLibrary::setDefaultOutputPinCap(float cap) +{ + default_output_pin_cap_ = cap; +} + +void +LibertyLibrary::defaultIntrinsic(const TransRiseFall *tr, + // Return values. + float &intrinsic, + bool &exists) const +{ + default_intrinsic_.value(tr, intrinsic, exists); +} + +void +LibertyLibrary::setDefaultIntrinsic(const TransRiseFall *tr, + float value) +{ + default_intrinsic_.setValue(tr, value); +} + +void +LibertyLibrary::defaultPinResistance(const TransRiseFall *tr, + const PortDirection *dir, + // Return values. + float &res, + bool &exists) const +{ + if (dir->isAnyTristate()) + defaultBidirectPinRes(tr, res, exists); + else + defaultOutputPinRes(tr, res, exists); +} + +void +LibertyLibrary::defaultBidirectPinRes(const TransRiseFall *tr, + // Return values. + float &res, + bool &exists) const +{ + return default_inout_pin_res_.value(tr, res, exists); +} + +void +LibertyLibrary::setDefaultBidirectPinRes(const TransRiseFall *tr, + float value) +{ + default_inout_pin_res_.setValue(tr, value); +} + +void +LibertyLibrary::defaultOutputPinRes(const TransRiseFall *tr, + // Return values. + float &res, + bool &exists) const +{ + default_output_pin_res_.value(tr, res, exists); +} + +void +LibertyLibrary::setDefaultOutputPinRes(const TransRiseFall *tr, + float value) +{ + default_output_pin_res_.setValue(tr, value); +} + +void +LibertyLibrary::addWireload(Wireload *wireload) +{ + wireloads_[wireload->name()] = wireload; +} + +Wireload * +LibertyLibrary::findWireload(const char *name) const +{ + return wireloads_.findKey(name); +} + +void +LibertyLibrary::setDefaultWireload(Wireload *wireload) +{ + default_wire_load_ = wireload; +} + +Wireload * +LibertyLibrary::defaultWireload() const +{ + return default_wire_load_; +} + +void +LibertyLibrary::addWireloadSelection(WireloadSelection *selection) +{ + wire_load_selections_[selection->name()] = selection; +} + +WireloadSelection * +LibertyLibrary::findWireloadSelection(const char *name) const +{ + return wire_load_selections_.findKey(name); +} + +WireloadSelection * +LibertyLibrary::defaultWireloadSelection() const +{ + return default_wire_load_selection_; +} + +void +LibertyLibrary::setDefaultWireloadSelection(WireloadSelection *selection) +{ + default_wire_load_selection_ = selection; +} + +WireloadMode +LibertyLibrary::defaultWireloadMode() const +{ + return default_wire_load_mode_; +} + +void +LibertyLibrary::setDefaultWireloadMode(WireloadMode mode) +{ + default_wire_load_mode_ = mode; +} + +void +LibertyLibrary::addOperatingConditions(OperatingConditions *op_cond) +{ + operating_conditions_[op_cond->name()] = op_cond; +} + +OperatingConditions * +LibertyLibrary::findOperatingConditions(const char *name) +{ + return operating_conditions_.findKey(name); +} + +OperatingConditions * +LibertyLibrary::defaultOperatingConditions() const +{ + return default_operating_conditions_; +} + +void +LibertyLibrary::setDefaultOperatingConditions(OperatingConditions *op_cond) +{ + default_operating_conditions_ = op_cond; +} + +float +LibertyLibrary::inputThreshold(const TransRiseFall *tr) const +{ + return input_threshold_[tr->index()]; +} + +void +LibertyLibrary::setInputThreshold(const TransRiseFall *tr, + float th) +{ + input_threshold_[tr->index()] = th; +} + +float +LibertyLibrary::outputThreshold(const TransRiseFall *tr) const +{ + return output_threshold_[tr->index()]; +} + +void +LibertyLibrary::setOutputThreshold(const TransRiseFall *tr, + float th) +{ + output_threshold_[tr->index()] = th; +} + +float +LibertyLibrary::slewLowerThreshold(const TransRiseFall *tr) const +{ + return slew_lower_threshold_[tr->index()]; +} + +void +LibertyLibrary::setSlewLowerThreshold(const TransRiseFall *tr, + float th) +{ + slew_lower_threshold_[tr->index()] = th; +} + +float +LibertyLibrary::slewUpperThreshold(const TransRiseFall *tr) const +{ + return slew_upper_threshold_[tr->index()]; +} + +void +LibertyLibrary::setSlewUpperThreshold(const TransRiseFall *tr, + float th) +{ + slew_upper_threshold_[tr->index()] = th; +} + +float +LibertyLibrary::slewDerateFromLibrary() const +{ + return slew_derate_from_library_; +} + +void +LibertyLibrary::setSlewDerateFromLibrary(float derate) +{ + slew_derate_from_library_ = derate; +} + +LibertyCell * +LibertyLibrary::makeScaledCell(const char *name, + const char *filename) +{ + LibertyCell *cell = new LibertyCell(this, name, filename); + return cell; +} + +//////////////////////////////////////////////////////////////// + +void +LibertyLibrary::makeCornerMap(LibertyLibrary *lib, + int ap_index, + 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); + if (link_cell) + makeCornerMap(link_cell, cell, ap_index, report); + } +} + +// Map a cell linked in the network to the corresponding liberty cell +// to use for delay calculation at a corner. +void +LibertyLibrary::makeCornerMap(LibertyCell *link_cell, + LibertyCell *corner_cell, + int ap_index, + 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); +} + +void +LibertyLibrary::makeCornerMap(LibertyCell *cell1, + LibertyCell *cell2, + bool link, + int ap_index, + Report *report) +{ + LibertyCellPortBitIterator port_iter1(cell1); + while (port_iter1.hasNext()) { + LibertyPort *port1 = port_iter1.next(); + const char *port_name = port1->name(); + LibertyPort *port2 = cell2->findLibertyPort(port_name); + if (port2) { + if (link) + port1->setCornerPort(port2, ap_index); + } + else + report->warn("cell %s/%s port %s not found in cell %s/%s.\n", + cell1->library()->name(), + cell1->name(), + port_name, + cell2->library()->name(), + cell2->name()); + } + + for (auto arc_set1 : cell1->timing_arc_sets_) { + auto arc_set2 = cell2->findTimingArcSet(arc_set1); + if (arc_set2) { + if (link) { + TimingArcSetArcIterator arc_iter1(arc_set1); + TimingArcSetArcIterator arc_iter2(arc_set2); + while (arc_iter1.hasNext() && arc_iter2.hasNext()) { + TimingArc *arc1 = arc_iter1.next(); + TimingArc *arc2 = arc_iter2.next(); + if (TimingArc::equiv(arc1, arc2)) + arc1->setCornerArc(arc2, ap_index); + } + } + } + else + report->warn("cell %s/%s %s -> %s timing group %s not found in cell %s/%s.\n", + cell1->library()->name(), + cell1->name(), + arc_set1->from()->name(), + arc_set1->to()->name(), + arc_set1->role()->asString(), + cell2->library()->name(), + cell2->name()); + } +} + +//////////////////////////////////////////////////////////////// + +float +LibertyLibrary::ocvArcDepth() const +{ + return ocv_arc_depth_; +} + +void +LibertyLibrary::setOcvArcDepth(float depth) +{ + ocv_arc_depth_ = depth; +} + +OcvDerate * +LibertyLibrary::defaultOcvDerate() const +{ + return default_ocv_derate_; +} + +void +LibertyLibrary::setDefaultOcvDerate(OcvDerate *derate) +{ + default_ocv_derate_ = derate; +} + +OcvDerate * +LibertyLibrary::findOcvDerate(const char *derate_name) +{ + return ocv_derate_map_.findKey(derate_name); +} + +void +LibertyLibrary::addOcvDerate(OcvDerate *derate) +{ + ocv_derate_map_[derate->name()] = derate; +} + +void +LibertyLibrary::addSupplyVoltage(const char *supply_name, + float voltage) +{ + supply_voltage_map_[stringCopy(supply_name)] = voltage; +} + +void +LibertyLibrary::supplyVoltage(const char *supply_name, + // Return value. + float &voltage, + bool &exists) const +{ + supply_voltage_map_.findKey(supply_name, voltage, exists); +} + +bool +LibertyLibrary::supplyExists(const char *supply_name) const +{ + return supply_voltage_map_.hasKey(supply_name); +} + +//////////////////////////////////////////////////////////////// + +LibertyCellIterator::LibertyCellIterator(const LibertyLibrary * + library): + iter_(library->cell_map_) +{ +} + +bool +LibertyCellIterator::hasNext() +{ + return iter_.hasNext(); +} + +LibertyCell * +LibertyCellIterator::next() +{ + return static_cast(iter_.next()); +} + +//////////////////////////////////////////////////////////////// + +LibertyCell::LibertyCell(LibertyLibrary *library, + const char *name, + const char *filename) : + ConcreteCell(library, name, true, filename), + liberty_library_(library), + area_(0.0), + dont_use_(false), + is_macro_(false), + is_pad_(false), + has_internal_ports_(false), + interface_timing_(false), + clock_gate_type_(ClockGateType::none), + has_infered_reg_timing_arcs_(false), + 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) +{ + liberty_cell_ = this; +} + +LibertyCell::~LibertyCell() +{ + mode_defs_.deleteContents(); + latch_d_to_q_map_.deleteContents(); + + deleteTimingArcAttrs(); + 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(); + bus_dcls_.deleteContents(); + scaled_cells_.deleteContents(); + + delete test_cell_; + ocv_derate_map_.deleteContents(); + + pg_port_map_.deleteContents(); +} + +void +LibertyCell::deleteTimingArcAttrs() +{ + for (auto attrs : timing_arc_attrs_) { + attrs->deleteContents(); + delete attrs; + } +} + +LibertyPort * +LibertyCell::findLibertyPort(const char *name) const +{ + return static_cast(findPort(name)); +} + +void +LibertyCell::findLibertyPortsMatching(PatternMatch *pattern, + LibertyPortSeq *ports) const +{ + LibertyCellPortIterator port_iter(this); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + if (pattern->match(port->name())) + ports->push_back(port); + } +} + +void +LibertyCell::addPort(ConcretePort *port) +{ + ConcreteCell::addPort(port); + if (port->direction()->isInternal()) + has_internal_ports_ = true; +} + + +void +LibertyCell::setHasInternalPorts(bool has_internal) +{ + has_internal_ports_ = has_internal; +} + +void +LibertyCell::addPgPort(LibertyPgPort *pg_port) +{ + pg_port_map_[pg_port->name()] = pg_port; +} + +LibertyPgPort * +LibertyCell::findPgPort(const char *name) +{ + return pg_port_map_.findKey(name); +} + +ModeDef * +LibertyCell::makeModeDef(const char *name) +{ + ModeDef *mode = new ModeDef(name); + mode_defs_[mode->name()] = mode; + return mode; +} + +ModeDef * +LibertyCell::findModeDef(const char *name) +{ + return mode_defs_.findKey(name); +} + +void +LibertyCell::setScaleFactors(ScaleFactors *scale_factors) +{ + scale_factors_ = scale_factors; +} + +void +LibertyCell::addBusDcl(BusDcl *bus_dcl) +{ + bus_dcls_[bus_dcl->name()] = bus_dcl; +} + +BusDcl * +LibertyCell::findBusDcl(const char *name) const +{ + return bus_dcls_.findKey(name); +} + +void +LibertyCell::setArea(float area) +{ + area_ = area; +} + +void +LibertyCell::setDontUse(bool dont_use) +{ + dont_use_ = dont_use; +} + +void +LibertyCell::setIsMacro(bool is_macro) +{ + is_macro_ = is_macro; +} + +void +LibertyCell::LibertyCell::setIsPad(bool is_pad) +{ + is_pad_ = is_pad; +} + +void +LibertyCell::setInterfaceTiming(bool value) +{ + interface_timing_ = value; +} + +bool +LibertyCell::isClockGateLatchPosedge() const +{ + return clock_gate_type_ == ClockGateType::latch_posedge; +} + +bool +LibertyCell::isClockGateLatchNegedge() const +{ + return clock_gate_type_ == ClockGateType::latch_posedge; +} + +bool +LibertyCell::isClockGateOther() const +{ + return clock_gate_type_ == ClockGateType::other; +} + +bool +LibertyCell::isClockGate() const +{ + return clock_gate_type_ != ClockGateType::none; +} + +void +LibertyCell::setClockGateType(ClockGateType type) +{ + clock_gate_type_ = type; +} + +bool +LibertyCell::isBuffer() const +{ + if (ports_.size() == 2) { + LibertyPort *port1 = static_cast(ports_[0]); + LibertyPort *port2 = static_cast(ports_[1]); + return (port1->direction()->isInput() + && port2->direction()->isOutput() + && hasBufferFunc(port1, port2)) + || (port2->direction()->isInput() + && port1->direction()->isOutput() + && hasBufferFunc(port2, port1)); + } + return false; +} + +bool +LibertyCell::hasBufferFunc(const LibertyPort *input, + const LibertyPort *output) const +{ + FuncExpr *func = output->function(); + return func + && func->op() == FuncExpr::op_port + && func->port() == input; +} + +void +LibertyCell::bufferPorts(// Return values. + LibertyPort *&input, + LibertyPort *&output) +{ + LibertyPort *port1 = static_cast(ports_[0]); + LibertyPort *port2 = static_cast(ports_[1]); + if (port1->direction()->isInput() + && port2->direction()->isOutput()) { + input = port1; + output = port2; + } + else { + input = port2; + output = port1; + } +} + +unsigned +LibertyCell::addTimingArcSet(TimingArcSet *arc_set) +{ + int set_index = timing_arc_sets_.size(); + if (set_index > timing_arc_set_index_max) + internalError("timing arc set max index exceeded"); + timing_arc_sets_.push_back(arc_set); + + LibertyPort *from = arc_set->from(); + TimingRole *role = arc_set->role(); + if (role == TimingRole::regClkToQ() + || role == TimingRole::latchEnToQ()) + from->setIsRegClk(true); + if (role->isTimingCheck()) + from->setIsCheckClk(true); + return set_index; +} + +void +LibertyCell::addTimingArcAttrs(TimingArcAttrs *attrs) +{ + timing_arc_attrs_.push_back(attrs); +} + +void +LibertyCell::addInternalPower(InternalPower *power) +{ + internal_powers_.push_back(power); +} + +void +LibertyCell::addInternalPowerAttrs(InternalPowerAttrs *attrs) +{ + internal_power_attrs_.push_back(attrs); +} + +void +LibertyCell::deleteInternalPowerAttrs() +{ + for (auto attrs : internal_power_attrs_) { + attrs->deleteContents(); + delete attrs; + } +} + +void +LibertyCell::addLeakagePower(LeakagePower *power) +{ + leakage_powers_.push_back(power); +} + +void +LibertyCell::setLeakagePower(float leakage) +{ + leakage_power_ = leakage; + leakage_power_exists_ = true; +} + +void +LibertyCell::leakagePower(// Return values. + float &leakage, + bool &exists) const +{ + leakage = leakage_power_; + exists = leakage_power_exists_; +} + +void +LibertyCell::finish(bool infer_latches, + Report *report, + Debug *debug) +{ + translatePresetClrCheckRoles(); + makeTimingArcMap(report); + makeTimingArcPortMaps(); + findDefaultCondArcs(); + makeLatchEnables(report, debug); + if (infer_latches + && !interface_timing_) + inferLatchRoles(debug); +} + +void +LibertyCell::findDefaultCondArcs() +{ + for (auto port_pair_set : port_timing_arc_set_map_) { + TimingArcSetSeq *sets = port_pair_set.second; + bool has_cond_arcs = false; + for (auto set : *sets) { + if (set->cond()) { + has_cond_arcs = true; + break; + } + } + if (has_cond_arcs) { + for (auto set : *sets) { + if (!set->cond()) + set->setIsCondDefault(true); + } + } + } +} + +// Timing checks for set/clear pins use setup/hold times. This +// changes their roles to recovery/removal by finding the set/clear +// pins and then translating the timing check roles. +void +LibertyCell::translatePresetClrCheckRoles() +{ + LibertyPortSet pre_clr_ports; + for (auto arc_set : timing_arc_sets_) { + if (arc_set->role() == TimingRole::regSetClr()) + pre_clr_ports.insert(arc_set->from()); + } + + 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()); + } + } + } +} + +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.\n", + // 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()) + internalError("timing arc count mismatch\n"); +} + +void +LibertyCell::makeTimingArcPortMaps() +{ + for (auto 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; + } + 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); + + sets = timing_arc_set_to_map_.findKey(to); + if (sets == nullptr) { + sets = new TimingArcSetSeq; + timing_arc_set_to_map_[to] = sets; + } + sets->push_back(arc_set); + } +} + +TimingArcSetSeq * +LibertyCell::timingArcSets(const LibertyPort *from, + const LibertyPort *to) const +{ + if (from && to) { + LibertyPortPair port_pair(from, to); + return port_timing_arc_set_map_.findKey(port_pair); + } + else if (from) + return timing_arc_set_from_map_.findKey(from); + else if (to) + return timing_arc_set_to_map_.findKey(to); + else + return nullptr; +} + +TimingArcSet * +LibertyCell::findTimingArcSet(TimingArcSet *key) const +{ + return timing_arc_set_map_.findKey(key); +} + +TimingArcSet * +LibertyCell::findTimingArcSet(unsigned arc_set_index) const +{ + return timing_arc_sets_[arc_set_index]; +} + +size_t +LibertyCell::timingArcSetCount() const +{ + return timing_arc_sets_.size(); +} + +bool +LibertyCell::hasTimingArcs(LibertyPort *port) const +{ + return timing_arc_set_from_map_.findKey(port) + || timing_arc_set_to_map_.findKey(port); +} + +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) +{ + for (int bit = 0; bit < size; bit++) { + FuncExpr *clk_bit = nullptr; + if (clk) + clk_bit = clk->bitSubExpr(bit); + FuncExpr *data_bit = nullptr; + if (data) + data_bit = data->bitSubExpr(bit); + FuncExpr *clear_bit = nullptr; + if (clear) + clear_bit = clear->bitSubExpr(bit); + FuncExpr *preset_bit = nullptr; + if (preset) + preset_bit = preset->bitSubExpr(bit); + LibertyPort *out_bit = output; + if (output && output->hasMembers()) + out_bit = output->findLibertyMember(bit); + 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; + } +} + +Sequential * +LibertyCell::outputPortSequential(LibertyPort *port) +{ + return port_to_seq_map_.findKey(port); +} + +bool +LibertyCell::hasSequentials() const +{ + return !sequentials_.empty(); +} + +void +LibertyCell::addScaledCell(OperatingConditions *op_cond, + LibertyCell *scaled_cell) +{ + scaled_cells_[op_cond] = scaled_cell; + + LibertyCellPortBitIterator port_iter1(this); + LibertyCellPortBitIterator port_iter2(scaled_cell); + while (port_iter1.hasNext() && port_iter2.hasNext()) { + LibertyPort *port = port_iter1.next(); + LibertyPort *scaled_port = port_iter2.next(); + port->addScaledPort(op_cond, scaled_port); + } + + LibertyCellTimingArcSetIterator set_iter1(this); + LibertyCellTimingArcSetIterator set_iter2(scaled_cell); + while (set_iter1.hasNext() && set_iter2.hasNext()) { + TimingArcSet *arc_set1 = set_iter1.next(); + TimingArcSet *arc_set2 = set_iter2.next(); + TimingArcSetArcIterator arc_iter1(arc_set1); + TimingArcSetArcIterator arc_iter2(arc_set2); + while (arc_iter1.hasNext() && arc_iter2.hasNext()) { + TimingArc *arc = arc_iter1.next(); + TimingArc *scaled_arc = arc_iter2.next(); + if (TimingArc::equiv(arc, scaled_arc)) { + TimingModel *model = scaled_arc->model(); + model->setIsScaled(true); + arc->addScaledModel(op_cond, model); + } + } + } +} + +void +LibertyCell::setLibertyLibrary(LibertyLibrary *library) +{ + liberty_library_ = library; + library_ = library; +} + +void +LibertyCell::setHasInferedRegTimingArcs(bool infered) +{ + has_infered_reg_timing_arcs_ = infered; +} + +void +LibertyCell::setTestCell(TestCell *test) +{ + test_cell_ = test; +} + +void +LibertyCell::setIsDisabledConstraint(bool is_disabled) +{ + is_disabled_constraint_ = is_disabled; +} + +LibertyCell * +LibertyCell::cornerCell(int ap_index) +{ + if (ap_index < static_cast(corner_cells_.size())) + return corner_cells_[ap_index]; + else + return nullptr; +} + +void +LibertyCell::setCornerCell(LibertyCell *corner_cell, + int ap_index) +{ + if (ap_index >= static_cast(corner_cells_.size())) + corner_cells_.resize(ap_index + 1); + corner_cells_[ap_index] = corner_cell; +} + +//////////////////////////////////////////////////////////////// + +float +LibertyCell::ocvArcDepth() const +{ + return ocv_arc_depth_; +} + +void +LibertyCell::setOcvArcDepth(float depth) +{ + ocv_arc_depth_ = depth; +} + +OcvDerate * +LibertyCell::ocvDerate() const +{ + if (ocv_derate_) + return ocv_derate_; + else + return liberty_library_->defaultOcvDerate(); +} + +void +LibertyCell::setOcvDerate(OcvDerate *derate) +{ + ocv_derate_ = derate; +} + +OcvDerate * +LibertyCell::findOcvDerate(const char *derate_name) +{ + return ocv_derate_map_.findKey(derate_name); +} + +void +LibertyCell::addOcvDerate(OcvDerate *derate) +{ + ocv_derate_map_[derate->name()] = derate; +} + +//////////////////////////////////////////////////////////////// + +LibertyCellTimingArcSetIterator::LibertyCellTimingArcSetIterator(const LibertyCell *cell) : + TimingArcSetSeq::ConstIterator(&cell->timing_arc_sets_) +{ +} + +LibertyCellTimingArcSetIterator::LibertyCellTimingArcSetIterator(const LibertyCell *cell, + const LibertyPort *from, + const LibertyPort *to): + TimingArcSetSeq::ConstIterator(cell->timingArcSets(from, to)) +{ +} + +//////////////////////////////////////////////////////////////// + +// Latch enable port/function for a latch D->Q timing arc set. +class LatchEnable +{ +public: + LatchEnable(LibertyPort *data, + LibertyPort *enable, + TransRiseFall *enable_tr, + 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_; } + TransRiseFall *enableTransition() const { return enable_tr_; } + TimingArcSet *dToQ() const { return d_to_q_; } + TimingArcSet *enToQ() const { return en_to_q_; } + TimingArcSet *setupCheck() const { return setup_check_; } + +private: + DISALLOW_COPY_AND_ASSIGN(LatchEnable); + + LibertyPort *data_; + LibertyPort *enable_; + TransRiseFall *enable_tr_; + FuncExpr *enable_func_; + LibertyPort *output_; + TimingArcSet *d_to_q_; + TimingArcSet *en_to_q_; + TimingArcSet *setup_check_; +}; + +LatchEnable::LatchEnable(LibertyPort *data, + LibertyPort *enable, + TransRiseFall *enable_tr, + FuncExpr *enable_func, + LibertyPort *output, + TimingArcSet *d_to_q, + TimingArcSet *en_to_q, + TimingArcSet *setup_check) : + data_(data), + enable_(enable), + enable_tr_(enable_tr), + enable_func_(enable_func), + output_(output), + d_to_q_(d_to_q), + en_to_q_(en_to_q), + setup_check_(setup_check) +{ +} + +// Latch enable port/function for a latch D->Q timing arc set. +// This augments cell timing data by linking enables to D->Q arcs. +// Use timing arcs rather than sequentials (because they are optional). +void +LibertyCell::makeLatchEnables(Report *report, + 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(); + LibertyCellTimingArcSetIterator to_iter(this, nullptr, q); + while (to_iter.hasNext()) { + TimingArcSet *d_to_q = to_iter.next(); + if (d_to_q->role() == TimingRole::latchDtoQ()) { + LibertyPort *d = d_to_q->from(); + LibertyCellTimingArcSetIterator check_iter(this, en, d); + while (check_iter.hasNext()) { + TimingArcSet *setup_check = check_iter.next(); + if (setup_check->role() == TimingRole::setup()) { + LatchEnable *latch_enable = makeLatchEnable(d, en, q, d_to_q, + en_to_q, + setup_check, + debug); + TimingArcSetArcIterator check_arc_iter(setup_check); + if (check_arc_iter.hasNext()) { + TimingArc *check_arc = check_arc_iter.next(); + TransRiseFall *en_tr = latch_enable->enableTransition(); + TransRiseFall *check_tr = check_arc->fromTrans()->asRiseFall(); + if (check_tr == en_tr) { + report->warn("cell %s/%s %s -> %s latch enable %s_edge timing arc is inconsistent with %s -> %s setup_%s check.\n", + library_->name(), + name_, + en->name(), + q->name(), + en_tr == TransRiseFall::rise()?"rising":"falling", + en->name(), + d->name(), + check_tr==TransRiseFall::rise()?"rising":"falling"); + } + FuncExpr *en_func = latch_enable->enableFunc(); + if (en_func) { + TimingSense en_sense = en_func->portTimingSense(en); + if (en_sense == TimingSense::positive_unate + && en_tr != TransRiseFall::rise()) + report->warn("cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.\n", + library_->name(), + name_, + en->name(), + q->name(), + en_tr == TransRiseFall::rise()?"rising":"falling"); + else if (en_sense == TimingSense::negative_unate + && en_tr != TransRiseFall::fall()) + report->warn("cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.\n", + library_->name(), + name_, + en->name(), + q->name(), + en_tr == TransRiseFall::rise()?"rising":"falling"); + } + break; + } + } + } + } + } + } + } + } +} + +FuncExpr * +LibertyCell::findLatchEnableFunc(LibertyPort *data, + LibertyPort *enable) const +{ + for (auto seq : sequentials_) { + if (seq->isLatch() + && seq->data() + && seq->data()->hasPort(data) + && seq->clock() + && seq->clock()->hasPort(enable)) + return seq->clock(); + } + return nullptr; +} + +LatchEnable * +LibertyCell::makeLatchEnable(LibertyPort *d, + LibertyPort *en, + LibertyPort *q, + TimingArcSet *d_to_q, + TimingArcSet *en_to_q, + TimingArcSet *setup_check, + Debug *debug) +{ + TransRiseFall *en_tr = en_to_q->isRisingFallingEdge(); + FuncExpr *en_func = findLatchEnableFunc(d, en); + LatchEnable *latch_enable = new LatchEnable(d, en, en_tr, en_func, q, + d_to_q, en_to_q, setup_check); + // Multiple enables for D->Q pairs are not supported. + if (latch_d_to_q_map_[d_to_q]) + delete latch_d_to_q_map_[d_to_q]; + latch_d_to_q_map_[d_to_q] = latch_enable; + latch_check_map_[setup_check] = latch_enable; + latch_data_ports_.insert(d); + debugPrint3(debug, "liberty", 2, "latch d=%s en=%s q=%s\n", + d->name(), en->name(), q->name()); + return latch_enable; +} + +void +LibertyCell::inferLatchRoles(Debug *debug) +{ + if (hasInferedRegTimingArcs()) { + // Hunt down potential latch D/EN/Q triples. + LatchEnableSet latch_enables; + LibertyCellTimingArcSetIterator set_iter(this); + while (set_iter.hasNext()) { + TimingArcSet *en_to_q = set_iter.next(); + // 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(); + LibertyCellTimingArcSetIterator to_iter(this, nullptr, q); + while (to_iter.hasNext()) { + TimingArcSet *d_to_q = to_iter.next(); + // Look for combinational d->q arcs. + 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)) + || (d_to_q->arcCount() == 4))) + // Previously identified as D->Q arc. + || d_to_q_role == TimingRole::latchDtoQ()) { + LibertyPort *d = d_to_q->from(); + // Check for setup check from en -> d. + LibertyCellTimingArcSetIterator check_iter(this, en, d); + while (check_iter.hasNext()) { + TimingArcSet *setup_check = check_iter.next(); + if (setup_check->role() == TimingRole::setup()) { + makeLatchEnable(d, en, q, d_to_q, en_to_q, setup_check, debug); + d_to_q->setRole(TimingRole::latchDtoQ()); + en_to_q->setRole(TimingRole::latchEnToQ()); + } + } + } + } + } + } + } +} + +bool +LibertyCell::isLatchData(LibertyPort *port) +{ + return latch_data_ports_.hasKey(port); +} + +void +LibertyCell::latchEnable(TimingArcSet *d_to_q_set, + // Return values. + LibertyPort *&enable_port, + FuncExpr *&enable_func, + TransRiseFall *&enable_tr) const +{ + enable_port = nullptr; + 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_tr = latch_enable->enableTransition(); + } +} + +TransRiseFall * +LibertyCell::latchCheckEnableTrans(TimingArcSet *check_set) +{ + LatchEnable *latch_enable = latch_check_map_.findKey(check_set); + if (latch_enable) + return latch_enable->enableTransition(); + else + return nullptr; +} + +//////////////////////////////////////////////////////////////// + +LibertyCellPortIterator::LibertyCellPortIterator(const LibertyCell *cell) : + iter_(cell->ports_) +{ +} + +bool +LibertyCellPortIterator::hasNext() +{ + return iter_.hasNext(); +} + +LibertyPort * +LibertyCellPortIterator::next() +{ + return static_cast(iter_.next()); +} + +//////////////////////////////////////////////////////////////// + +LibertyCellPortBitIterator::LibertyCellPortBitIterator(const LibertyCell *cell): + iter_(cell->portBitIterator()) +{ +} + +LibertyCellPortBitIterator::~LibertyCellPortBitIterator() +{ + delete iter_; +} + +bool +LibertyCellPortBitIterator::hasNext() +{ + return iter_->hasNext(); +} + +LibertyPort * +LibertyCellPortBitIterator::next() +{ + return static_cast(iter_->next()); +} + +//////////////////////////////////////////////////////////////// + +LibertyPort::LibertyPort(LibertyCell *cell, + const char *name, + bool is_bus, + int from_index, + int to_index, + bool is_bundle, + ConcretePortSeq *members) : + ConcretePort(cell, name, is_bus, from_index, to_index, is_bundle, members), + liberty_cell_(cell), + function_(nullptr), + tristate_enable_(nullptr), + scaled_ports_(nullptr), + // capacitance_ intentionally not initialized so + // liberty reader can apply default capacitance. + min_period_(0.0), + pulse_clk_trigger_(nullptr), + pulse_clk_sense_(nullptr), + related_ground_pin_(nullptr), + related_power_pin_(nullptr), + min_pulse_width_exists_(false), + min_period_exists_(false), + is_clk_(false), + is_reg_clk_(false), + is_check_clk_(false), + is_clk_gate_clk_pin_(false), + is_clk_gate_enable_pin_(false), + is_clk_gate_out_pin_(false), + is_pll_feedback_pin_(false), + is_disabled_constraint_(false) +{ + liberty_port_ = this; + min_pulse_width_[TransRiseFall::riseIndex()] = 0.0; + min_pulse_width_[TransRiseFall::fallIndex()] = 0.0; +} + +LibertyPort::~LibertyPort() +{ + if (function_) + function_->deleteSubexprs(); + if (tristate_enable_) + tristate_enable_->deleteSubexprs(); + delete scaled_ports_; + stringDelete(related_ground_pin_); + stringDelete(related_power_pin_); +} + +void +LibertyPort::setDirection(PortDirection *dir) +{ + ConcretePort::setDirection(dir); + if (dir->isInternal()) + liberty_cell_->setHasInternalPorts(true); +} + +LibertyPort * +LibertyPort::findLibertyMember(int index) const +{ + return static_cast(findMember(index)); +} + +LibertyPort * +LibertyPort::findLibertyBusBit(int index) const +{ + return static_cast(findBusBit(index)); +} + +void +LibertyPort::setCapacitance(float cap) +{ + setCapacitance(TransRiseFall::rise(), MinMax::min(), cap); + setCapacitance(TransRiseFall::fall(), MinMax::min(), cap); + setCapacitance(TransRiseFall::rise(), MinMax::max(), cap); + setCapacitance(TransRiseFall::fall(), MinMax::max(), cap); +} + +void +LibertyPort::setCapacitance(const TransRiseFall *tr, + const MinMax *min_max, + float cap) +{ + capacitance_.setValue(tr, min_max, cap); + if (hasMembers()) { + LibertyPortMemberIterator member_iter(this); + while (member_iter.hasNext()) { + LibertyPort *port_bit = member_iter.next(); + port_bit->setCapacitance(tr, min_max, cap); + } + } +} + +float +LibertyPort::capacitance(const TransRiseFall *tr, + const MinMax *min_max) const +{ + float cap; + bool exists; + capacitance_.value(tr, min_max, cap, exists); + if (exists) + return cap; + else + return 0.0; +} + +void +LibertyPort::capacitance(const TransRiseFall *tr, + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) const +{ + capacitance_.value(tr, min_max, cap, exists); +} + +float +LibertyPort::capacitance(const TransRiseFall *tr, + const MinMax *min_max, + const OperatingConditions *op_cond, + const Pvt *pvt) const +{ + if (scaled_ports_) { + LibertyPort *scaled_port = (*scaled_ports_)[op_cond]; + // Scaled capacitance is not derated because scale factors are wrt + // nominal pvt. + if (scaled_port) + return scaled_port->capacitance(tr, min_max); + } + LibertyLibrary *lib = liberty_cell_->libertyLibrary(); + float cap = capacitance(tr, min_max); + return cap * lib->scaleFactor(ScaleFactorType::pin_cap, liberty_cell_, pvt); +} + +bool +LibertyPort::capacitanceIsOneValue() const +{ + return capacitance_.isOneValue(); +} + +//////////////////////////////////////////////////////////////// + +// Use the min/max "drive" for all the timing arcs in the cell. +float +LibertyPort::driveResistance(const TransRiseFall *tr, + const MinMax *min_max) const +{ + float max_drive = min_max->initValue(); + bool found_drive = false; + LibertyCellTimingArcSetIterator set_iter(liberty_cell_, nullptr, this); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); + if (!set->role()->isTimingCheck()) { + TimingArcSetArcIterator arc_iter(set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + if (tr == nullptr + || arc->toTrans()->asRiseFall() == tr) { + GateTimingModel *model = dynamic_cast(arc->model()); + if (model) { + float drive = model->driveResistance(liberty_cell_, nullptr); + if (min_max->compare(drive, max_drive)) + max_drive = drive; + found_drive = true; + } + } + } + } + } + if (found_drive) + return max_drive; + else + return 0.0; +} + +float +LibertyPort::driveResistance() const +{ + return driveResistance(nullptr, MinMax::max()); +} + +void +LibertyPort::setFunction(FuncExpr *func) +{ + function_ = func; + if (is_bus_ || is_bundle_) { + LibertyPortMemberIterator member_iter(this); + int bit_offset = 0; + while (member_iter.hasNext()) { + LibertyPort *port_bit = member_iter.next(); + FuncExpr *sub_expr = (func) ? func->bitSubExpr(bit_offset) : nullptr; + port_bit->setFunction(sub_expr); + bit_offset++; + } + } +} + +void +LibertyPort::setTristateEnable(FuncExpr *enable) +{ + tristate_enable_ = enable; + if (hasMembers()) { + LibertyPortMemberIterator member_iter(this); + while (member_iter.hasNext()) { + LibertyPort *port_bit = member_iter.next(); + FuncExpr *sub_expr = + (enable) ? enable->bitSubExpr(port_bit->busBitIndex()) : nullptr; + port_bit->setTristateEnable(sub_expr); + } + } +} + +void +LibertyPort::slewLimit(const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const +{ + slew_limit_.value(min_max, limit, exists); +} + +void +LibertyPort::setSlewLimit(float slew, const MinMax *min_max) +{ + slew_limit_.setValue(min_max, slew); +} + +void +LibertyPort::capacitanceLimit(const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const +{ + return cap_limit_.value(min_max, limit, exists); +} + +void +LibertyPort::setCapacitanceLimit(float cap, + const MinMax *min_max) +{ + cap_limit_.setValue(min_max, cap); +} + +void +LibertyPort::fanoutLimit(const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const +{ + return fanout_limit_.value(min_max, limit, exists); +} + +void +LibertyPort::setFanoutLimit(float fanout, + 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 +{ + if (scaled_ports_) { + LibertyPort *scaled_port = (*scaled_ports_)[op_cond]; + if (scaled_port) { + scaled_port->minPeriod(min_period, exists); + return; + } + } + LibertyLibrary *lib = liberty_cell_->libertyLibrary(); + min_period = min_period_ * lib->scaleFactor(ScaleFactorType::min_period, + liberty_cell_, pvt); + exists = min_period_exists_; +} + +void +LibertyPort::minPeriod(float &min_period, + bool &exists) const +{ + min_period = min_period_; + exists = min_period_exists_; +} + +void +LibertyPort::setMinPeriod(float min_period) +{ + min_period_ = min_period; + min_period_exists_ = true; +} + +void +LibertyPort::minPulseWidth(const TransRiseFall *hi_low, + const OperatingConditions *op_cond, + const Pvt *pvt, + float &min_width, + bool &exists) const +{ + if (scaled_ports_) { + LibertyPort *scaled_port = (*scaled_ports_)[op_cond]; + if (scaled_port) { + scaled_port->minPulseWidth(hi_low, min_width, exists); + return; + } + } + int hi_low_index = hi_low->index(); + LibertyLibrary *lib = liberty_cell_->libertyLibrary(); + min_width = min_pulse_width_[hi_low_index] + * lib->scaleFactor(ScaleFactorType::min_pulse_width, hi_low_index, + liberty_cell_, pvt); + exists = min_pulse_width_exists_ & (1 << hi_low_index); +} + +void +LibertyPort::minPulseWidth(const TransRiseFall *hi_low, + float &min_width, + bool &exists) const +{ + int hi_low_index = hi_low->index(); + min_width = min_pulse_width_[hi_low_index]; + exists = min_pulse_width_exists_ & (1 << hi_low_index); +} + +void +LibertyPort::setMinPulseWidth(TransRiseFall *hi_low, + float min_width) +{ + int hi_low_index = hi_low->index(); + min_pulse_width_[hi_low_index] = min_width; + min_pulse_width_exists_ |= (1 << hi_low_index); +} + +bool +LibertyPort::equiv(const LibertyPort *port1, + const LibertyPort *port2) +{ + return (port1 == nullptr && port2 == nullptr) + || (port1 != nullptr && port2 != nullptr + && stringEq(port1->name(), port2->name()) + && port1->direction() == port2->direction()); +} + +bool +LibertyPort::less(const LibertyPort *port1, + const LibertyPort *port2) +{ + const char *name1 = port1->name(); + const char *name2 = port2->name(); + if (stringEq(name1, name2)) { + PortDirection *dir1 = port1->direction(); + PortDirection *dir2 = port2->direction(); + if (dir1 == dir2) { + } + else + return dir1->index() < dir2->index(); + } + return stringLess(name1, name2); +} + +void +LibertyPort::addScaledPort(OperatingConditions *op_cond, + LibertyPort *scaled_port) +{ + if (scaled_ports_ == nullptr) + scaled_ports_ = new ScaledPortMap; + (*scaled_ports_)[op_cond] = scaled_port; +} + +bool +LibertyPort::isClock() const +{ + return is_clk_; +} + +void +LibertyPort::setIsClock(bool is_clk) +{ + is_clk_ = is_clk; +} + +void +LibertyPort::setIsRegClk(bool is_clk) +{ + is_reg_clk_ = is_clk; +} + +void +LibertyPort::setIsCheckClk(bool is_clk) +{ + is_check_clk_ = is_clk; +} + +void +LibertyPort::setIsClockGateClockPin(bool is_clk_gate_clk) +{ + is_clk_gate_clk_pin_ = is_clk_gate_clk; +} + +void +LibertyPort::setIsClockGateEnablePin(bool is_clk_gate_enable) +{ + is_clk_gate_enable_pin_ = is_clk_gate_enable; +} + +void +LibertyPort::setIsClockGateOutPin(bool is_clk_gate_out) +{ + is_clk_gate_out_pin_ = is_clk_gate_out; +} + +void +LibertyPort::setIsPllFeedbackPin(bool is_pll_feedback_pin) +{ + is_pll_feedback_pin_ = is_pll_feedback_pin; +} + +void +LibertyPort::setPulseClk(TransRiseFall *trigger, + TransRiseFall *sense) +{ + pulse_clk_trigger_ = trigger; + pulse_clk_sense_ = sense; +} + +void +LibertyPort::setIsDisabledConstraint(bool is_disabled) +{ + is_disabled_constraint_ = is_disabled; +} + +LibertyPort * +LibertyPort::cornerPort(int ap_index) +{ + if (ap_index < static_cast(corner_ports_.size())) { + LibertyPort *corner_port = corner_ports_[ap_index]; + if (corner_port) + return corner_port; + } + return this; +} + +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; +} + +void +LibertyPort::setRelatedGroundPin(const char *related_ground_pin) +{ + related_ground_pin_ = stringCopy(related_ground_pin); +} + +void +LibertyPort::setRelatedPowerPin(const char *related_power_pin) +{ + related_power_pin_ = stringCopy(related_power_pin); +} + +//////////////////////////////////////////////////////////////// + +void +sortLibertyPortSet(LibertyPortSet *set, + LibertyPortSeq &ports) +{ + LibertyPortSet::Iterator port_iter(set); + while (port_iter.hasNext()) + ports.push_back(port_iter.next()); + sort(ports, LibertyPortNameLess()); +} + +bool +LibertyPortNameLess::operator()(const LibertyPort *port1, + const LibertyPort *port2) const +{ + return stringLess(port1->name(), port2->name()); +} + +bool +LibertyPortPairLess::operator()(const LibertyPortPair *pair1, + const LibertyPortPair *pair2) const +{ + return pair1->first < pair2->first + || (pair1->first == pair2->first + && pair1->second < pair2->second); +} + +bool +LibertyPortPairLess::operator()(const LibertyPortPair &pair1, + const LibertyPortPair &pair2) const +{ + return pair1.first < pair2.first + || (pair1.first == pair2.first + && pair1.second < pair2.second); +} + +//////////////////////////////////////////////////////////////// + +LibertyPortMemberIterator::LibertyPortMemberIterator(const LibertyPort *port) : + iter_(port->memberIterator()) +{ +} + +LibertyPortMemberIterator::~LibertyPortMemberIterator() +{ + delete iter_; +} + +bool +LibertyPortMemberIterator::hasNext() +{ + return iter_->hasNext(); +} + +LibertyPort * +LibertyPortMemberIterator::next() +{ + return static_cast(iter_->next()); +} + +//////////////////////////////////////////////////////////////// + +BusDcl::BusDcl(const char *name, + int from, + int to) : + name_(stringCopy(name)), + from_(from), + to_(to) +{ +} + +BusDcl::~BusDcl() +{ + stringDelete(name_); +} + +//////////////////////////////////////////////////////////////// + +ModeDef::ModeDef(const char *name) : + name_(stringCopy(name)) +{ +} + +ModeDef::~ModeDef() +{ + values_.deleteContents(); + stringDelete(name_); +} + +ModeValueDef * +ModeDef::defineValue(const char *value, + FuncExpr *cond, + const char *sdf_cond) +{ + ModeValueDef *val_def = new ModeValueDef(value, cond, sdf_cond); + values_[val_def->value()] = val_def; + return val_def; +} + +ModeValueDef * +ModeDef::findValueDef(const char *value) +{ + return values_[value]; +} + +//////////////////////////////////////////////////////////////// + +ModeValueDef::ModeValueDef(const char *value, + FuncExpr *cond, + const char *sdf_cond) : + value_(stringCopy(value)), + cond_(cond), + sdf_cond_(stringCopy(sdf_cond)) +{ +} + +ModeValueDef::~ModeValueDef() +{ + stringDelete(value_); + if (cond_) + cond_->deleteSubexprs(); + if (sdf_cond_) + stringDelete(sdf_cond_); +} + +void +ModeValueDef::setSdfCond(const char *sdf_cond) +{ + sdf_cond_ = stringCopy(sdf_cond); +} + +//////////////////////////////////////////////////////////////// + +TableTemplate::TableTemplate(const char *name) : + name_(stringCopy(name)), + axis1_(nullptr), + axis2_(nullptr), + axis3_(nullptr) +{ +} + +TableTemplate::TableTemplate(const char *name, + TableAxis *axis1, + TableAxis *axis2, + TableAxis *axis3) : + name_(stringCopy(name)), + axis1_(axis1), + axis2_(axis2), + axis3_(axis3) +{ +} + +TableTemplate::~TableTemplate() +{ + stringDelete(name_); + delete axis1_; + delete axis2_; + delete axis3_; +} + +void +TableTemplate::setName(const char *name) +{ + stringDelete(name_); + name_ = stringCopy(name); +} + +void +TableTemplate::setAxis1(TableAxis *axis) +{ + axis1_ = axis; +} + +void +TableTemplate::setAxis2(TableAxis *axis) +{ + axis2_ = axis; +} + +void +TableTemplate::setAxis3(TableAxis *axis) +{ + axis3_ = axis; +} + +//////////////////////////////////////////////////////////////// + +Pvt::Pvt(float process, + float voltage, + float temperature) : + process_(process), + voltage_(voltage), + temperature_(temperature) +{ +} + +void +Pvt::setProcess(float process) +{ + process_ = process; +} + +void +Pvt::setVoltage(float voltage) +{ + voltage_ = voltage; +} + +void +Pvt::setTemperature(float temp) +{ + temperature_ = temp; +} + +OperatingConditions::OperatingConditions(const char *name) : + Pvt(0.0, 0.0, 0.0), + name_(stringCopy(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_(stringCopy(name)), + wire_load_tree_(wire_load_tree) +{ +} + +OperatingConditions::~OperatingConditions() +{ + stringDelete(name_); +} + +void +OperatingConditions::setWireloadTree(WireloadTree tree) +{ + wire_load_tree_ = tree; +} + +//////////////////////////////////////////////////////////////// + +static EnumNameMap scale_factor_type_map = + {{ScaleFactorType::pin_cap, "pin_cap"}, + {ScaleFactorType::wire_cap, "wire_res"}, + {ScaleFactorType::min_period, "min_period"}, + {ScaleFactorType::cell, "cell"}, + {ScaleFactorType::hold, "hold"}, + {ScaleFactorType::setup, "setup"}, + {ScaleFactorType::recovery, "recovery"}, + {ScaleFactorType::removal, "removal"}, + {ScaleFactorType::nochange, "nochange"}, + {ScaleFactorType::skew, "skew"}, + {ScaleFactorType::leakage_power, "leakage_power"}, + {ScaleFactorType::internal_power, "internal_power"}, + {ScaleFactorType::transition, "transition"}, + {ScaleFactorType::min_pulse_width, "min_pulse_width"}, + {ScaleFactorType::unknown, "unknown"} + }; + +const char * +scaleFactorTypeName(ScaleFactorType type) +{ + return scale_factor_type_map.find(type); +} + +ScaleFactorType +findScaleFactorType(const char *name) +{ + return scale_factor_type_map.find(name, ScaleFactorType::unknown); +} + +bool +scaleFactorTypeRiseFallSuffix(ScaleFactorType type) +{ + return type == ScaleFactorType::cell + || type == ScaleFactorType::hold + || type == ScaleFactorType::setup + || type == ScaleFactorType::recovery + || type == ScaleFactorType::removal + || type == ScaleFactorType::nochange + || type == ScaleFactorType::skew; +} + +bool +scaleFactorTypeRiseFallPrefix(ScaleFactorType type) +{ + return type == ScaleFactorType::transition; +} + +bool +scaleFactorTypeLowHighSuffix(ScaleFactorType type) +{ + return type == ScaleFactorType::min_pulse_width; +} + +//////////////////////////////////////////////////////////////// + +EnumNameMap scale_factor_pvt_names = + {{ScaleFactorPvt::process, "process"}, + {ScaleFactorPvt::volt, "volt"}, + {ScaleFactorPvt::temp, "temp"} + }; + +ScaleFactorPvt +findScaleFactorPvt(const char *name) +{ + return scale_factor_pvt_names.find(name, ScaleFactorPvt::unknown); +} + +const char * +scaleFactorPvtName(ScaleFactorPvt pvt) +{ + return scale_factor_pvt_names.find(pvt); +} + +//////////////////////////////////////////////////////////////// + +ScaleFactors::ScaleFactors(const char *name) : + name_(stringCopy(name)) +{ + for (int type = 0; type < scale_factor_type_count; type++) { + for (int pvt = 0; pvt < int(ScaleFactorPvt::count); pvt++) { + for (auto tr_index : TransRiseFall::rangeIndex()) { + scales_[type][pvt][tr_index] = 0.0; + } + } + } +} + +ScaleFactors::~ScaleFactors() +{ + stringDelete(name_); +} + +void +ScaleFactors::setScale(ScaleFactorType type, + ScaleFactorPvt pvt, + TransRiseFall *tr, + float scale) +{ + scales_[int(type)][int(pvt)][tr->index()] = scale; +} + +void +ScaleFactors::setScale(ScaleFactorType type, + ScaleFactorPvt pvt, + float scale) +{ + scales_[int(type)][int(pvt)][0] = scale; +} + +float +ScaleFactors::scale(ScaleFactorType type, + ScaleFactorPvt pvt, + TransRiseFall *tr) +{ + return scales_[int(type)][int(pvt)][tr->index()]; +} + +float +ScaleFactors::scale(ScaleFactorType type, + ScaleFactorPvt pvt, + int tr_index) +{ + return scales_[int(type)][int(pvt)][tr_index]; +} + +float +ScaleFactors::scale(ScaleFactorType type, + ScaleFactorPvt pvt) +{ + return scales_[int(type)][int(pvt)][0]; +} + +void +ScaleFactors::print() +{ + printf("%10s", " "); + for (int pvt_index = 0; pvt_index < int(ScaleFactorPvt::count); pvt_index++) { + ScaleFactorPvt pvt = (ScaleFactorPvt) pvt_index; + printf("%10s", scaleFactorPvtName(pvt)); + } + printf("\n"); + for (int type_index = 0; type_index < scale_factor_type_count; type_index++) { + ScaleFactorType type = (ScaleFactorType) type_index; + printf("%10s ", scaleFactorTypeName(type)); + for (int pvt_index = 0; pvt_index < int(ScaleFactorPvt::count); pvt_index++) { + if (scaleFactorTypeRiseFallSuffix(type) + || scaleFactorTypeRiseFallPrefix(type) + || scaleFactorTypeLowHighSuffix(type)) { + printf(" %.3f,%.3f", + scales_[type_index][pvt_index][TransRiseFall::riseIndex()], + scales_[type_index][pvt_index][TransRiseFall::fallIndex()]); + } + else { + printf(" %.3f", + scales_[type_index][pvt_index][0]); + } + } + printf("\n"); + } +} + +TestCell::TestCell(LibertyPort *data_in, + LibertyPort *scan_in, + LibertyPort *scan_enable, + LibertyPort *scan_out, + LibertyPort *scan_out_inv) : + data_in_(data_in), + scan_in_(scan_in), + scan_enable_(scan_enable), + scan_out_(scan_out), + scan_out_inv_(scan_out_inv) +{ +} + +TestCell::TestCell() : + data_in_(nullptr), + scan_in_(nullptr), + scan_enable_(nullptr), + scan_out_(nullptr), + scan_out_inv_(nullptr) +{ +} + +void +TestCell::setDataIn(LibertyPort *port) +{ + data_in_ = port; +} + +void +TestCell::setScanIn(LibertyPort *port) +{ + scan_in_ = port; +} + +void +TestCell::setScanEnable(LibertyPort *port) +{ + scan_enable_ = port; +} + +void +TestCell::setScanOut(LibertyPort *port) +{ + scan_out_ = port; +} + +void +TestCell::setScanOutInv(LibertyPort *port) +{ + scan_out_inv_ = port; +} + +//////////////////////////////////////////////////////////////// + +OcvDerate::OcvDerate(const char *name) : + name_(name) +{ + for (auto el_index : EarlyLate::rangeIndex()) { + for (auto tr_index : TransRiseFall::rangeIndex()) { + derate_[tr_index][el_index][int(PathType::clk)] = nullptr; + derate_[tr_index][el_index][int(PathType::data)] = nullptr; + } + } +} + +OcvDerate::~OcvDerate() +{ + stringDelete(name_); + // Derating table models can be shared in multiple places in derate_; + // Collect them in a set to avoid duplicate deletes. + Set models; + for (auto el_index : EarlyLate::rangeIndex()) { + for (auto tr_index : TransRiseFall::rangeIndex()) { + Table *derate; + derate = derate_[tr_index][el_index][int(PathType::clk)]; + if (derate) + models.insert(derate); + derate = derate_[tr_index][el_index][int(PathType::data)]; + if (derate) + models.insert(derate); + } + } + Set::Iterator model_iter(models); + while (model_iter.hasNext()) { + Table *model = model_iter.next(); + delete model; + } +} + +Table * +OcvDerate::derateTable(const TransRiseFall *tr, + const EarlyLate *early_late, + PathType path_type) +{ + return derate_[tr->index()][early_late->index()][int(path_type)]; +} + +void +OcvDerate::setDerateTable(const TransRiseFall *tr, + const EarlyLate *early_late, + const PathType path_type, + Table *derate) +{ + derate_[tr->index()][early_late->index()][int(path_type)] = derate; +} + +//////////////////////////////////////////////////////////////// + +LibertyPgPort::LibertyPgPort(const char *name, + LibertyCell *cell) : + name_(stringCopy(name)), + pg_type_(unknown), + voltage_name_(nullptr), + cell_(cell) +{ +} + +LibertyPgPort::~LibertyPgPort() +{ + stringDelete(name_); + stringDelete(voltage_name_); +} + +void +LibertyPgPort::setPgType(PgType type) +{ + pg_type_ = type; +} + +void +LibertyPgPort::setVoltageName(const char *voltage_name) +{ + voltage_name_ = stringCopy(voltage_name); +} + +} // namespace diff --git a/liberty/Liberty.hh b/liberty/Liberty.hh new file mode 100644 index 0000000..6f45bc8 --- /dev/null +++ b/liberty/Liberty.hh @@ -0,0 +1,1069 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_LIBERTY_H +#define STA_LIBERTY_H + +#include "DisallowCopyAssign.hh" +#include "Transition.hh" +#include "MinMax.hh" +#include "MinMaxValues.hh" +#include "RiseFallValues.hh" +#include "RiseFallMinMax.hh" +#include "ConcreteLibrary.hh" +#include "LibertyClass.hh" + +namespace sta { + +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 LibertyPgPort; + +typedef Set LibrarySet; +typedef Map TableTemplateMap; +typedef Map BusDclMap; +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 Vector LeakagePowerSeq; +typedef Map LibertyPortTimingArcMap; +typedef Map ScaledCellMap; +typedef Map ScaledPortMap; +typedef Map ModeDefMap; +typedef Map ModeValueMap; +typedef Map LatchEnableMap; +typedef Map OcvDerateMap; +typedef Vector TimingArcAttrsSeq; +typedef Vector InternalPowerAttrsSeq; +typedef Map SupplyVoltageMap; +typedef Map LibertyPgPortMap; + +enum class ClockGateType { none, latch_posedge, latch_negedge, other }; + +enum class DelayModelType { cmos_linear, cmos_pwl, cmos2, table, polynomial, dcm }; + +enum class ScaleFactorPvt { process, volt, temp, count, unknown }; + +enum class TableTemplateType { delay, power, output_current, ocv, count }; + +void +initLiberty(); +void +deleteLiberty(); + +ScaleFactorPvt +findScaleFactorPvt(const char *name); +const char * +scaleFactorPvtName(ScaleFactorPvt pvt); + +ScaleFactorType +findScaleFactorType(const char *name); +const char * +scaleFactorTypeName(ScaleFactorType type); +bool +scaleFactorTypeRiseFallSuffix(ScaleFactorType type); +bool +scaleFactorTypeRiseFallPrefix(ScaleFactorType type); +bool +scaleFactorTypeLowHighSuffix(ScaleFactorType type); + +// Timing sense as a string. +const char * +timingSenseString(TimingSense sense); + +// Opposite timing sense. +TimingSense +timingSenseOpposite(TimingSense sense); + +class LibertyLibrary : public ConcreteLibrary +{ +public: + LibertyLibrary(const char *name, + const char *filename); + virtual ~LibertyLibrary(); + LibertyCell *findLibertyCell(const char *name) const; + void findLibertyCellsMatching(PatternMatch *pattern, + LibertyCellSeq *cells); + // Liberty cells that are buffers. + LibertyCellSeq *buffers(); + + DelayModelType delayModelType() const { return delay_model_type_; } + void setDelayModelType(DelayModelType type); + void addBusDcl(BusDcl *bus_dcl); + BusDcl *findBusDcl(const char *name) const; + void addTableTemplate(TableTemplate *tbl_template, + TableTemplateType type); + TableTemplate *findTableTemplate(const char *name, + TableTemplateType type); + float nominalProcess() { return nominal_process_; } + void setNominalProcess(float process); + float nominalVoltage() const { return nominal_voltage_; } + void setNominalVoltage(float voltage); + float nominalTemperature() const { return nominal_temperature_; } + void setNominalTemperature(float temperature); + void setScaleFactors(ScaleFactors *scales); + // Add named scale factor group. + void addScaleFactors(ScaleFactors *scales); + ScaleFactors *findScaleFactors(const char *name); + ScaleFactors *scaleFactors() const { return scale_factors_; } + float scaleFactor(ScaleFactorType type, + const Pvt *pvt) const; + float scaleFactor(ScaleFactorType type, + const LibertyCell *cell, + const Pvt *pvt) const; + float scaleFactor(ScaleFactorType type, + int tr_index, + const LibertyCell *cell, + const Pvt *pvt) const; + void setWireSlewDegradationTable(TableModel *model, + TransRiseFall *tr); + TableModel *wireSlewDegradationTable(const TransRiseFall *tr) const; + float degradeWireSlew(const LibertyCell *cell, + const TransRiseFall *tr, + const Pvt *pvt, + float in_slew, + float wire_delay) const; + // Check for supported axis variables. + // Return true if axes are supported. + static bool checkSlewDegradationAxes(Table *table); + + float defaultInputPinCap() const { return default_input_pin_cap_; } + void setDefaultInputPinCap(float cap); + float defaultOutputPinCap() const { return default_output_pin_cap_; } + void setDefaultOutputPinCap(float cap); + float defaultBidirectPinCap() const { return default_bidirect_pin_cap_; } + void setDefaultBidirectPinCap(float cap); + + void defaultIntrinsic(const TransRiseFall *tr, + // Return values. + float &intrisic, + bool &exists) const; + void setDefaultIntrinsic(const TransRiseFall *tr, + float value); + // Uses defaultOutputPinRes or defaultBidirectPinRes based on dir. + void defaultPinResistance(const TransRiseFall *tr, + const PortDirection *dir, + // Return values. + float &res, + bool &exists) const; + void defaultBidirectPinRes(const TransRiseFall *tr, + // Return values. + float &res, + bool &exists) const; + void setDefaultBidirectPinRes(const TransRiseFall *tr, + float value); + void defaultOutputPinRes(const TransRiseFall *tr, + // Return values. + float &res, + bool &exists) const; + void setDefaultOutputPinRes(const TransRiseFall *tr, + float value); + + void defaultMaxSlew(float &slew, + bool &exists) const; + void setDefaultMaxSlew(float slew); + void defaultMaxCapacitance(float &cap, + bool &exists) const; + void setDefaultMaxCapacitance(float cap); + void defaultMaxFanout(float &fanout, + bool &exists) const; + void setDefaultMaxFanout(float fanout); + float defaultFanoutLoad() const { return default_fanout_load_; } + void setDefaultFanoutLoad(float load); + + // Logic thresholds. + float inputThreshold(const TransRiseFall *tr) const; + void setInputThreshold(const TransRiseFall *tr, + float th); + float outputThreshold(const TransRiseFall *tr) const; + void setOutputThreshold(const TransRiseFall *tr, + float th); + // Slew thresholds (measured). + float slewLowerThreshold(const TransRiseFall *tr) const; + void setSlewLowerThreshold(const TransRiseFall *tr, + float th); + float slewUpperThreshold(const TransRiseFall *tr) const; + void setSlewUpperThreshold(const TransRiseFall *tr, + 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 + // the user. + float slewDerateFromLibrary() const; + void setSlewDerateFromLibrary(float derate); + + 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); + WireloadMode defaultWireloadMode() const; + void setDefaultWireloadMode(WireloadMode mode); + void addWireloadSelection(WireloadSelection *selection); + void setDefaultWireloadSelection(WireloadSelection *selection); + + OperatingConditions *findOperatingConditions(const char *name); + OperatingConditions *defaultOperatingConditions() const; + void addOperatingConditions(OperatingConditions *op_cond); + void setDefaultOperatingConditions(OperatingConditions *op_cond); + + // AOCV + // Zero means the ocv depth is not specified. + float ocvArcDepth() const; + 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; + + // Make scaled cell. Call LibertyCell::addScaledCell after it is complete. + LibertyCell *makeScaledCell(const char *name, + const char *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); + +protected: + float degradeWireSlew(const LibertyCell *cell, + const Pvt *pvt, + const TableModel *model, + float in_slew, + float wire_delay) const; + + Units *units_; + DelayModelType delay_model_type_; + BusDclMap bus_dcls_; + TableTemplateMap template_maps_[int(TableTemplateType::count)]; + float nominal_process_; + float nominal_voltage_; + float nominal_temperature_; + ScaleFactors *scale_factors_; + ScaleFactorsMap scale_factors_map_; + TableModel *wire_slew_degradation_tbls_[TransRiseFall::index_count]; + float default_input_pin_cap_; + float default_output_pin_cap_; + float default_bidirect_pin_cap_; + RiseFallValues default_intrinsic_; + RiseFallValues default_inout_pin_res_; + RiseFallValues default_output_pin_res_; + float default_fanout_load_; + 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_[TransRiseFall::index_count]; + float output_threshold_[TransRiseFall::index_count]; + float slew_lower_threshold_[TransRiseFall::index_count]; + float slew_upper_threshold_[TransRiseFall::index_count]; + float slew_derate_from_library_; + WireloadMap wireloads_; + Wireload *default_wire_load_; + WireloadMode default_wire_load_mode_; + WireloadSelection *default_wire_load_selection_; + WireloadSelectionMap wire_load_selections_; + OperatingConditionsMap operating_conditions_; + OperatingConditions *default_operating_conditions_; + float ocv_arc_depth_; + OcvDerate *default_ocv_derate_; + OcvDerateMap ocv_derate_map_; + SupplyVoltageMap supply_voltage_map_; + LibertyCellSeq *buffers_; + + // Set if any library has rise/fall capacitances. + static bool found_rise_fall_caps_; + 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: + DISALLOW_COPY_AND_ASSIGN(LibertyLibrary); + + friend class LibertyCell; + friend class LibertyCellIterator; + friend class TableTemplateIterator; + friend class OperatingConditionsIterator; +}; + +class LibertyCellIterator : public Iterator +{ +public: + explicit LibertyCellIterator(const LibertyLibrary *library); + bool hasNext(); + LibertyCell *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyCellIterator); + + ConcreteCellMap::ConstIterator iter_; +}; + +class TableTemplateIterator : public TableTemplateMap::ConstIterator +{ +public: + TableTemplateIterator(const LibertyLibrary *library, + TableTemplateType type) : + TableTemplateMap::ConstIterator(library->template_maps_[int(type)]) {} +}; + +class OperatingConditionsIterator : public OperatingConditionsMap::ConstIterator +{ +public: + OperatingConditionsIterator(const LibertyLibrary *library) : + OperatingConditionsMap::ConstIterator(library->operating_conditions_) {} +}; + +//////////////////////////////////////////////////////////////// + +class LibertyCell : public ConcreteCell +{ +public: + LibertyCell(LibertyLibrary *library, + const char *name, + const char *filename); + virtual ~LibertyCell(); + LibertyLibrary *libertyLibrary() const { return liberty_library_; } + LibertyLibrary *libertyLibrary() { return liberty_library_; } + LibertyPort *findLibertyPort(const char *name) const; + void findLibertyPortsMatching(PatternMatch *pattern, + LibertyPortSeq *ports) const; + bool hasInternalPorts() const { return has_internal_ports_; } + LibertyPgPort *findPgPort(const char *name); + ScaleFactors *scaleFactors() const { return scale_factors_; } + void setScaleFactors(ScaleFactors *scale_factors); + ModeDef *makeModeDef(const char *name); + ModeDef *findModeDef(const char *name); + + float area() const { return area_; } + void setArea(float area); + bool dontUse() const { return dont_use_; } + void setDontUse(bool dont_use); + bool isMacro() const { return is_macro_; } + void setIsMacro(bool is_macro); + bool isPad() const { return is_pad_; } + void setIsPad(bool is_pad); + bool interfaceTiming() const { return interface_timing_; } + void setInterfaceTiming(bool value); + bool isClockGateLatchPosedge() const; + bool isClockGateLatchNegedge() const; + bool isClockGateOther() const; + bool isClockGate() const; + void setClockGateType(ClockGateType type); + // from or to may be nullptr to wildcard. + 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; + bool hasTimingArcs(LibertyPort *port) const; + + InternalPowerSeq *internalPowers() { return &internal_powers_; } + LeakagePowerSeq *leakagePowers() { return &leakage_powers_; } + void leakagePower(// Return values. + float &leakage, + bool &exists) const; + bool leakagePowerEx() const { return leakage_power_exists_; } + + bool hasSequentials() const; + // Find the sequential with the output connected to an (internal) port. + Sequential *outputPortSequential(LibertyPort *port); + + // Find bus declaration local to this cell. + BusDcl *findBusDcl(const char *name) const; + // True when TimingArcSetBuilder::makeRegLatchArcs infers register + // timing arcs. + bool hasInferedRegTimingArcs() const { return has_infered_reg_timing_arcs_; } + TestCell *testCell() const { return test_cell_; } + bool isLatchData(LibertyPort *port); + void latchEnable(TimingArcSet *arc_set, + // Return values. + LibertyPort *&enable_port, + FuncExpr *&enable_func, + TransRiseFall *&enable_tr) const; + TransRiseFall *latchCheckEnableTrans(TimingArcSet *check_set); + bool isDisabledConstraint() const { return is_disabled_constraint_; } + LibertyCell *cornerCell(int ap_index); + + // AOCV + float ocvArcDepth() const; + OcvDerate *ocvDerate() const; + OcvDerate *findOcvDerate(const char *derate_name); + + // Next higher/lower drive equivalent cells. + LibertyCell *higherDrive() const; + LibertyCell *lowerDrive() const; + + // Build helpers. + 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); + 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 addTimingArcAttrs(TimingArcAttrs *attrs); + void addInternalPower(InternalPower *power); + void addInternalPowerAttrs(InternalPowerAttrs *attrs); + void addLeakagePower(LeakagePower *power); + void setLeakagePower(float leakage); + void setOcvArcDepth(float depth); + void setOcvDerate(OcvDerate *derate); + void addOcvDerate(OcvDerate *derate); + void addPgPort(LibertyPgPort *pg_port); + void setTestCell(TestCell *test); + void setHasInferedRegTimingArcs(bool infered); + void setIsDisabledConstraint(bool is_disabled); + void setCornerCell(LibertyCell *corner_cell, + int ap_index); + // Call after cell is finished being constructed. + void finish(bool infer_latches, + Report *report, + Debug *debug); + bool isBuffer() const; + // Only valid when isBuffer() returns true. + void bufferPorts(// Return values. + LibertyPort *&input, + LibertyPort *&output); + +protected: + void addPort(ConcretePort *port); + void setHasInternalPorts(bool has_internal); + void setLibertyLibrary(LibertyLibrary *library); + void deleteTimingArcAttrs(); + void makeLatchEnables(Report *report, + Debug *debug); + FuncExpr *findLatchEnableFunc(LibertyPort *data, + LibertyPort *enable) const; + LatchEnable *makeLatchEnable(LibertyPort *d, + LibertyPort *en, + LibertyPort *q, + TimingArcSet *d_to_q, + TimingArcSet *en_to_q, + TimingArcSet *setup_check, + Debug *debug); + void findDefaultCondArcs(); + void translatePresetClrCheckRoles(); + void inferLatchRoles(Debug *debug); + void deleteInternalPowerAttrs(); + void makeTimingArcMap(Report *report); + void makeTimingArcPortMaps(); + bool hasBufferFunc(const LibertyPort *input, + const LibertyPort *output) const; + + LibertyLibrary *liberty_library_; + float area_; + bool dont_use_; + bool is_macro_; + bool is_pad_; + bool has_internal_ports_; + bool interface_timing_; + ClockGateType clock_gate_type_; + TimingArcSetSeq timing_arc_sets_; + TimingArcSetMap timing_arc_set_map_; + LibertyPortPairTimingArcMap port_timing_arc_set_map_; + LibertyPortTimingArcMap timing_arc_set_from_map_; + LibertyPortTimingArcMap timing_arc_set_to_map_; + TimingArcAttrsSeq timing_arc_attrs_; + bool has_infered_reg_timing_arcs_; + InternalPowerSeq internal_powers_; + InternalPowerAttrsSeq internal_power_attrs_; + LeakagePowerSeq leakage_powers_; + SequentialSeq sequentials_; + PortToSequentialMap port_to_seq_map_; + BusDclMap bus_dcls_; + ModeDefMap mode_defs_; + ScaleFactors *scale_factors_; + 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_; + // Ports that have latch D->Q timing arc sets from them. + LibertyPortSet latch_data_ports_; + float ocv_arc_depth_; + OcvDerate *ocv_derate_; + OcvDerateMap ocv_derate_map_; + bool is_disabled_constraint_; + Vector corner_cells_; + float leakage_power_; + bool leakage_power_exists_; + LibertyPgPortMap pg_port_map_; + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyCell); + + friend class LibertyLibrary; + friend class LibertyCellPortIterator; + friend class LibertyPort; + friend class LibertyBuilder; + friend class LibertyCellTimingArcSetIterator; + friend class LibertyCellSequentialIterator; +}; + +class LibertyCellPortIterator : public Iterator +{ +public: + explicit LibertyCellPortIterator(const LibertyCell *cell); + bool hasNext(); + LibertyPort *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyCellPortIterator); + + ConcretePortSeq::ConstIterator iter_; +}; + +class LibertyCellPortBitIterator : public Iterator +{ +public: + explicit LibertyCellPortBitIterator(const LibertyCell *cell); + virtual ~LibertyCellPortBitIterator(); + bool hasNext(); + LibertyPort *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyCellPortBitIterator); + + ConcreteCellPortBitIterator *iter_; +}; + +class LibertyCellTimingArcSetIterator : public TimingArcSetSeq::ConstIterator +{ +public: + LibertyCellTimingArcSetIterator(const LibertyCell *cell); + // from or to may be nullptr to wildcard. + LibertyCellTimingArcSetIterator(const LibertyCell *cell, + const LibertyPort *from, + const LibertyPort *to); +}; + +class LibertyCellSequentialIterator : public SequentialSeq::ConstIterator +{ +public: + LibertyCellSequentialIterator(const LibertyCell *cell) : + SequentialSeq::ConstIterator(cell->sequentials_) {} +}; + +class LibertyCellLeakagePowerIterator : public LeakagePowerSeq::Iterator +{ +public: + LibertyCellLeakagePowerIterator(LibertyCell *cell) : + LeakagePowerSeq::Iterator(cell->leakagePowers()) {} +}; + +class LibertyCellInternalPowerIterator : public InternalPowerSeq::Iterator +{ +public: + LibertyCellInternalPowerIterator(LibertyCell *cell) : + InternalPowerSeq::Iterator(cell->internalPowers()) {} +}; + +//////////////////////////////////////////////////////////////// + +class LibertyPort : public ConcretePort +{ +public: + LibertyCell *libertyCell() const { return liberty_cell_; } + LibertyPort *findLibertyMember(int index) const; + LibertyPort *findLibertyBusBit(int index) const; + float capacitance(const TransRiseFall *tr, + const MinMax *min_max) const; + void capacitance(const TransRiseFall *tr, + 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 TransRiseFall *tr, + const MinMax *min_max, + const OperatingConditions *op_cond, + const Pvt *pvt) const; + bool capacitanceIsOneValue() const; + void setCapacitance(float cap); + void setCapacitance(const TransRiseFall *tr, + const MinMax *min_max, + float cap); + float driveResistance(const TransRiseFall *tr, + const MinMax *min_max) const; + // Max of rise/fall. + float driveResistance() const; + FuncExpr *function() const { return function_; } + void setFunction(FuncExpr *func); + FuncExpr *&functionRef() { return function_; } + // Tristate enable function. + FuncExpr *tristateEnable() const { return tristate_enable_; } + void setTristateEnable(FuncExpr *enable); + FuncExpr *&tristateEnableRef() { return tristate_enable_; } + void slewLimit(const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const; + void setSlewLimit(float slew, + const MinMax *min_max); + void capacitanceLimit(const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const; + void setCapacitanceLimit(float cap, + const MinMax *min_max); + void fanoutLimit(const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const; + void setFanoutLimit(float fanout, + const MinMax *min_max); + void minPeriod(const OperatingConditions *op_cond, + const Pvt *pvt, + float &min_period, + bool &exists) const; + // Unscaled value. + void minPeriod(float &min_period, + bool &exists) const; + void setMinPeriod(float min_period); + // high = rise, low = fall + void minPulseWidth(const TransRiseFall *hi_low, + const OperatingConditions *op_cond, + const Pvt *pvt, + float &min_width, + bool &exists) const; + // Unscaled value. + void minPulseWidth(const TransRiseFall *hi_low, + float &min_width, + bool &exists) const; + void setMinPulseWidth(TransRiseFall *hi_low, + float min_width); + bool isClock() const; + void setIsClock(bool is_clk); + bool isClockGateClockPin() const { return is_clk_gate_clk_pin_; } + void setIsClockGateClockPin(bool is_clk_gate_clk); + bool isClockGateEnablePin() const { return is_clk_gate_enable_pin_; } + void setIsClockGateEnablePin(bool is_clk_gate_enable); + bool isClockGateOutPin() const { return is_clk_gate_out_pin_; } + void setIsClockGateOutPin(bool is_clk_gate_out); + bool isPllFeedbackPin() const { return is_pll_feedback_pin_; } + void setIsPllFeedbackPin(bool is_pll_feedback_pin); + // Has register/latch rise/fall edges from pin. + bool isRegClk() const { return is_reg_clk_; } + void setIsRegClk(bool is_clk); + // Is the clock for timing checks. + bool isCheckClk() const { return is_check_clk_; } + void setIsCheckClk(bool is_clk); + TransRiseFall *pulseClkTrigger() const { return pulse_clk_trigger_; } + // Rise for high, fall for low. + TransRiseFall *pulseClkSense() const { return pulse_clk_sense_; } + void setPulseClk(TransRiseFall *trigger, + TransRiseFall *sense); + bool isDisabledConstraint() const { return is_disabled_constraint_; } + void setIsDisabledConstraint(bool is_disabled); + LibertyPort *cornerPort(int ap_index); + void setCornerPort(LibertyPort *corner_port, + int ap_index); + const char *relatedGroundPin() const { return related_ground_pin_; } + void setRelatedGroundPin(const char *related_ground_pin); + const char *relatedPowerPin() const { return related_power_pin_; } + void setRelatedPowerPin(const char *related_power_pin); + + static bool equiv(const LibertyPort *port1, + const LibertyPort *port2); + static bool less(const LibertyPort *port1, + const LibertyPort *port2); + +protected: + // Constructor is internal to LibertyBuilder. + LibertyPort(LibertyCell *cell, + const char *name, + bool is_bus, + int from_index, + int to_index, + bool is_bundle, + ConcretePortSeq *members); + virtual ~LibertyPort(); + void setDirection(PortDirection *dir); + void setMinPort(LibertyPort *min); + void addScaledPort(OperatingConditions *op_cond, + LibertyPort *scaled_port); + + LibertyCell *liberty_cell_; + FuncExpr *function_; + FuncExpr *tristate_enable_; + ScaledPortMap *scaled_ports_; + RiseFallMinMax capacitance_; + MinMaxFloatValues slew_limit_; // inputs and outputs + MinMaxFloatValues cap_limit_; // outputs + MinMaxFloatValues fanout_limit_; // outputs + float min_period_; + float min_pulse_width_[TransRiseFall::index_count]; + TransRiseFall *pulse_clk_trigger_; + TransRiseFall *pulse_clk_sense_; + const char *related_ground_pin_; + const char *related_power_pin_; + Vector corner_ports_; + + unsigned int min_pulse_width_exists_:TransRiseFall::index_count; + bool min_period_exists_:1; + bool is_clk_:1; + bool is_reg_clk_:1; + bool is_check_clk_:1; + bool is_clk_gate_clk_pin_:1; + bool is_clk_gate_enable_pin_:1; + bool is_clk_gate_out_pin_:1; + bool is_pll_feedback_pin_:1; + bool is_disabled_constraint_:1; + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyPort); + + friend class LibertyLibrary; + friend class LibertyCell; + friend class LibertyBuilder; + friend class LibertyReader; +}; + +void +sortLibertyPortSet(LibertyPortSet *set, + LibertyPortSeq &ports); + +class LibertyPortMemberIterator : public Iterator +{ +public: + explicit LibertyPortMemberIterator(const LibertyPort *port); + virtual ~LibertyPortMemberIterator(); + virtual bool hasNext(); + virtual LibertyPort *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyPortMemberIterator); + + ConcretePortMemberIterator *iter_; +}; + +// Process, voltage temperature. +class Pvt +{ +public: + Pvt(float process, + float voltage, + float temperature); + virtual ~Pvt() {} + float process() const { return process_; } + void setProcess(float process); + float voltage() const { return voltage_; } + void setVoltage(float voltage); + float temperature() const { return temperature_; } + void setTemperature(float temp); + +protected: + float process_; + float voltage_; + float temperature_; + +private: + DISALLOW_COPY_AND_ASSIGN(Pvt); +}; + +class OperatingConditions : public Pvt +{ +public: + explicit OperatingConditions(const char *name); + OperatingConditions(const char *name, + float process, + float voltage, + float temperature, + WireloadTree wire_load_tree); + virtual ~OperatingConditions(); + const char *name() const { return name_; } + WireloadTree wireloadTree() const { return wire_load_tree_; } + void setWireloadTree(WireloadTree tree); + +protected: + const char *name_; + WireloadTree wire_load_tree_; + +private: + DISALLOW_COPY_AND_ASSIGN(OperatingConditions); +}; + +class ScaleFactors +{ +public: + explicit ScaleFactors(const char *name); + ~ScaleFactors(); + const char *name() const { return name_; } + float scale(ScaleFactorType type, + ScaleFactorPvt pvt, + TransRiseFall *tr); + float scale(ScaleFactorType type, + ScaleFactorPvt pvt, + int tr_index); + float scale(ScaleFactorType type, + ScaleFactorPvt pvt); + void setScale(ScaleFactorType type, + ScaleFactorPvt pvt, + TransRiseFall *tr, + float scale); + void setScale(ScaleFactorType type, + ScaleFactorPvt pvt, + float scale); + void print(); + +protected: + const char *name_; + float scales_[scale_factor_type_count][int(ScaleFactorPvt::count)][TransRiseFall::index_count]; + +private: + DISALLOW_COPY_AND_ASSIGN(ScaleFactors); +}; + +class BusDcl +{ +public: + BusDcl(const char *name, + int from, + int to); + ~BusDcl(); + const char *name() const { return name_; } + int from() const { return from_; } + int to() const { return to_; } + +protected: + const char *name_; + int from_; + int to_; + +private: + DISALLOW_COPY_AND_ASSIGN(BusDcl); +}; + +// Cell mode_definition group. +class ModeDef +{ +public: + ~ModeDef(); + const char *name() const { return name_; } + ModeValueDef *defineValue(const char *value, + FuncExpr *cond, + const char *sdf_cond); + ModeValueDef *findValueDef(const char *value); + ModeValueMap *values() { return &values_; } + +protected: + // Private to LibertyCell::makeModeDef. + explicit ModeDef(const char *name); + + const char *name_; + ModeValueMap values_; + +private: + DISALLOW_COPY_AND_ASSIGN(ModeDef); + + friend class LibertyCell; +}; + +// Mode definition mode_value group. +class ModeValueDef +{ +public: + ~ModeValueDef(); + const char *value() const { return value_; } + FuncExpr *cond() const { return cond_; } + FuncExpr *&condRef() { return cond_; } + const char *sdfCond() const { return sdf_cond_; } + void setSdfCond(const char *sdf_cond); + +protected: + // Private to ModeDef::defineValue. + ModeValueDef(const char *value, + FuncExpr *cond, + const char *sdf_cond); + + const char *value_; + FuncExpr *cond_; + const char *sdf_cond_; + +private: + DISALLOW_COPY_AND_ASSIGN(ModeValueDef); + + friend class ModeDef; +}; + +class TableTemplate +{ +public: + explicit TableTemplate(const char *name); + TableTemplate(const char *name, + TableAxis *axis1, + TableAxis *axis2, + TableAxis *axis3); + ~TableTemplate(); + const char *name() const { return name_; } + void setName(const char *name); + TableAxis *axis1() const { return axis1_; } + void setAxis1(TableAxis *axis); + TableAxis *axis2() const { return axis2_; } + void setAxis2(TableAxis *axis); + TableAxis *axis3() const { return axis3_; } + void setAxis3(TableAxis *axis); + +protected: + const char *name_; + TableAxis *axis1_; + TableAxis *axis2_; + TableAxis *axis3_; + +private: + DISALLOW_COPY_AND_ASSIGN(TableTemplate); +}; + +class TestCell +{ +public: + TestCell(); + TestCell(LibertyPort *data_in, + LibertyPort *scan_in, + LibertyPort *scan_enable, + LibertyPort *scan_out, + LibertyPort *scan_out_inv); + LibertyPort *dataIn() const { return data_in_; } + void setDataIn(LibertyPort *port); + LibertyPort *scanIn() const { return scan_in_; } + void setScanIn(LibertyPort *port); + LibertyPort *scanEnable() const { return scan_enable_; } + void setScanEnable(LibertyPort *port); + LibertyPort *scanOut() const { return scan_out_; } + void setScanOut(LibertyPort *port); + LibertyPort *scanOutInv() const { return scan_out_inv_; } + void setScanOutInv(LibertyPort *port); + +protected: + LibertyPort *data_in_; + LibertyPort *scan_in_; + LibertyPort *scan_enable_; + LibertyPort *scan_out_; + LibertyPort *scan_out_inv_; + +private: + DISALLOW_COPY_AND_ASSIGN(TestCell); +}; + +class OcvDerate +{ +public: + OcvDerate(const char *name); + ~OcvDerate(); + const char *name() const { return name_; } + Table *derateTable(const TransRiseFall *tr, + const EarlyLate *early_late, + PathType path_type); + void setDerateTable(const TransRiseFall *tr, + const EarlyLate *early_late, + PathType path_type, + Table *derate); + +private: + const char *name_; + // [rf_type][derate_type][path_type] + Table *derate_[TransRiseFall::index_count][EarlyLate::index_count][path_type_count]; +}; + +// Power/ground port. +class LibertyPgPort +{ +public: + enum PgType { unknown, + primary_power, primary_ground, + backup_power, backup_ground, + internal_power, internal_ground, + nwell, pwell, + deepnwell, deeppwell}; + LibertyPgPort(const char *name, + LibertyCell *cell); + ~LibertyPgPort(); + const char *name() { return name_; } + LibertyCell *cell() const { return cell_; } + PgType pgType() const { return pg_type_; } + void setPgType(PgType type); + const char *voltageName() const { return voltage_name_; } + void setVoltageName(const char *voltage_name); + +private: + const char *name_; + PgType pg_type_; + const char *voltage_name_; + LibertyCell *cell_; +}; + +} // namespace +#endif diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc new file mode 100644 index 0000000..ad38c95 --- /dev/null +++ b/liberty/LibertyBuilder.cc @@ -0,0 +1,648 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "PortDirection.hh" +#include "TimingRole.hh" +#include "FuncExpr.hh" +#include "TimingArc.hh" +#include "InternalPower.hh" +#include "LeakagePower.hh" +#include "Sequential.hh" +#include "Liberty.hh" +#include "LibertyBuilder.hh" + +namespace sta { + +LibertyCell * +LibertyBuilder::makeCell(LibertyLibrary *library, + const char *name, + const char *filename) +{ + LibertyCell *cell = new LibertyCell(library, name, filename); + library->addCell(cell); + return cell; +} + +LibertyPort * +LibertyBuilder::makePort(LibertyCell *cell, + const char *name) +{ + LibertyPort *port = new LibertyPort(cell, name, false, -1, -1, false, nullptr); + cell->addPort(port); + return port; +} + +LibertyPort * +LibertyBuilder::makeBusPort(LibertyCell *cell, + const char *name, + int from_index, + int to_index) +{ + LibertyPort *port = new LibertyPort(cell, name, true, from_index, to_index, + false, new ConcretePortSeq); + cell->addPort(port); + makeBusPortBits(cell->library(), cell, port, name, from_index, to_index); + return port; +} + +void +LibertyBuilder::makeBusPortBits(ConcreteLibrary *library, + LibertyCell *cell, + ConcretePort *bus_port, + const char *name, + int from_index, + int to_index) +{ + if (from_index < to_index) { + for (int index = from_index; index <= to_index; index++) + makeBusPortBit(library, cell, bus_port, name, index); + } + else { + for (int index = from_index; index >= to_index; index--) + makeBusPortBit(library, cell, bus_port, name, index); + } +} + +void +LibertyBuilder::makeBusPortBit(ConcreteLibrary *library, + LibertyCell *cell, + ConcretePort *bus_port, + const char *bus_name, + int bit_index) +{ + char *bit_name = stringPrintTmp("%s%c%d%c", + bus_name, + library->busBrktLeft(), + bit_index, + library->busBrktRight()); + ConcretePort *port = makePort(cell, bit_name, bit_index); + bus_port->addPortBit(port); + cell->addPortBit(port); +} + +ConcretePort * +LibertyBuilder::makePort(LibertyCell *cell, + const char *bit_name, + int bit_index) +{ + ConcretePort *port = new LibertyPort(cell, bit_name, false, + bit_index, bit_index, false, nullptr); + return port; +} + +LibertyPort * +LibertyBuilder::makeBundlePort(LibertyCell *cell, + const char *name, + ConcretePortSeq *members) +{ + LibertyPort *port = new LibertyPort(cell, name, false, -1, -1, true, members); + cell->addPort(port); + return port; +} + +//////////////////////////////////////////////////////////////// + +TimingArcSet * +LibertyBuilder::makeTimingArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + TimingArcAttrs *attrs) +{ + FuncExpr *to_func; + Sequential *seq = nullptr; + switch (attrs->timingType()) { + case TimingType::combinational: + to_func = to_port->function(); + if (to_func && to_func->op() == FuncExpr::op_port) + seq = cell->outputPortSequential(to_func->port()); + if (seq && seq->isLatch()) + return makeLatchDtoQArcs(cell, from_port, to_port, related_out, attrs); + else + return makeCombinationalArcs(cell, from_port, to_port, related_out, + true, true, attrs); + case TimingType::combinational_fall: + return makeCombinationalArcs(cell, from_port, to_port, related_out, + false, true, attrs); + case TimingType::combinational_rise: + return makeCombinationalArcs(cell, from_port, to_port, related_out, + true, false, attrs); + case TimingType::setup_rising: + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + TransRiseFall::rise(), TimingRole::setup(), + attrs); + case TimingType::setup_falling: + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + TransRiseFall::fall(), TimingRole::setup(), + attrs); + case TimingType::hold_rising: + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + TransRiseFall::rise(), TimingRole::hold(), + attrs); + case TimingType::hold_falling: + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + TransRiseFall::fall(), TimingRole::hold(), + attrs); + case TimingType::rising_edge: + return makeRegLatchArcs(cell, from_port, to_port, related_out, + TransRiseFall::rise(), attrs); + case TimingType::falling_edge: + return makeRegLatchArcs(cell, from_port, to_port, related_out, + TransRiseFall::fall(), attrs); + case TimingType::preset: + return makePresetClrArcs(cell, from_port, to_port, related_out, + TransRiseFall::rise(), attrs); + case TimingType::clear: + return makePresetClrArcs(cell, from_port, to_port, related_out, + TransRiseFall::fall(), attrs); + case TimingType::recovery_rising: + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + TransRiseFall::rise(),TimingRole::recovery(), + attrs); + case TimingType::recovery_falling: + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + TransRiseFall::fall(),TimingRole::recovery(), + attrs); + case TimingType::removal_rising: + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + TransRiseFall::rise(), TimingRole::removal(), + attrs); + case TimingType::removal_falling: + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + TransRiseFall::fall(), TimingRole::removal(), + attrs); + case TimingType::three_state_disable: + return makeTristateDisableArcs(cell, from_port, to_port, related_out, + true, true, attrs); + case TimingType::three_state_disable_fall: + return makeTristateDisableArcs(cell, from_port, to_port, related_out, + false, true, attrs); + case TimingType::three_state_disable_rise: + return makeTristateDisableArcs(cell, from_port, to_port, related_out, + true, false, attrs); + case TimingType::three_state_enable: + return makeTristateEnableArcs(cell, from_port, to_port, related_out, + true, true, attrs); + case TimingType::three_state_enable_fall: + return makeTristateEnableArcs(cell, from_port, to_port, related_out, + false, true, attrs); + case TimingType::three_state_enable_rise: + return makeTristateEnableArcs(cell, from_port, to_port, related_out, + true, false, attrs); + case TimingType::skew_falling: + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + TransRiseFall::fall(), TimingRole::skew(), + attrs); + case TimingType::skew_rising: + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + TransRiseFall::rise(), TimingRole::skew(), + attrs); + case TimingType::non_seq_setup_rising: + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + TransRiseFall::rise(), + TimingRole::nonSeqSetup(), attrs); + case TimingType::non_seq_setup_falling: + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + TransRiseFall::fall(), + TimingRole::nonSeqSetup(), attrs); + case TimingType::non_seq_hold_rising: + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + TransRiseFall::rise(), + TimingRole::nonSeqHold(), + attrs); + case TimingType::non_seq_hold_falling: + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + TransRiseFall::fall(), + TimingRole::nonSeqHold(), + attrs); + case TimingType::min_pulse_width: + case TimingType::minimum_period: + case TimingType::nochange_high_high: + case TimingType::nochange_high_low: + case TimingType::nochange_low_high: + case TimingType::nochange_low_low: + case TimingType::retaining_time: + case TimingType::unknown: + case TimingType::min_clock_tree_path: + case TimingType::max_clock_tree_path: + return nullptr; + } + // Prevent warnings from lame compilers. + return nullptr; +} + +TimingArcSet * +LibertyBuilder::makeCombinationalArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + bool to_rise, + bool to_fall, + TimingArcAttrs *attrs) +{ + FuncExpr *func = to_port->function(); + TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, related_out, + TimingRole::combinational(), attrs); + TimingSense sense = attrs->timingSense(); + if (sense == TimingSense::unknown && func) { + // Timing sense not specified - find it from function. + sense = func->portTimingSense(from_port); + if (sense == TimingSense::none + && to_port->direction()->isAnyTristate()) { + // from_port is not an input to function, check tristate enable. + FuncExpr *enable = to_port->tristateEnable(); + if (enable && enable->hasPort(from_port)) + sense = TimingSense::non_unate; + } + } + TimingModel *model; + TransRiseFall *to_tr; + switch (sense) { + case TimingSense::positive_unate: + if (to_rise) { + to_tr = TransRiseFall::rise(); + model = attrs->model(to_tr); + if (model) + makeTimingArc(arc_set, TransRiseFall::rise(), to_tr, model); + } + if (to_fall) { + to_tr = TransRiseFall::fall(); + model = attrs->model(to_tr); + if (model) + makeTimingArc(arc_set, TransRiseFall::fall(), to_tr, model); + } + break; + case TimingSense::negative_unate: + if (to_fall) { + to_tr = TransRiseFall::fall(); + model = attrs->model(to_tr); + if (model) + makeTimingArc(arc_set, TransRiseFall::rise(), to_tr, model); + } + if (to_rise) { + to_tr = TransRiseFall::rise(); + model = attrs->model(to_tr); + if (model) + makeTimingArc(arc_set, TransRiseFall::fall(), to_tr, model); + } + break; + case TimingSense::non_unate: + case TimingSense::unknown: + // Timing sense none means function does not mention from_port. + // This can happen if the function references an internal port, + // as in fpga lut cells. + case TimingSense::none: + if (to_fall) { + to_tr = TransRiseFall::fall(); + model = attrs->model(to_tr); + if (model) { + makeTimingArc(arc_set, TransRiseFall::fall(), to_tr, model); + makeTimingArc(arc_set, TransRiseFall::rise(), to_tr, model); + } + } + if (to_rise) { + to_tr = TransRiseFall::rise(); + model = attrs->model(to_tr); + if (model) { + makeTimingArc(arc_set, TransRiseFall::rise(), to_tr, model); + makeTimingArc(arc_set, TransRiseFall::fall(), to_tr, model); + } + } + break; + } + return arc_set; +} + +TimingArcSet * +LibertyBuilder::makeLatchDtoQArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + TimingArcAttrs *attrs) +{ + TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, + related_out, + TimingRole::latchDtoQ(), attrs); + TimingModel *model; + TransRiseFall *to_tr = TransRiseFall::rise(); + model = attrs->model(to_tr); + TimingSense sense = attrs->timingSense(); + if (model) { + TransRiseFall *from_tr = (sense == TimingSense::negative_unate) ? + to_tr->opposite() : to_tr; + makeTimingArc(arc_set, from_tr, to_tr, model); + } + to_tr = TransRiseFall::fall(); + model = attrs->model(to_tr); + if (model) { + TransRiseFall *from_tr = (sense == TimingSense::negative_unate) ? + to_tr->opposite() : to_tr; + makeTimingArc(arc_set, from_tr, to_tr, model); + } + return arc_set; +} + +TimingArcSet * +LibertyBuilder::makeRegLatchArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + TransRiseFall *from_tr, + TimingArcAttrs *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)) { + TimingRole *role = seq->isRegister() ? + TimingRole::regClkToQ() : TimingRole::latchEnToQ(); + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + from_tr, role, attrs); + } + else if (seq->isLatch() + && seq->data() + && seq->data()->hasPort(from_port)) + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + from_tr, 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, related_out, + from_tr, TimingRole::regSetClr(), attrs); + } + } + // No associated ff/latch - assume register clk->q. + cell->setHasInferedRegTimingArcs(true); + return makeFromTransitionArcs(cell, from_port, to_port, related_out, + from_tr, TimingRole::regClkToQ(), attrs); +} + +TimingArcSet * +LibertyBuilder::makeFromTransitionArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + TransRiseFall *from_tr, + TimingRole *role, + TimingArcAttrs *attrs) +{ + TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, + related_out, role, attrs); + TimingModel *model; + TransRiseFall *to_tr; + to_tr = TransRiseFall::rise(); + model = attrs->model(to_tr); + if (model) + makeTimingArc(arc_set, from_tr, to_tr, model); + to_tr = TransRiseFall::fall(); + model = attrs->model(to_tr); + if (model) + makeTimingArc(arc_set, from_tr, to_tr, model); + return arc_set; +} + +TimingArcSet * +LibertyBuilder::makePresetClrArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + TransRiseFall *to_tr, + TimingArcAttrs *attrs) +{ + TimingArcSet *arc_set = nullptr; + TimingModel *model = attrs->model(to_tr); + if (model) { + arc_set = makeTimingArcSet(cell, from_port, to_port, related_out, + TimingRole::regSetClr(), attrs); + TransRiseFall *opp_tr = to_tr->opposite(); + switch (attrs->timingSense()) { + case TimingSense::positive_unate: + makeTimingArc(arc_set, to_tr, to_tr, model); + break; + case TimingSense::negative_unate: + makeTimingArc(arc_set, opp_tr, to_tr, model); + break; + case TimingSense::non_unate: + case TimingSense::unknown: + makeTimingArc(arc_set, to_tr, to_tr, model); + makeTimingArc(arc_set, opp_tr, to_tr, model); + break; + case TimingSense::none: + break; + } + } + return arc_set; +} + +// To rise/fall for Z transitions is as follows: +// 0Z, Z1 rise +// 1Z, Z0 fall +TimingArcSet * +LibertyBuilder::makeTristateEnableArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + bool to_rise, + bool to_fall, + TimingArcAttrs *attrs) +{ + TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, related_out, + TimingRole::tristateEnable(),attrs); + FuncExpr *tristate_enable = to_port->tristateEnable(); + TimingSense sense = attrs->timingSense(); + if (sense == TimingSense::unknown && tristate_enable) + sense = tristate_enable->portTimingSense(from_port); + TimingModel *model; + TransRiseFall *to_tr; + switch (sense) { + case TimingSense::positive_unate: + if (to_rise) { + to_tr = TransRiseFall::rise(); + model = attrs->model(to_tr); + if (model) + makeTimingArc(arc_set, Transition::rise(), Transition::trZ1(), model); + } + if (to_fall) { + to_tr = TransRiseFall::fall(); + model = attrs->model(to_tr); + if (model) + makeTimingArc(arc_set, Transition::rise(), Transition::trZ0(), model); + } + break; + case TimingSense::negative_unate: + if (to_rise) { + to_tr = TransRiseFall::rise(); + model = attrs->model(to_tr); + if (model) + makeTimingArc(arc_set, Transition::fall(), Transition::trZ1(), model); + } + if (to_fall) { + to_tr = TransRiseFall::fall(); + model = attrs->model(to_tr); + if (model) + makeTimingArc(arc_set, Transition::fall(), Transition::trZ0(), model); + } + break; + case TimingSense::non_unate: + case TimingSense::unknown: + if (to_rise) { + to_tr = TransRiseFall::rise(); + model = attrs->model(to_tr); + if (model) { + makeTimingArc(arc_set, Transition::rise(), Transition::trZ1(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::trZ1(), model); + } + } + if (to_fall) { + to_tr = TransRiseFall::fall(); + model = attrs->model(to_tr); + if (model) { + makeTimingArc(arc_set, Transition::rise(), Transition::trZ0(), model); + makeTimingArc(arc_set, Transition::fall(), Transition::trZ0(), model); + } + } + break; + case TimingSense::none: + break; + } + return arc_set; +} + +TimingArcSet * +LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + bool to_rise, + bool to_fall, + TimingArcAttrs *attrs) +{ + TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, + related_out, + TimingRole::tristateDisable(), + attrs); + TimingSense sense = attrs->timingSense(); + FuncExpr *tristate_enable = to_port->tristateEnable(); + if (sense == TimingSense::unknown && tristate_enable) + sense = timingSenseOpposite(tristate_enable->portTimingSense(from_port)); + TimingModel *model; + TransRiseFall *to_tr; + switch (sense) { + case TimingSense::positive_unate: + if (to_rise) { + to_tr = TransRiseFall::rise(); + model = attrs->model(to_tr); + if (model) + makeTimingArc(arc_set, Transition::rise(), Transition::tr0Z(), model); + } + if (to_fall) { + to_tr = TransRiseFall::fall(); + model = attrs->model(to_tr); + if (model) + makeTimingArc(arc_set, Transition::rise(), Transition::tr1Z(), model); + } + break; + case TimingSense::negative_unate: + if (to_rise) { + to_tr = TransRiseFall::rise(); + model = attrs->model(to_tr); + if (model) + makeTimingArc(arc_set, Transition::fall(), Transition::tr0Z(), model); + } + if (to_fall) { + to_tr = TransRiseFall::fall(); + model = attrs->model(to_tr); + if (model) + makeTimingArc(arc_set, Transition::fall(), Transition::tr1Z(), model); + } + break; + case TimingSense::non_unate: + case TimingSense::unknown: + if (to_rise) { + to_tr = TransRiseFall::rise(); + model = attrs->model(to_tr); + if (model) { + makeTimingArc(arc_set, Transition::fall(), Transition::tr0Z(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::tr0Z(), model); + } + } + if (to_fall) { + to_tr = TransRiseFall::fall(); + model = attrs->model(to_tr); + if (model) { + makeTimingArc(arc_set, Transition::fall(), Transition::tr1Z(), model); + makeTimingArc(arc_set, Transition::rise(), Transition::tr1Z(), model); + } + } + break; + case TimingSense::none: + break; + } + return arc_set; +} + +TimingArcSet * +LibertyBuilder::makeTimingArcSet(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + TimingRole *role, + TimingArcAttrs *attrs) +{ + return new TimingArcSet(cell, from, to, related_out, role, attrs); +} + +TimingArc * +LibertyBuilder::makeTimingArc(TimingArcSet *set, + TransRiseFall *from_tr, + TransRiseFall *to_tr, + TimingModel *model) +{ + return new TimingArc(set, from_tr->asTransition(), + to_tr->asTransition(), model); +} + +TimingArc * +LibertyBuilder::makeTimingArc(TimingArcSet *set, + Transition *from_tr, + Transition *to_tr, + TimingModel *model) +{ + return new TimingArc(set, from_tr, to_tr, model); +} + +//////////////////////////////////////////////////////////////// + +InternalPower * +LibertyBuilder::makeInternalPower(LibertyCell *cell, + LibertyPort *port, + LibertyPort *related_port, + InternalPowerAttrs *attrs) +{ + return new InternalPower(cell, port, related_port, attrs); +} + +LeakagePower * +LibertyBuilder::makeLeakagePower(LibertyCell *cell, + LeakagePowerAttrs *attrs) +{ + return new LeakagePower(cell, attrs); +} + +} // namespace diff --git a/liberty/LibertyBuilder.hh b/liberty/LibertyBuilder.hh new file mode 100644 index 0000000..53bd3b6 --- /dev/null +++ b/liberty/LibertyBuilder.hh @@ -0,0 +1,143 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_LIBERTY_BUILDER_H +#define STA_LIBERTY_BUILDER_H + +#include "DisallowCopyAssign.hh" +#include "Vector.hh" +#include "Transition.hh" +#include "LibertyClass.hh" + +namespace sta { + +class LibertyBuilder +{ +public: + LibertyBuilder() {} + virtual ~LibertyBuilder() {} + 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 *name, + int from_index, + int to_index); + virtual LibertyPort *makeBundlePort(LibertyCell *cell, + const char *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, + TimingArcAttrs *attrs); + InternalPower *makeInternalPower(LibertyCell *cell, + LibertyPort *port, + LibertyPort *related_port, + InternalPowerAttrs *attrs); + LeakagePower *makeLeakagePower(LibertyCell *cell, + LeakagePowerAttrs *attrs); + +protected: + ConcretePort *makeBusPort(const char *name, + int from_index, + int to_index, + ConcretePortSeq *members); + void makeBusPortBits(ConcreteLibrary *library, + LibertyCell *cell, + ConcretePort *bus_port, + const char *name, + int from_index, + int to_index); + // Bus port bit (internal to makeBusPortBits). + virtual ConcretePort *makePort(LibertyCell *cell, + const char *bit_name, + int bit_index); + void makeBusPortBit(ConcreteLibrary *library, + LibertyCell *cell, + ConcretePort *bus_port, + const char *name, + int index); + virtual TimingArcSet *makeTimingArcSet(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + TimingRole *role, + TimingArcAttrs *attrs); + virtual TimingArc *makeTimingArc(TimingArcSet *set, + Transition *from_tr, + Transition *to_tr, + TimingModel *model); + TimingArc *makeTimingArc(TimingArcSet *set, + TransRiseFall *from_tr, + TransRiseFall *to_tr, + TimingModel *model); + TimingArcSet *makeCombinationalArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + bool to_rise, + bool to_fall, + TimingArcAttrs *attrs); + TimingArcSet *makeLatchDtoQArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + TimingArcAttrs *attrs); + TimingArcSet *makeRegLatchArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + TransRiseFall *from_tr, + TimingArcAttrs *attrs); + TimingArcSet *makeFromTransitionArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + TransRiseFall *from_tr, + TimingRole *role, + TimingArcAttrs *attrs); + TimingArcSet *makePresetClrArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + TransRiseFall *to_tr, + TimingArcAttrs *attrs); + TimingArcSet *makeTristateEnableArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + bool to_rise, + bool to_fall, + TimingArcAttrs *attrs); + TimingArcSet *makeTristateDisableArcs(LibertyCell *cell, + LibertyPort *from_port, + LibertyPort *to_port, + LibertyPort *related_out, + bool to_rise, + bool to_fall, + TimingArcAttrs *attrs); +private: + DISALLOW_COPY_AND_ASSIGN(LibertyBuilder); +}; + +} // namespace +#endif diff --git a/liberty/LibertyClass.hh b/liberty/LibertyClass.hh new file mode 100644 index 0000000..12c3941 --- /dev/null +++ b/liberty/LibertyClass.hh @@ -0,0 +1,172 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_LIBERTY_CLASS_H +#define STA_LIBERTY_CLASS_H + +#include "Vector.hh" +#include "Map.hh" +#include "Set.hh" + +namespace sta { + +class Units; +class Unit; +class LibertyLibrary; +class LibertyCell; +class LibertyPort; +class Pvt; +class OperatingConditions; +class BusDcl; +class ModeDef; +class ModeValueDef; +class TestCell; +class TableTemplate; +class Table; +class TableModel; +class TableAxis; +class GateTimingModel; +class CheckTimingModel; +class ScaleFactors; +class Group; +class Wireload; +class WireloadSelection; +class TimingArcSet; +class TimingArc; +class InternalPower; +class LeakagePower; +class Sequential; +class FuncExpr; +class TimingModel; +class TimingRole; +class Transition; +class TransRiseFall; +class TransRiseFallBoth; +class LibertyCellSequentialIterator; + +typedef Vector LibertyLibrarySeq; +typedef Vector LibertyCellSeq; +typedef Vector SequentialSeq; +typedef Map LibertyCellEquivMap; +typedef Vector LibertyPortSeq; +typedef Set LibertyPortSet; +typedef std::pair LibertyPortPair; +typedef Set LibertyCellSet; +typedef Vector FloatSeq; +typedef Vector FloatTable; + +enum class ScaleFactorType : unsigned { + pin_cap, + wire_cap, + wire_res, + min_period, + // Liberty attributes have rise/fall suffix. + cell, + hold, + setup, + recovery, + removal, + nochange, + skew, + leakage_power, + internal_power, + // Liberty attributes have rise/fall prefix. + transition, + // Liberty attributes have low/high suffix (indexed as rise/fall). + min_pulse_width, + unknown, +}; +const int scale_factor_type_count = int(ScaleFactorType::unknown) + 1; +// Enough bits to hold a ScaleFactorType enum. +const int scale_factor_bits = 4; + +enum class WireloadTree { worst_case, best_case, balanced, unknown }; + +enum class WireloadMode { top, enclosed, segmented, unknown }; + +enum class TimingSense { + positive_unate, + negative_unate, + non_unate, + none, + unknown +}; +const int timing_sense_count = int(TimingSense::unknown) + 1; +const int timing_sense_bit_count = 3; + +enum class TableAxisVariable { + total_output_net_capacitance, + equal_or_opposite_output_net_capacitance, + input_net_transition, + input_transition_time, + related_pin_transition, + constrained_pin_transition, + output_pin_transition, + connect_delay, + related_out_total_output_net_capacitance, + time, + iv_output_voltage, + input_noise_width, + input_noise_height, + input_voltage, + output_voltage, + path_depth, + path_distance, + normalized_voltage, + unknown +}; + +enum class PathType { clk, data, clk_and_data }; +const int path_type_count = 2; + +// Rise/fall to rise/fall. +const int timing_arc_index_bit_count = 2; +const int timing_arc_index_max = (1<. + +#include "Machine.hh" +#include "Report.hh" +#include "StringUtil.hh" +#include "FuncExpr.hh" +#include "Liberty.hh" +#include "LibertyExprPvt.hh" + +extern int +LibertyExprParse_parse(); + +namespace sta { + +LibExprParser *libexpr_parser; + +FuncExpr * +parseFuncExpr(const char *func, + LibertyCell *cell, + const char *error_msg, + Report *report) +{ + if (func != nullptr && func[0] != '\0') { + LibExprParser parser(func, cell, error_msg, report); + libexpr_parser = &parser; + LibertyExprParse_parse(); + return parser.result(); + } + else + return nullptr; +} + +LibExprParser::LibExprParser(const char *func, + LibertyCell *cell, + const char *error_msg, + Report *report) : + func_(func), + cell_(cell), + error_msg_(error_msg), + report_(report), + result_(nullptr), + token_length_(100), + token_(new char[token_length_]), + token_next_(token_) +{ +} + +LibExprParser::~LibExprParser() +{ + stringDelete(token_); +} + +FuncExpr * +LibExprParser::makeFuncExprPort(const char *port_name) +{ + LibertyPort *port = cell_->findLibertyPort(port_name); + FuncExpr *expr = nullptr; + if (port) + expr = FuncExpr::makePort(port); + else + report_->error("%s references unknown port %s.\n", + error_msg_, port_name); + stringDelete(port_name); + return expr; +} + +FuncExpr * +LibExprParser::makeFuncExprNot(FuncExpr *arg) +{ + if (arg) + return FuncExpr::makeNot(arg); + else + return nullptr; +} + +FuncExpr * +LibExprParser::makeFuncExprXor(FuncExpr *arg1, + FuncExpr *arg2) +{ + if (arg1 && arg2) + return FuncExpr::makeXor(arg1, arg2); + else + return nullptr; +} + +FuncExpr * +LibExprParser::makeFuncExprAnd(FuncExpr *arg1, + FuncExpr *arg2) +{ + if (arg1 && arg2) + return FuncExpr::makeAnd(arg1, arg2); + else + return nullptr; +} + +FuncExpr * +LibExprParser::makeFuncExprOr(FuncExpr *arg1, + FuncExpr *arg2) +{ + if (arg1 && arg2) + return FuncExpr::makeOr(arg1, arg2); + else + return nullptr; +} + +void +LibExprParser::setResult(FuncExpr *result) +{ + result_ = result; +} + +size_t +LibExprParser::copyInput(char *buf, + size_t max_size) +{ + size_t length = strlen(func_); + if (length == 0) + return 0; + else { + size_t count; + + if (length < max_size) + count = length; + else + count = max_size; + strncpy(buf, func_, count); + func_ += count; + return count; + } +} + +char * +LibExprParser::tokenCopy() +{ + return stringCopy(token_); +} + +void +LibExprParser::tokenErase() +{ + token_next_ = token_; +} + +void +LibExprParser::tokenAppend(char ch) +{ + if (token_next_ + 1 - token_ >= static_cast(token_length_)) { + size_t index = token_next_ - token_; + token_length_ *= 2; + char *prev_token = token_; + token_ = new char[token_length_]; + strcpy(token_, prev_token); + stringDelete(prev_token); + token_next_ = &token_[index]; + } + *token_next_++ = ch; + // Make sure the token is always terminated. + *token_next_ = '\0'; +} + +void +LibExprParser::parseError(const char *msg) +{ + report_->printError("%s %s.\n", error_msg_, msg); +} + +} // namespace + +//////////////////////////////////////////////////////////////// +// Global namespace + +int +LibertyExprParse_error(const char *msg) +{ + sta::libexpr_parser->parseError(msg); + libertyExprFlushBuffer(); + return 0; +} diff --git a/liberty/LibertyExpr.hh b/liberty/LibertyExpr.hh new file mode 100644 index 0000000..7a2ae1d --- /dev/null +++ b/liberty/LibertyExpr.hh @@ -0,0 +1,35 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_LIBERTY_EXPR_H +#define STA_LIBERTY_EXPR_H + +#include "LibertyClass.hh" + +namespace sta { + +class Report; +class LibertyCell; +class FuncExpr; + +FuncExpr * +parseFuncExpr(const char *func, + LibertyCell *cell, + const char *error_msg, + Report *report); + +} // namespace +#endif diff --git a/liberty/LibertyExprLex.ll b/liberty/LibertyExprLex.ll new file mode 100644 index 0000000..8b24331 --- /dev/null +++ b/liberty/LibertyExprLex.ll @@ -0,0 +1,86 @@ +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +// Liberty function expression lexical analyzer + +#include "Machine.hh" +#include "Debug.hh" +#include "StringUtil.hh" +#include "LibertyExprPvt.hh" + +using sta::libexpr_parser; +using sta::stringCopy; +using sta::FuncExpr; + +#include "LibertyExprParse.hh" + +#define YY_NO_INPUT + +#define YY_INPUT(buf,result,max_size) \ + result = libexpr_parser->copyInput(buf, max_size) + +void +libertyExprFlushBuffer() +{ + YY_FLUSH_BUFFER; +} + +%} + +/* %option debug */ +%option noyywrap +%option nounput +%option never-interactive + +%x ESCAPED_STRING + +PORT [A-Za-z_]([A-Za-z0-9_\[\]])* +OP "'"|"!"|"^"|"*"|"&"|"+"|"|"|1|0 +PAREN "("|")" +BLANK [ \t\r] +ESCAPE \\ +QUOTE \" +EOL \r?\n + +%% + +{OP}|{PAREN} { return ((int) LibertyExprLex_text[0]); } + +{ESCAPE}{EOL} { /* I doubt that escaped returns get thru the parser */ } + +{ESCAPE}{QUOTE} { BEGIN(ESCAPED_STRING); libexpr_parser->tokenErase(); } + +. { libexpr_parser->tokenAppend(LibertyExprLex_text[0]); } + +{ESCAPE}{QUOTE} { + BEGIN(INITIAL); + LibertyExprParse_lval.string = libexpr_parser->tokenCopy(); + return PORT; + } + +{PORT} { + LibertyExprParse_lval.string = stringCopy(LibertyExprLex_text); + return PORT; + } + +{BLANK} {} + + /* Send out of bound characters to parser. */ +. { return (int) LibertyExprLex_text[0]; } + +%% diff --git a/liberty/LibertyExprParse.yy b/liberty/LibertyExprParse.yy new file mode 100644 index 0000000..b5217b6 --- /dev/null +++ b/liberty/LibertyExprParse.yy @@ -0,0 +1,86 @@ +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +// Liberty function expression parser. + +#include "Machine.hh" +#include "FuncExpr.hh" +#include "LibertyExpr.hh" +#include "LibertyExprPvt.hh" + +int LibertyExprLex_lex(); +#define LibertyExprParse_lex LibertyExprLex_lex + +%} + +%union { + int int_val; + const char *string; + sta::FuncExpr *expr; +} + +%token PORT +%left '+' '|' +%left '*' '&' +%left '^' +%left '!' '\'' + +%type PORT +%type expr terminal terminal_expr implicit_and + +%{ +%} + +%% + +result_expr: + expr { sta::libexpr_parser->setResult($1); } +| expr ';'{ sta::libexpr_parser->setResult($1); } +; + +terminal: + PORT { $$ = sta::libexpr_parser->makeFuncExprPort($1); } +| '0' { $$ = sta::FuncExpr::makeZero(); } +| '1' { $$ = sta::FuncExpr::makeOne(); } +| '(' expr ')' { $$ = $2; } +; + +terminal_expr: + terminal +| '!' terminal { $$ = sta::libexpr_parser->makeFuncExprNot($2); } +| terminal '\'' { $$ = sta::libexpr_parser->makeFuncExprNot($1); } +; + +implicit_and: + terminal_expr terminal_expr + { $$ = sta::libexpr_parser->makeFuncExprAnd($1, $2); } +| implicit_and terminal_expr + { $$ = sta::libexpr_parser->makeFuncExprAnd($1, $2); } +; + +expr: + terminal_expr +| implicit_and +| expr '+' expr { $$ = sta::libexpr_parser->makeFuncExprOr($1, $3); } +| expr '|' expr { $$ = sta::libexpr_parser->makeFuncExprOr($1, $3); } +| expr '*' expr { $$ = sta::libexpr_parser->makeFuncExprAnd($1, $3); } +| expr '&' expr { $$ = sta::libexpr_parser->makeFuncExprAnd($1, $3); } +| expr '^' expr { $$ = sta::libexpr_parser->makeFuncExprXor($1, $3); } +; + +%% diff --git a/liberty/LibertyExprPvt.hh b/liberty/LibertyExprPvt.hh new file mode 100644 index 0000000..896e55b --- /dev/null +++ b/liberty/LibertyExprPvt.hh @@ -0,0 +1,79 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_LIBERTY_EXPR_PVT_H +#define STA_LIBERTY_EXPR_PVT_H + +#include "DisallowCopyAssign.hh" + +namespace sta { + +class Report; +class LibertyCell; +class FuncExpr; + +class LibExprParser +{ +public: + LibExprParser(const char *func, + LibertyCell *cell, + const char *error_msg, + Report *report); + ~LibExprParser(); + FuncExpr *makeFuncExprPort(const char *port_name); + FuncExpr *makeFuncExprOr(FuncExpr *arg1, + FuncExpr *arg2); + FuncExpr *makeFuncExprAnd(FuncExpr *arg1, + FuncExpr *arg2); + FuncExpr *makeFuncExprXor(FuncExpr *arg1, + 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 tokenStart(); + const char *token(); + char *tokenCopy(); + void tokenErase(); + void tokenAppend(char ch); + +private: + DISALLOW_COPY_AND_ASSIGN(LibExprParser); + + const char *func_; + LibertyCell *cell_; + const char *error_msg_; + Report *report_; + FuncExpr *result_; + size_t token_length_; + char *token_; + char *token_next_; +}; + +extern LibExprParser *libexpr_parser; + +} // namespace + +// Global namespace + +void +libertyExprFlushBuffer(); +int +LibertyExprParse_error(const char *msg); + +#endif diff --git a/liberty/LibertyExt.cc b/liberty/LibertyExt.cc new file mode 100644 index 0000000..afea806 --- /dev/null +++ b/liberty/LibertyExt.cc @@ -0,0 +1,268 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +// 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 new file mode 100644 index 0000000..f55fc70 --- /dev/null +++ b/liberty/LibertyLex.ll @@ -0,0 +1,209 @@ +%{ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include "Machine.hh" +#include "LibertyParser.hh" +#include "LibertyParse.hh" + +#define YY_NO_INPUT + +#if defined(YY_FLEX_MAJOR_VERSION) \ + && defined(YY_FLEX_MINOR_VERSION) \ + && YY_FLEX_MAJOR_VERSION >=2 \ + && (YY_FLEX_MINOR_VERSION > 5 \ + || (YY_FLEX_MINOR_VERSION == 5 \ + && defined(YY_FLEX_SUBMINOR_VERSION) \ + && YY_FLEX_SUBMINOR_VERSION >= 31)) + #define INCLUDE_SUPPORTED +#endif + +static std::string string_buf; + +void +libertyParseFlushBuffer() +{ + YY_FLUSH_BUFFER; +} + +%} + +/* %option debug */ +%option noyywrap +%option nounput +%option never-interactive + +%x comment +%x qstring + +DIGIT [0-9] +ALPHA [a-zA-Z] +FLOAT [-+]?(({DIGIT}+\.?{DIGIT}*)|(\.{DIGIT}+))([Ee][-+]?{DIGIT}+)? +BLANK [ \t] +BUS_LEFT [\[<] +BUS_RIGHT [\]>] +BUS_SUB {BUS_LEFT}{DIGIT}+{BUS_RIGHT} +BUS_RANGE {BUS_LEFT}{DIGIT}+:{DIGIT}+{BUS_RIGHT} +PIN_NAME ({ALPHA}|_)({ALPHA}|{DIGIT}|_)* +BUS_NAME {PIN_NAME}({BUS_SUB}|{BUS_RANGE}) +BUS_NAME2 {PIN_NAME}{BUS_SUB}({BUS_SUB}|{BUS_RANGE}) +MIXED_NAME {BUS_NAME}_{PIN_NAME} +HNAME ({PIN_NAME}|{BUS_NAME}|{MIXED_NAME})([\/.]({PIN_NAME}|{BUS_NAME}|{MIXED_NAME}))+ +/* ocv_table_template(2D_ocv_template) */ +/* leakage_power_unit : 1pW; */ +/* default_operating_conditions : slow_100_3.00 ; */ +/* revision : 1.0.17; */ +/* default_wire_load : xc2v250-5_avg; */ +TOKEN ({ALPHA}|{DIGIT}|_)({ALPHA}|{DIGIT}|[._\-])* +/* bus_naming_style : %s[%d] ; */ +BUS_STYLE "%s"{BUS_LEFT}"%d"{BUS_RIGHT} +PUNCTUATION [,\:;|(){}+*&!'=] +TOKEN_END {PUNCTUATION}|[ \t\r\n] +EOL \r?\n +%% + +{PUNCTUATION} { return ((int) LibertyLex_text[0]); } + +{FLOAT}{TOKEN_END} { + /* Push back the TOKEN_END character. */ + yyless(LibertyLex_leng - 1); + LibertyParse_lval.number = static_cast(strtod(LibertyLex_text, + NULL)); + return FLOAT; + } + +{ALPHA}({ALPHA}|_|{DIGIT})*{TOKEN_END} { + /* Push back the TOKEN_END character. */ + yyless(LibertyLex_leng - 1); + LibertyParse_lval.string = sta::stringCopy(LibertyLex_text); + return KEYWORD; + } + +{PIN_NAME}{TOKEN_END} | +{BUS_NAME}{TOKEN_END} | +{BUS_NAME2}{TOKEN_END} | +{MIXED_NAME}{TOKEN_END} | +{HNAME}{TOKEN_END} | +{BUS_STYLE}{TOKEN_END} | +{TOKEN}{TOKEN_END} { + /* Push back the TOKEN_END character. */ + yyless(LibertyLex_leng - 1); + LibertyParse_lval.string = sta::stringCopy(LibertyLex_text); + return STRING; + } + +\\?{EOL} { sta::libertyIncrLine(); } + +"include_file"[ \t]*"(".+")"[ \t]*";"? { +#ifdef INCLUDE_SUPPORTED + if (sta::libertyInInclude()) + sta::libertyParseError("nested include_file's are not supported\n"); + else { + char *filename = &yytext[strlen("include_file")]; + /* Skip blanks between include_file and '('. */ + while (isspace(*filename) && *filename != '\0') + filename++; + /* Skip '('. */ + filename++; + /* Skip blanks between '(' and filename. */ + while (isspace(*filename) && *filename != '\0') + filename++; + char *filename_end = strpbrk(filename, ")"); + if (filename_end == NULL) + sta::libertyParseError("include_file missing ')'\n"); + else { + /* Trim trailing blanks. */ + while (isspace(filename_end[-1]) && filename_end > filename) + filename_end--; + *filename_end = '\0'; + FILE *stream = sta::libertyIncludeBegin(filename); + if (stream) { + yypush_buffer_state(yy_create_buffer(stream, YY_BUF_SIZE)); + BEGIN(INITIAL); + } + } + } +#else + sta::libertyParseError("include_file is not supported.\n"); +#endif +} + +"/*" BEGIN(comment); + + /* Straight out of the flex man page. */ +[^*\r\n]* /* eat anything that's not a '*' */ +"*"+[^*/\r\n]* /* eat up '*'s not followed by '/'s */ +{EOL} sta::libertyIncrLine(); +"*"+"/" BEGIN(INITIAL); + +\" { + string_buf.erase(); + BEGIN(qstring); + } + +\" { + BEGIN(INITIAL); + LibertyParse_lval.string = sta::stringCopy(string_buf.c_str()); + return STRING; + } + +{EOL} { + LibertyParse_error("unterminated string constant"); + BEGIN(INITIAL); + LibertyParse_lval.string = sta::stringCopy(string_buf.c_str()); + return STRING; + } + +\\{EOL} { + /* Line continuation. */ + sta::libertyIncrLine(); + } + +\\. { + /* Escaped character. */ + string_buf += '\\'; + string_buf += LibertyLex_text[1]; + } + +[^\\\r\n\"]+ { + /* Anything but escape, return or double quote */ + string_buf += LibertyLex_text; + } + +<> { + LibertyParse_error("unterminated string constant"); + BEGIN(INITIAL); + yyterminate(); + } + +{BLANK}* {} + /* Send out of bound characters to parser. */ +. { return (int) LibertyLex_text[0]; } + +<> { +#ifdef INCLUDE_SUPPORTED + if (sta::libertyInInclude()) { + sta::libertyIncludeEnd(); + yypop_buffer_state(); + } + else +#endif + yyterminate(); +} + +%% diff --git a/liberty/LibertyParse.yy b/liberty/LibertyParse.yy new file mode 100644 index 0000000..d7d1f7d --- /dev/null +++ b/liberty/LibertyParse.yy @@ -0,0 +1,179 @@ +%{ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include "Machine.hh" +#include "StringUtil.hh" +#include "LibertyParser.hh" + +int LibertyLex_lex(); +#define LibertyParse_lex LibertyLex_lex +// Use yacc generated parser errors. +#define YYERROR_VERBOSE + +%} + +%union { + char *string; + float number; + int line; + sta::LibertyAttrValue *attr_value; + sta::LibertyAttrValueSeq *attr_values; + sta::LibertyGroup *group; + sta::LibertyStmt *stmt; +} + +%token FLOAT +%token STRING KEYWORD + +%type statement complex_attr simple_attr variable group file +%type attr_values +%type attr_value simple_attr_value +%type string +%type line +%type volt_expr volt_token + +%start file + +%{ +%} + +%% + +file: + group + ; + +group: + KEYWORD '(' ')' line '{' + { sta::libertyGroupBegin($1, NULL, $4); } + '}' semi_opt + { $$ = sta::libertyGroupEnd(); } +| KEYWORD '(' ')' line '{' + { sta::libertyGroupBegin($1, NULL, $4); } + statements '}' semi_opt + { $$ = sta::libertyGroupEnd(); } +| KEYWORD '(' attr_values ')' line '{' + { sta::libertyGroupBegin($1, $3, $5); } + '}' semi_opt + { $$ = sta::libertyGroupEnd(); } +| KEYWORD '(' attr_values ')' line '{' + { sta::libertyGroupBegin($1, $3, $5); } + statements '}' semi_opt + { $$ = sta::libertyGroupEnd(); } + ; + +line: /* empty */ + { $$ = sta::libertyLine(); } + ; + +statements: + statement +| statements statement + ; + +statement: + simple_attr +| complex_attr +| group +| variable + ; + +simple_attr: + KEYWORD ':' simple_attr_value line semi_opt + { $$ = sta::makeLibertySimpleAttr($1, $3, $4); } + ; + +simple_attr_value: + attr_value +| volt_expr + { $$ = static_cast(NULL); } +/* Unquoted NOT function. */ +/* clocked_on : !CP; */ +| '!' string + { $$ = sta::makeLibertyStringAttrValue(sta::stringPrint("!%s", $2)); sta::stringDelete($2); } + ; + +complex_attr: + KEYWORD '(' ')' line semi_opt + { $$ = sta::makeLibertyComplexAttr($1, NULL, $4); } +| KEYWORD '(' attr_values ')' line semi_opt + { $$ = sta::makeLibertyComplexAttr($1, $3, $5); } + ; + +attr_values: + attr_value + { $$ = new sta::LibertyAttrValueSeq; + $$->push_back($1); + } +| attr_values ',' attr_value + { $1->push_back($3); + $$ = $1; + } +| attr_values attr_value + { $1->push_back($2); + $$ = $1; + } + ; + +variable: + string '=' FLOAT line semi_opt + { $$ = sta::makeLibertyVariable($1, $3, $4); } + ; + +string: + STRING + { $$ = $1; } +| KEYWORD + { $$ = $1; } + ; + +attr_value: + FLOAT + { $$ = sta::makeLibertyFloatAttrValue($1); } + | string + { $$ = sta::makeLibertyStringAttrValue($1); } + ; + +/* Voltage expressions are ignored. */ +volt_expr: + volt_token expr_op volt_token +| volt_expr expr_op volt_token + ; + +volt_token: + FLOAT +| KEYWORD + { sta::stringDelete($1); + $$ = 0.0; + } + ; + +expr_op: + '+' +| '-' +| '*' +| '/' + ; + +semi_opt: + /* empty */ +| semi_opt ';' + ; + +%% diff --git a/liberty/LibertyParser.cc b/liberty/LibertyParser.cc new file mode 100644 index 0000000..807f95f --- /dev/null +++ b/liberty/LibertyParser.cc @@ -0,0 +1,576 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include "Machine.hh" +#include "Report.hh" +#include "Error.hh" +#include "StringUtil.hh" +#include "LibertyParser.hh" + +// Global namespace + +int +LibertyParse_parse(); +extern FILE *LibertyLex_in; + +namespace sta { + +typedef Vector LibertyGroupSeq; + +static const char *liberty_filename; +static int liberty_line; +// Previous lex reader state for include files. +static const char *liberty_filename_prev; +static int liberty_line_prev; +static FILE *liberty_stream_prev; + +static LibertyGroupVisitor *liberty_group_visitor; +static LibertyGroupSeq liberty_group_stack; +static Report *liberty_report; + +static LibertyStmt * +makeLibertyDefine(LibertyAttrValueSeq *values, + int line); +static LibertyAttrType +attrValueType(const char *value_type_name); +static LibertyGroupType +groupType(const char *group_type_name); + +//////////////////////////////////////////////////////////////// + +void +parseLibertyFile(const char *filename, + LibertyGroupVisitor *library_visitor, + Report *report) +{ + LibertyLex_in = fopen(filename, "r"); + if (LibertyLex_in) { + liberty_group_visitor = library_visitor; + liberty_group_stack.clear(); + liberty_filename = filename; + liberty_filename_prev = nullptr; + liberty_stream_prev = nullptr; + liberty_line = 1; + liberty_report = report; + LibertyParse_parse(); + fclose(LibertyLex_in); + } + else + throw FileNotReadable(filename); +} + +void +libertyGroupBegin(const char *type, + LibertyAttrValueSeq *params, + int line) +{ + LibertyGroup *group = new LibertyGroup(type, params, line); + liberty_group_visitor->begin(group); + liberty_group_stack.push_back(group); +} + +LibertyGroup * +libertyGroupEnd() +{ + LibertyGroup *group = libertyGroup(); + liberty_group_visitor->end(group); + liberty_group_stack.pop_back(); + LibertyGroup *parent = + liberty_group_stack.empty() ? nullptr : liberty_group_stack.back(); + if (parent && liberty_group_visitor->save(group)) { + parent->addSubgroup(group); + return group; + } + else { + delete group; + return nullptr; + } +} + +//////////////////////////////////////////////////////////////// + +LibertyStmt::LibertyStmt(int line) : + line_(line) +{ +} + +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) +{ +} + +void +LibertyGroup::addSubgroup(LibertyGroup *subgroup) +{ + if (subgroups_ == nullptr) + subgroups_ = new LibertyGroupSeq; + subgroups_->push_back(subgroup); +} + +void +LibertyGroup::addDefine(LibertyDefine *define) +{ + 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; +} + +void +LibertyGroup::addAttribute(LibertyAttr *attr) +{ + if (attrs_ == nullptr) + attrs_ = new LibertyAttrSeq; + attrs_->push_back(attr); + if (attr_map_) + (*attr_map_)[attr->name()] = attr; +} + +LibertyGroup::~LibertyGroup() +{ + stringDelete(type_); + 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_; + } +} + +const char * +LibertyGroup::firstName() +{ + if (params_ && params_->size() > 0) { + LibertyAttrValue *value = (*params_)[0]; + if (value->isString()) + return value->stringValue(); + } + return nullptr; +} + +const char * +LibertyGroup::secondName() +{ + if (params_ && params_->size() > 1) { + LibertyAttrValue *value = (*params_)[1]; + if (value->isString()) + return value->stringValue(); + } + return nullptr; +} + +LibertyAttr * +LibertyGroup::findAttr(const char *name) +{ + 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); + } + else + return nullptr; +} + +LibertySubgroupIterator::LibertySubgroupIterator(LibertyGroup *group) : + LibertyGroupSeq::Iterator(group->subgroups()) +{ +} + +LibertyAttrIterator::LibertyAttrIterator(LibertyGroup *group) : + LibertyAttrSeq::Iterator(group->attrs()) +{ +} + +//////////////////////////////////////////////////////////////// + +LibertyAttr::LibertyAttr(const char *name, + int line) : + LibertyStmt(line), + name_(name) +{ +} + +LibertyAttr::~LibertyAttr() +{ + stringDelete(name_); +} + +LibertyStmt * +makeLibertySimpleAttr(const char *name, + LibertyAttrValue *value, + int line) +{ + LibertyAttr *attr = new LibertySimpleAttr(name, value, line); + if (liberty_group_visitor) + liberty_group_visitor->visitAttr(attr); + LibertyGroup *group = libertyGroup(); + if (group && liberty_group_visitor->save(attr)) { + group->addAttribute(attr); + return attr; + } + else { + delete attr; + return nullptr; + } +} + +LibertyGroup * +libertyGroup() +{ + return liberty_group_stack.back(); +} + +LibertySimpleAttr::LibertySimpleAttr(const char *name, + LibertyAttrValue *value, + int line) : + LibertyAttr(name, line), + value_(value) +{ +} + +LibertySimpleAttr::~LibertySimpleAttr() +{ + delete value_; +} + +LibertyAttrValueSeq * +LibertySimpleAttr::values() const +{ + internalError("valueIterator called for LibertySimpleAttribute"); + return nullptr; +} + +LibertyStmt * +makeLibertyComplexAttr(const char *name, + LibertyAttrValueSeq *values, + int line) +{ + // Defines have the same syntax as complex attributes. + // Detect and convert them. + if (stringEq(name, "define")) { + LibertyStmt *define = makeLibertyDefine(values, line); + stringDelete(name); + LibertyAttrValueSeq::Iterator attr_iter(values); + while (attr_iter.hasNext()) + delete attr_iter.next(); + delete values; + return define; + } + else { + LibertyAttr *attr = new LibertyComplexAttr(name, values, line); + if (liberty_group_visitor) + liberty_group_visitor->visitAttr(attr); + if (liberty_group_visitor->save(attr)) { + LibertyGroup *group = libertyGroup(); + group->addAttribute(attr); + return attr; + } + else { + delete attr; + return nullptr; + } + } +} + +LibertyComplexAttr::LibertyComplexAttr(const char *name, + LibertyAttrValueSeq *values, + int line) : + LibertyAttr(name, line), + values_(values) +{ +} + +LibertyComplexAttr::~LibertyComplexAttr() +{ + if (values_) { + values_->deleteContents(); + delete values_; + } +} + +LibertyAttrValue * +LibertyComplexAttr::firstValue() +{ + if (values_ && values_->size() > 0) + return (*values_)[0]; + else + return nullptr; +} + +LibertyAttrValue * +makeLibertyStringAttrValue(char *value) +{ + return new LibertyStringAttrValue(value); +} + +LibertyStringAttrValue::LibertyStringAttrValue(const char *value) : + LibertyAttrValue(), + value_(value) +{ +} + +LibertyStringAttrValue::~LibertyStringAttrValue() +{ + stringDelete(value_); +} + +float +LibertyStringAttrValue::floatValue() +{ + internalError("LibertyStringAttrValue called for float value"); + return 0.0; +} + +const char * +LibertyStringAttrValue::stringValue() +{ + return value_; +} + +LibertyAttrValue * +makeLibertyFloatAttrValue(float value) +{ + return new LibertyFloatAttrValue(value); +} + +LibertyFloatAttrValue::LibertyFloatAttrValue(float value) : + value_(value) +{ +} + +float +LibertyFloatAttrValue::floatValue() +{ + return value_; +} + +const char * +LibertyFloatAttrValue::stringValue() +{ + internalError("LibertyStringAttrValue called for float value"); + return nullptr; +} + +//////////////////////////////////////////////////////////////// + +static LibertyStmt * +makeLibertyDefine(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(); + LibertyAttrType value_type = attrValueType(value_type_name); + LibertyGroupType group_type = groupType(group_type_name); + define = new LibertyDefine(stringCopy(define_name), group_type, + value_type, line); + LibertyGroup *group = libertyGroup(); + group->addDefine(define); + } + else + liberty_report->fileWarn(liberty_filename, line, + "define does not have three arguments.\n"); + return define; +} + +// The Liberty User Guide Version 2001.08 fails to define the strings +// used to define valid attribute types. Beyond "string" these are +// guesses. +static LibertyAttrType +attrValueType(const char *value_type_name) +{ + if (stringEq(value_type_name, "string")) + return LibertyAttrType::attr_string; + else if (stringEq(value_type_name, "integer")) + return LibertyAttrType::attr_int; + else if (stringEq(value_type_name, "float")) + return LibertyAttrType::attr_double; + else if (stringEq(value_type_name, "boolean")) + return LibertyAttrType::attr_boolean; + else + return LibertyAttrType::attr_unknown; +} + +static LibertyGroupType +groupType(const char *group_type_name) +{ + if (stringEq(group_type_name, "library")) + return LibertyGroupType::library; + else if (stringEq(group_type_name, "cell")) + return LibertyGroupType::cell; + else if (stringEq(group_type_name, "pin")) + return LibertyGroupType::pin; + else if (stringEq(group_type_name, "timing")) + return LibertyGroupType::timing; + else + return LibertyGroupType::unknown; +} + +LibertyDefine::LibertyDefine(const char *name, + LibertyGroupType group_type, + LibertyAttrType value_type, + int line) : + LibertyStmt(line), + name_(name), + group_type_(group_type), + value_type_(value_type) +{ +} + +LibertyDefine::~LibertyDefine() +{ + stringDelete(name_); +} + +//////////////////////////////////////////////////////////////// + +LibertyStmt * +makeLibertyVariable(char *var, + float value, + int line) +{ + LibertyVariable *variable = new LibertyVariable(var, value, line); + liberty_group_visitor->visitVariable(variable); + if (liberty_group_visitor->save(variable)) + return variable; + else { + delete variable; + return nullptr; + } +} + +LibertyVariable::LibertyVariable(const char *var, + float value, + int line) : + LibertyStmt(line), + var_(var), + value_(value) +{ +} + +LibertyVariable::~LibertyVariable() +{ + stringDelete(var_); +} + +//////////////////////////////////////////////////////////////// + +bool +libertyInInclude() +{ + return liberty_filename_prev != nullptr; +} + +FILE * +libertyIncludeBegin(const char *filename) +{ + FILE *stream = fopen(filename, "r" ); + if (stream == nullptr) + libertyParseError("cannot open include file %s.\n", filename); + else { + liberty_filename_prev = liberty_filename; + liberty_line_prev = liberty_line; + liberty_stream_prev = LibertyLex_in; + + liberty_filename = filename; + liberty_line = 1; + } + return stream; +} + +void +libertyIncludeEnd() +{ + fclose(LibertyLex_in); + liberty_filename = liberty_filename_prev; + liberty_line = liberty_line_prev; + LibertyLex_in = liberty_stream_prev; + liberty_filename_prev = nullptr; + liberty_stream_prev = nullptr; +} + +void +libertyIncrLine() +{ + sta::liberty_line++; +} + +int +libertyLine() +{ + return liberty_line; +} + +void +libertyParseError(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + sta::liberty_report->vfileError(sta::liberty_filename, sta::liberty_line, + fmt, args); + va_end(args); +} + +} // namespace + +//////////////////////////////////////////////////////////////// +// Global namespace + +void libertyParseFlushBuffer(); + +int +LibertyParse_error(const char *msg) +{ + sta::liberty_report->fileError(sta::liberty_filename, sta::liberty_line, + "%s.\n", msg); + libertyParseFlushBuffer(); + return 0; +} diff --git a/liberty/LibertyParser.hh b/liberty/LibertyParser.hh new file mode 100644 index 0000000..6f79b0e --- /dev/null +++ b/liberty/LibertyParser.hh @@ -0,0 +1,353 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_LIBERTY_PARSER_H +#define STA_LIBERTY_PARSER_H + +#include "DisallowCopyAssign.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 LibertyAttrValue; +class LibertyVariable; +class LibertySubgroupIterator; +class LibertyAttrIterator; + +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; + +enum class LibertyAttrType { attr_string, attr_int, attr_double, + attr_boolean, attr_unknown }; + +enum class LibertyGroupType { library, cell, pin, timing, unknown }; + +// Abstract base class for liberty statements. +class LibertyStmt +{ +public: + explicit 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; } + +protected: + int line_; + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyStmt); +}; + +// Groups are a type keyword with a set of parameters and statements +// enclosed in brackets. +// type([param1][, param2]...) { stmts.. } +class LibertyGroup : public LibertyStmt +{ +public: + LibertyGroup(const char *type, + LibertyAttrValueSeq *params, + int line); + virtual ~LibertyGroup(); + virtual bool isGroup() const { return true; } + const char *type() const { return type_; } + // First param as a string. + const char *firstName(); + // Second param as a string. + const char *secondName(); + LibertyAttr *findAttr(const char *name); + void addSubgroup(LibertyGroup *subgroup); + 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); + + const char *type_; + LibertyAttrValueSeq *params_; + LibertyAttrSeq *attrs_; + LibertyAttrMap *attr_map_; + LibertyGroupSeq *subgroups_; + LibertyDefineMap *define_map_; + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyGroup); +}; + +class LibertySubgroupIterator : public LibertyGroupSeq::Iterator +{ +public: + explicit LibertySubgroupIterator(LibertyGroup *group); + +private: + DISALLOW_COPY_AND_ASSIGN(LibertySubgroupIterator); +}; + +class LibertyAttrIterator : public LibertyAttrSeq::Iterator +{ +public: + explicit LibertyAttrIterator(LibertyGroup *group); + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyAttrIterator); +}; + +// Abstract base class for attributes. +class LibertyAttr : public LibertyStmt +{ +public: + LibertyAttr(const char *name, + int line); + virtual ~LibertyAttr(); + const char *name() const { return name_; } + 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: + const char *name_; + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyAttr); +}; + +// Abstract base class for simple attributes. +// name : value; +class LibertySimpleAttr : public LibertyAttr +{ +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; + +private: + DISALLOW_COPY_AND_ASSIGN(LibertySimpleAttr); + + 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: + DISALLOW_COPY_AND_ASSIGN(LibertyComplexAttr); + + 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; + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyAttrValue); +}; + +class LibertyStringAttrValue : public LibertyAttrValue +{ +public: + explicit LibertyStringAttrValue(const char *value); + virtual ~LibertyStringAttrValue(); + virtual bool isFloat() { return false; } + virtual bool isString() { return true; } + virtual float floatValue(); + virtual const char *stringValue(); + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyStringAttrValue); + + const char *value_; +}; + +class LibertyFloatAttrValue : public LibertyAttrValue +{ +public: + explicit LibertyFloatAttrValue(float value); + virtual bool isString() { return false; } + virtual bool isFloat() { return true; } + virtual float floatValue(); + virtual const char *stringValue(); + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyFloatAttrValue); + + float value_; +}; + +// Define statements define new simple attributes. +// define(attribute_name, group_name, attribute_type); +// attribute_type is string|integer|float. +class LibertyDefine : public LibertyStmt +{ +public: + LibertyDefine(const char *name, + LibertyGroupType group_type, + LibertyAttrType value_type, + int line); + virtual ~LibertyDefine(); + virtual bool isDefine() const { return true; } + const char *name() const { return name_; } + LibertyGroupType groupType() const { return group_type_; } + LibertyAttrType valueType() const { return value_type_; } + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyDefine); + + const char *name_; + LibertyGroupType group_type_; + LibertyAttrType value_type_; +}; + +// The Liberty User Guide Version 2003.12 fails to document variables. +// 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 +{ +public: + LibertyVariable(const char *var, + float value, + int line); + // var_ is NOT deleted by ~LibertyVariable because the group + // variable map ref's it. + virtual ~LibertyVariable(); + virtual bool isVariable() const { return true; } + const char *variable() const { return var_; } + float value() const { return value_; } + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyVariable); + + const char *var_; + float value_; +}; + +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 visitVariable(LibertyVariable *variable) = 0; + // 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; + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyGroupVisitor); +}; + +FILE * +libertyIncludeBegin(const char *filename); +void +libertyIncludeEnd(); +bool +libertyInInclude(); +void +libertyIncrLine(); +void +libertyParseError(const char *fmt, + ...); +int +libertyLine(); + +void +parseLibertyFile(const char *filename, + LibertyGroupVisitor *library_visitor, + Report *report); +void +libertyGroupBegin(const char *type, + LibertyAttrValueSeq *params, + int line); +LibertyGroup * +libertyGroupEnd(); +LibertyGroup * +libertyGroup(); +LibertyStmt * +makeLibertyComplexAttr(const char *name, + LibertyAttrValueSeq *values, + int line); +LibertyStmt * +makeLibertySimpleAttr(const char *name, + LibertyAttrValue *value, + int line); +LibertyAttrValue * +makeLibertyFloatAttrValue(float value); +LibertyAttrValue * +makeLibertyStringAttrValue(char *value); +LibertyStmt * +makeLibertyVariable(char *var, + float value, + int line); + +} // namespace + +// Global namespace. +int +LibertyParse_error(const char *msg); + +#endif diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc new file mode 100644 index 0000000..880a682 --- /dev/null +++ b/liberty/LibertyReader.cc @@ -0,0 +1,5276 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include "Machine.hh" +#include "Report.hh" +#include "Debug.hh" +#include "TokenParser.hh" +#include "Network.hh" +#include "Units.hh" +#include "PortDirection.hh" +#include "Transition.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 "LibertyExpr.hh" +#include "ParseBus.hh" +#include "Liberty.hh" +#include "LibertyBuilder.hh" +#include "LibertyReader.hh" +#include "LibertyReaderPvt.hh" + +namespace sta { + +static void +scaleFloats(FloatSeq *floats, + float scale); + +LibertyLibrary * +readLibertyFile(const char *filename, + bool infer_latches, + Network *network) +{ + LibertyBuilder builder; + LibertyReader reader(&builder); + return reader.readLibertyFile(filename, infer_latches, network); +} + +LibertyReader::LibertyReader(LibertyBuilder *builder) : + LibertyGroupVisitor(), + builder_(builder) +{ + defineVisitors(); +} + +LibertyReader::~LibertyReader() +{ + if (var_map_) { + LibertyVariableMap::Iterator iter(var_map_); + while (iter.hasNext()) { + const char *var; + float value; + iter.next(var, value); + stringDelete(var); + } + delete var_map_; + } + + // Scaling factor attribute names are allocated, so delete them. + LibraryAttrMap::Iterator attr_iter(attr_visitor_map_); + while (attr_iter.hasNext()) { + const char *attr_name; + LibraryAttrVisitor visitor; + attr_iter.next(attr_name, visitor); + stringDelete(attr_name); + } +} + +LibertyLibrary * +LibertyReader::readLibertyFile(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_ = nullptr; + port_group_ = nullptr; + saved_ports_ = nullptr; + saved_port_group_ = nullptr; + in_bus_ = false; + in_bundle_ = false; + sequential_ = nullptr; + timing_ = nullptr; + internal_power_ = nullptr; + leakage_power_ = nullptr; + table_ = nullptr; + table_model_scale_ = 1.0; + mode_def_ = nullptr; + mode_value_ = nullptr; + ocv_derate_ = nullptr; + pg_port_ = nullptr; + have_resistance_unit_ = false; + + for (auto tr_index : TransRiseFall::rangeIndex()) { + have_input_threshold_[tr_index] = false; + have_output_threshold_[tr_index] = false; + have_slew_lower_threshold_[tr_index] = false; + have_slew_upper_threshold_[tr_index] = false; + } + + parseLibertyFile(filename, this, report_); + return library_; +} + +void +LibertyReader::defineGroupVisitor(const char *type, + LibraryGroupVisitor begin_visitor, + LibraryGroupVisitor end_visitor) +{ + group_begin_map_[type] = begin_visitor; + group_end_map_[type] = end_visitor; +} + +void +LibertyReader::defineAttrVisitor(const char *attr_name, + LibraryAttrVisitor visitor) +{ + attr_visitor_map_[stringCopy(attr_name)] = visitor; +} + +void +LibertyReader::defineVisitors() +{ + 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("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); + + 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", &LibertyReader::visitIsMacro); + defineAttrVisitor("is_pad", &LibertyReader::visitIsPad); + defineAttrVisitor("interface_timing", &LibertyReader::visitInterfaceTiming); + defineAttrVisitor("scaling_factors", &LibertyReader::visitScalingFactors); + + 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("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); + + 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); + + 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); + 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); +} + +void +LibertyReader::defineScalingFactorVisitors() +{ + 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 < int(ScaleFactorPvt::count); pvt_index++) { + ScaleFactorPvt pvt = static_cast(pvt_index); + const char *pvt_name = scaleFactorPvtName(pvt); + if (scaleFactorTypeRiseFallSuffix(type)) { + for (auto tr : TransRiseFall::range()) { + const char *tr_name = (tr == TransRiseFall::rise()) ? "rise":"fall"; + const char *attr_name = stringPrintTmp("k_%s_%s_%s", + pvt_name, + type_name, + tr_name); + defineAttrVisitor(attr_name,&LibertyReader::visitScaleFactorSuffix); + } + } + else if (scaleFactorTypeRiseFallPrefix(type)) { + for (auto tr : TransRiseFall::range()) { + const char *tr_name = (tr == TransRiseFall::rise()) ? "rise":"fall"; + const char *attr_name = stringPrintTmp("k_%s_%s_%s", + pvt_name, + tr_name, + type_name); + defineAttrVisitor(attr_name,&LibertyReader::visitScaleFactorPrefix); + } + } + else if (scaleFactorTypeLowHighSuffix(type)) { + for (auto tr : TransRiseFall::range()) { + const char *tr_name = (tr == TransRiseFall::rise()) ? "high":"low"; + const char *attr_name = stringPrintTmp("k_%s_%s_%s", + pvt_name, + tr_name, + type_name); + defineAttrVisitor(attr_name,&LibertyReader::visitScaleFactorHiLow); + } + } + else { + const char *attr_name = stringPrintTmp("k_%s_%s", + pvt_name, + type_name); + defineAttrVisitor(attr_name,&LibertyReader::visitScaleFactor); + } + } + } +} + +void +LibertyReader::visitAttr(LibertyAttr *attr) +{ + LibraryAttrVisitor visitor = attr_visitor_map_.findKey(attr->name()); + if (visitor) + (this->*visitor)(attr); +} + +void +LibertyReader::begin(LibertyGroup *group) +{ + LibraryGroupVisitor visitor = group_begin_map_.findKey(group->type()); + if (visitor) + (this->*visitor)(group); +} + +void +LibertyReader::end(LibertyGroup *group) +{ + LibraryGroupVisitor visitor = group_end_map_.findKey(group->type()); + if (visitor) + (this->*visitor)(group); +} + +void +LibertyReader::beginLibrary(LibertyGroup *group) +{ + const char *name = group->firstName(); + if (name) { + LibertyLibrary *library = network_->findLiberty(name); + if (library) + libWarn(group, "library %s already exists.\n", 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_); + // 1ns default + time_scale_ = 1E-9F; + // 1ohm default + res_scale_ = 1.0F; + // pF default + cap_scale_ = 1E-12F; + // 1v default + volt_scale_ = 1; + // Default is 1mA. + current_scale_ = 1E-3F; + // Default is 1; + power_scale_ = 1; + // Default is fJ. + setEnergyScale(); + // Default is 1 micron. + distance_scale_ = 1e-6; + + library_->units()->timeUnit()->setScale(time_scale_); + library_->units()->capacitanceUnit()->setScale(cap_scale_); + library_->units()->resistanceUnit()->setScale(res_scale_); + library_->units()->voltageUnit()->setScale(volt_scale_); + library_->units()->currentUnit()->setScale(current_scale_); + library_->units()->distanceUnit()->setScale(distance_scale_); + + + library_->setDelayModelType(DelayModelType::cmos_linear); + scale_factors_ = new ScaleFactors(""); + library_->setScaleFactors(scale_factors_); + } + else + libError(group, "library does not have a name.\n"); +} + +// Energy scale is derived. +void +LibertyReader::setEnergyScale() +{ + energy_scale_ = volt_scale_ * volt_scale_ * cap_scale_; +} + +void +LibertyReader::endLibrary(LibertyGroup *group) +{ + endLibraryAttrs(group); +} + +void +LibertyReader::endLibraryAttrs(LibertyGroup *group) +{ + // Default resistance_unit to pulling_resistance_unit. + if (!have_resistance_unit_) { + Units *units = library_->units(); + units->resistanceUnit()->copy(units->pullingResistanceUnit()); + } + + // 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(group, "default_wire_load %s not found.\n", default_wireload_); + stringDelete(default_wireload_); + } + + if (default_wireload_selection_) { + WireloadSelection *selection = + library_->findWireloadSelection(default_wireload_selection_); + if (selection) + library_->setDefaultWireloadSelection(selection); + else + libWarn(group, "default_wire_selection %s not found.\n", + default_wireload_selection_); + stringDelete(default_wireload_selection_); + } + + bool missing_threshold = false; + for (auto tr : TransRiseFall::range()) { + int tr_index = tr->index(); + if (!have_input_threshold_[tr_index]) { + libWarn(group, "input_threshold_pct_%s not found.\n", tr->name()); + missing_threshold = true; + } + if (!have_output_threshold_[tr_index]) { + libWarn(group, "output_threshold_pct_%s not found.\n", tr->name()); + missing_threshold = true; + } + if (!have_slew_lower_threshold_[tr_index]) { + libWarn(group, "slew_lower_threshold_pct_%s not found.\n", tr->name()); + missing_threshold = true; + } + if (!have_slew_upper_threshold_[tr_index]) { + libWarn(group, "slew_upper_threshold_pct_%s not found.\n", tr->name()); + missing_threshold = true; + } + } + if (missing_threshold) + libError(group, "Library %s is missing one or more thresholds.\n", + library_->name()); +} + +void +LibertyReader::visitTimeUnit(LibertyAttr *attr) +{ + if (library_) + parseUnits(attr, "s", time_scale_, library_->units()->timeUnit()); +} + +void +LibertyReader::visitPullingResistanceUnit(LibertyAttr *attr) +{ + if (library_) + parseUnits(attr, "ohm", res_scale_, + library_->units()->pullingResistanceUnit()); +} + +void +LibertyReader::visitResistanceUnit(LibertyAttr *attr) +{ + if (library_) { + parseUnits(attr, "ohm", res_scale_, library_->units()->resistanceUnit()); + have_resistance_unit_ = true; + } +} + +void +LibertyReader::visitCurrentUnit(LibertyAttr *attr) +{ + if (library_) + parseUnits(attr, "A", current_scale_, library_->units()->currentUnit()); +} + +void +LibertyReader::visitVoltageUnit(LibertyAttr *attr) +{ + if (library_) + parseUnits(attr, "V", volt_scale_, library_->units()->voltageUnit()); + setEnergyScale(); +} + +void +LibertyReader::visitPowerUnit(LibertyAttr *attr) +{ + if (library_) + parseUnits(attr, "W", power_scale_, library_->units()->powerUnit()); +} + +void +LibertyReader::visitDistanceUnit(LibertyAttr *attr) +{ + if (library_) + parseUnits(attr, "m", distance_scale_, library_->units()->distanceUnit()); +} + +void +LibertyReader::parseUnits(LibertyAttr *attr, + const char *unit_suffix, + float &scale_var, + Unit *unit) +{ + const char *unit_str = getAttrString(attr); + unsigned unit_str_length = strlen(unit_str); + + // Unit format is . + // Find the multiplier digits. + char *mult_str = makeTmpString(unit_str_length); + const char *s = unit_str; + char *m = mult_str; + for (; *s; s++) { + char ch = *s; + if (isdigit(ch)) + *m++ = ch; + else + break; + } + *m = '\0'; + + float mult = 1.0F; + if (*mult_str != '\0') { + if (stringEq(mult_str, "1")) + mult = 1.0F; + else if (stringEq(mult_str, "10")) + mult = 10.0F; + else if (stringEq(mult_str, "100")) + mult = 100.0F; + else + libWarn(attr, "unknown unit multiplier %s.\n", mult_str); + } + + float scale_mult = 1.0F; + if (*s && stringEqual(s + 1, unit_suffix)) { + char scale_char = tolower(*s); + 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(attr, "unknown unit scale %c.\n", scale_char); + } + else if (!stringEqual(s, unit_suffix)) + libWarn(attr, "unknown unit suffix %s.\n", s + 1); + + scale_var = scale_mult * mult; + unit->setScale(scale_var); +} + +void +LibertyReader::visitCapacitiveLoadUnit(LibertyAttr *attr) +{ + if (library_) { + if (attr->isComplex()) { + LibertyAttrValueIterator value_iter(attr->values()); + if (value_iter.hasNext()) { + LibertyAttrValue *value = value_iter.next(); + if (value->isFloat()) { + float scale = value->floatValue(); + 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(attr, "capacitive_load_units are not ff or pf.\n"); + } + else + libWarn(attr, "capacitive_load_units are not a string.\n"); + } + else + libWarn(attr, "capacitive_load_units missing suffix.\n"); + } + else + libWarn(attr, "capacitive_load_units scale is not a float.\n"); + } + else + libWarn(attr, "capacitive_load_units missing scale and suffix.\n"); + } + else + libWarn(attr, "capacitive_load_unit missing values suffix.\n"); + library_->units()->capacitanceUnit()->setScale(cap_scale_); + setEnergyScale(); + } +} + +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(attr, "delay_model %s not supported.\n.", type_name); + } + else if (stringEq(type_name, "cmos2")) { + library_->setDelayModelType(DelayModelType::cmos2); + libWarn(attr, "delay_model %s not supported.\n.", type_name); + } + else if (stringEq(type_name, "polynomial")) { + library_->setDelayModelType(DelayModelType::polynomial); + libWarn(attr, "delay_model %s not supported.\n.", type_name); + } + // Evil IBM garbage. + else if (stringEq(type_name, "dcm")) { + library_->setDelayModelType(DelayModelType::dcm); + libWarn(attr, "delay_model %s not supported.\n.", type_name); + } + else + libWarn(attr, "unknown delay_model %s\n.", type_name); + } + } +} + +void +LibertyReader::visitBusStyle(LibertyAttr *attr) +{ + 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(attr, "unknown bus_naming_style format.\n"); + } +} + +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(); + if (value->isFloat()) { + float voltage = value->floatValue(); + library_->addSupplyVoltage(supply_name, voltage); + } + else + libWarn(attr, "voltage_map voltage is not a float.\n"); + } + else + libWarn(attr, "voltage_map missing voltage.\n"); + } + else + libWarn(attr, "voltage_map supply name is not a string.\n"); + } + else + libWarn(attr, "voltage_map missing supply name and voltage.\n"); + } + else + libWarn(attr, "voltage_map missing values suffix.\n"); + } +} + +void +LibertyReader::visitNomTemp(LibertyAttr *attr) +{ + if (library_) { + float value; + bool valid; + getAttrFloat(attr, value, valid); + if (valid) + library_->setNominalTemperature(value); + } +} + +void +LibertyReader::visitNomProc(LibertyAttr *attr) +{ + if (library_) { + float value; + bool valid; + getAttrFloat(attr, value, valid); + if (valid) + library_->setNominalProcess(value); + } +} + +void +LibertyReader::visitNomVolt(LibertyAttr *attr) +{ + if (library_) { + float value; + bool valid; + getAttrFloat(attr, value, valid); + if (valid) + library_->setNominalVoltage(value); + } +} + +void +LibertyReader::visitDefaultInoutPinCap(LibertyAttr *attr) +{ + if (library_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + library_->setDefaultBidirectPinCap(value * cap_scale_); + } +} + +void +LibertyReader::visitDefaultInputPinCap(LibertyAttr *attr) +{ + if (library_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + library_->setDefaultInputPinCap(value * cap_scale_); + } +} + +void +LibertyReader::visitDefaultOutputPinCap(LibertyAttr *attr) +{ + if (library_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + library_->setDefaultOutputPinCap(value * cap_scale_); + } +} + +void +LibertyReader::visitDefaultMaxTransition(LibertyAttr *attr) +{ + if (library_){ + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + library_->setDefaultMaxSlew(value * time_scale_); + } +} + +void +LibertyReader::visitDefaultMaxFanout(LibertyAttr *attr) +{ + if (library_){ + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + library_->setDefaultMaxFanout(value); + } +} + +void +LibertyReader::visitDefaultIntrinsicRise(LibertyAttr *attr) +{ + visitDefaultIntrinsic(attr, TransRiseFall::rise()); +} + +void +LibertyReader::visitDefaultIntrinsicFall(LibertyAttr *attr) +{ + visitDefaultIntrinsic(attr, TransRiseFall::fall()); +} + +void +LibertyReader::visitDefaultIntrinsic(LibertyAttr *attr, + TransRiseFall *tr) +{ + if (library_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + library_->setDefaultIntrinsic(tr, value * time_scale_); + } +} + +void +LibertyReader::visitDefaultInoutPinRiseRes(LibertyAttr *attr) +{ + visitDefaultInoutPinRes(attr, TransRiseFall::rise()); +} + +void +LibertyReader::visitDefaultInoutPinFallRes(LibertyAttr *attr) +{ + visitDefaultInoutPinRes(attr, TransRiseFall::fall()); +} + +void +LibertyReader::visitDefaultInoutPinRes(LibertyAttr *attr, + TransRiseFall *tr) +{ + if (library_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + library_->setDefaultBidirectPinRes(tr, value * res_scale_); + } +} + +void +LibertyReader::visitDefaultOutputPinRiseRes(LibertyAttr *attr) +{ + visitDefaultOutputPinRes(attr, TransRiseFall::rise()); +} + +void +LibertyReader::visitDefaultOutputPinFallRes(LibertyAttr *attr) +{ + visitDefaultOutputPinRes(attr, TransRiseFall::fall()); +} + +void +LibertyReader::visitDefaultOutputPinRes(LibertyAttr *attr, + TransRiseFall *tr) +{ + if (library_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + library_->setDefaultOutputPinRes(tr, value * res_scale_); + } +} + +void +LibertyReader::visitDefaultFanoutLoad(LibertyAttr *attr) +{ + if (library_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + library_->setDefaultFanoutLoad(value); + } +} + +void +LibertyReader::visitDefaultWireLoad(LibertyAttr *attr) +{ + if (library_) { + const char *value = getAttrString(attr); + if (value) + default_wireload_ = stringCopy(value); + } +} + +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(attr, "default_wire_load_mode %s not found.\n", + wire_load_mode); + } + } +} + +void +LibertyReader::visitDefaultWireLoadSelection(LibertyAttr *attr) +{ + if (library_) { + const char *value = getAttrString(attr); + if (value) + default_wireload_selection_ = stringCopy(value); + } +} + +void +LibertyReader::visitDefaultOperatingConditions(LibertyAttr *attr) +{ + if (library_) { + const char *op_cond_name = getAttrString(attr); + OperatingConditions *op_cond = + library_->findOperatingConditions(op_cond_name); + if (op_cond) + library_->setDefaultOperatingConditions(op_cond); + else + libWarn(attr, "default_operating_condition %s not found.\n", + op_cond_name); + } +} + +void +LibertyReader::visitInputThresholdPctFall(LibertyAttr *attr) +{ + visitInputThresholdPct(attr, TransRiseFall::fall()); +} + +void +LibertyReader::visitInputThresholdPctRise(LibertyAttr *attr) +{ + visitInputThresholdPct(attr, TransRiseFall::rise()); +} + +void +LibertyReader::visitInputThresholdPct(LibertyAttr *attr, + TransRiseFall *tr) +{ + if (library_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + library_->setInputThreshold(tr, value / 100.0F); + } + have_input_threshold_[tr->index()] = true; +} + +void +LibertyReader::visitOutputThresholdPctFall(LibertyAttr *attr) +{ + visitOutputThresholdPct(attr, TransRiseFall::fall()); +} + +void +LibertyReader::visitOutputThresholdPctRise(LibertyAttr *attr) +{ + visitOutputThresholdPct(attr, TransRiseFall::rise()); +} + +void +LibertyReader::visitOutputThresholdPct(LibertyAttr *attr, + TransRiseFall *tr) +{ + if (library_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + library_->setOutputThreshold(tr, value / 100.0F); + } + have_output_threshold_[tr->index()] = true; +} + +void +LibertyReader::visitSlewLowerThresholdPctFall(LibertyAttr *attr) +{ + visitSlewLowerThresholdPct(attr, TransRiseFall::fall()); +} + +void +LibertyReader::visitSlewLowerThresholdPctRise(LibertyAttr *attr) +{ + visitSlewLowerThresholdPct(attr, TransRiseFall::rise()); +} + +void +LibertyReader::visitSlewLowerThresholdPct(LibertyAttr *attr, + TransRiseFall *tr) +{ + if (library_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + library_->setSlewLowerThreshold(tr, value / 100.0F); + } + have_slew_lower_threshold_[tr->index()] = true; +} + +void +LibertyReader::visitSlewUpperThresholdPctFall(LibertyAttr *attr) +{ + visitSlewUpperThresholdPct(attr, TransRiseFall::fall()); +} + +void +LibertyReader::visitSlewUpperThresholdPctRise(LibertyAttr *attr) +{ + visitSlewUpperThresholdPct(attr, TransRiseFall::rise()); +} + +void +LibertyReader::visitSlewUpperThresholdPct(LibertyAttr *attr, + TransRiseFall *tr) +{ + if (library_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + library_->setSlewUpperThreshold(tr, value / 100.0F); + } + have_slew_upper_threshold_[tr->index()] = true; +} + +void +LibertyReader::visitSlewDerateFromLibrary(LibertyAttr *attr) +{ + if (library_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + library_->setSlewDerateFromLibrary(value); + } +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::beginTableTemplateDelay(LibertyGroup *group) +{ + beginTableTemplate(group, TableTemplateType::delay); +} + +void +LibertyReader::beginTableTemplateOutputCurrent(LibertyGroup *group) +{ + beginTableTemplate(group, TableTemplateType::output_current); +} + +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); + } + else + libWarn(group, "table template does not have a name.\n"); + axis_var_[0] = axis_var_[1] = axis_var_[2] = TableAxisVariable::unknown; + clearAxisValues(); + } +} + +void +LibertyReader::clearAxisValues() +{ + axis_values_[0] = axis_values_[1] = axis_values_[2] = nullptr; +} + +void +LibertyReader::endTableTemplate(LibertyGroup *group) +{ + if (tbl_template_) { + TableAxis *axis; + makeAxis(0, group, axis); + if (axis) + tbl_template_->setAxis1(axis); + makeAxis(1, group, axis); + if (axis) + tbl_template_->setAxis2(axis); + makeAxis(2, group, axis); + if (axis) + tbl_template_->setAxis3(axis); + tbl_template_ = nullptr; + } +} + +void +LibertyReader::makeAxis(int index, + LibertyGroup *group, + TableAxis *&axis) +{ + axis = nullptr; + TableAxisVariable axis_var = axis_var_[index]; + FloatSeq *axis_values = axis_values_[index]; + if (axis_var != TableAxisVariable::unknown && axis_values) { + const Units *units = library_->units(); + float scale = tableVariableUnit(axis_var, units)->scale(); + scaleFloats(axis_values, scale); + axis = new TableAxis(axis_var, axis_values); + } + else if (axis_var == TableAxisVariable::unknown && axis_values) { + libWarn(group, "missing variable_%d attribute.\n", index + 1); + delete axis_values; + axis_values_[index] = nullptr; + } + // No warning for missing index_xx attributes because they are + // not required by ic_shell. +} + +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::visitVariable1(LibertyAttr *attr) +{ + visitVariable(0, attr); +} + +void +LibertyReader::visitVariable2(LibertyAttr *attr) +{ + visitVariable(1, attr); +} + +void +LibertyReader::visitVariable3(LibertyAttr *attr) +{ + visitVariable(2, attr); +} + +void +LibertyReader::visitVariable(int index, + LibertyAttr *attr) +{ + if (tbl_template_) { + const char *type = getAttrString(attr); + TableAxisVariable var = stringTableAxisVariable(type); + if (var == TableAxisVariable::unknown) + libWarn(attr, "axis type %s not supported.\n", type); + else + axis_var_[index] = var; + } +} + +void +LibertyReader::visitIndex1(LibertyAttr *attr) +{ + visitIndex(0, attr); +} + +void +LibertyReader::visitIndex2(LibertyAttr *attr) +{ + visitIndex(1, attr); +} + +void +LibertyReader::visitIndex3(LibertyAttr *attr) +{ + visitIndex(2, attr); +} + +void +LibertyReader::visitIndex(int index, + LibertyAttr *attr) +{ + if (tbl_template_ + // Ignore index_xx in ecsm_waveform groups. + && !stringEq(libertyGroup()->type(), "ecsm_waveform")) { + FloatSeq *axis_values = readFloatSeq(attr, 1.0F); + if (axis_values) + axis_values_[index] = axis_values; + } +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::beginType(LibertyGroup *) +{ + type_bit_from_exists_ = false; + type_bit_to_exists_ = false; +} + +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(group, "bus type %s missing bit_from.\n", name); + if (!type_bit_to_exists_) + libWarn(group, "bus type %s missing bit_to.\n", name); + } + } + else + libWarn(group, "type does not have a name.\n"); +} + +void +LibertyReader::visitBitFrom(LibertyAttr *attr) +{ + getAttrInt(attr, type_bit_from_, type_bit_from_exists_); +} + +void +LibertyReader::visitBitTo(LibertyAttr *attr) +{ + getAttrInt(attr, type_bit_to_, type_bit_to_exists_); +} + +//////////////////////////////////////////////////////////////// + +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(group, "scaling_factors do not have a name.\n"); +} + +void +LibertyReader::endScalingFactors(LibertyGroup *) +{ + scale_factors_ = save_scale_factors_; +} + +void +LibertyReader::visitScaleFactorSuffix(LibertyAttr *attr) +{ + if (scale_factors_) { + ScaleFactorPvt pvt = ScaleFactorPvt::unknown; + ScaleFactorType type = ScaleFactorType::unknown; + TransRiseFall *tr = 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")) + tr = TransRiseFall::rise(); + else if (stringEq(tr_name, "fall")) + tr = TransRiseFall::fall(); + } + if (pvt != ScaleFactorPvt::unknown + && type != ScaleFactorType::unknown + && tr) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + scale_factors_->setScale(type, pvt, tr, value); + } + } +} + +void +LibertyReader::visitScaleFactorPrefix(LibertyAttr *attr) +{ + if (scale_factors_) { + ScaleFactorPvt pvt = ScaleFactorPvt::unknown; + ScaleFactorType type = ScaleFactorType::unknown; + TransRiseFall *tr = 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")) + tr = TransRiseFall::rise(); + else if (stringEq(tr_name, "fall")) + tr = TransRiseFall::fall(); + } + if (parser.hasNext()) { + const char *type_name = parser.next(); + type = findScaleFactorType(type_name); + } + if (pvt != ScaleFactorPvt::unknown + && type != ScaleFactorType::unknown + && tr) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + scale_factors_->setScale(type, pvt, tr, value); + } + } +} + +void +LibertyReader::visitScaleFactorHiLow(LibertyAttr *attr) +{ + if (scale_factors_) { + ScaleFactorPvt pvt = ScaleFactorPvt::unknown; + ScaleFactorType type = ScaleFactorType::unknown; + TransRiseFall *tr = 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")) + tr = TransRiseFall::rise(); + else if (stringEq(tr_name, "low")) + tr = TransRiseFall::fall(); + } + if (pvt != ScaleFactorPvt::unknown + && type != ScaleFactorType::unknown + && tr) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + scale_factors_->setScale(type, pvt, tr, value); + } + } +} + +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); + } + } +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::beginOpCond(LibertyGroup *group) +{ + const char *name = group->firstName(); + if (name) { + op_cond_ = new OperatingConditions(name); + library_->addOperatingConditions(op_cond_); + } + else + libWarn(group, "operating_conditions does not have a name.\n"); +} + +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); + } +} + +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); + } + } +} + +void +LibertyReader::endOpCond(LibertyGroup *) +{ + op_cond_ = nullptr; +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::beginWireload(LibertyGroup *group) +{ + if (library_) { + const char *name = group->firstName(); + if (name) { + wireload_ = new Wireload(name, library_); + library_->addWireload(wireload_); + } + } + else + libWarn(group, "wire_load does not have a name.\n"); +} + +void +LibertyReader::endWireload(LibertyGroup *) +{ + wireload_ = nullptr; +} + +void +LibertyReader::visitResistance(LibertyAttr *attr) +{ + if (wireload_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + wireload_->setResistance(value * res_scale_); + } +} + +void +LibertyReader::visitSlope(LibertyAttr *attr) +{ + if (wireload_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + wireload_->setSlope(value); + } +} + +void +LibertyReader::visitFanoutLength(LibertyAttr *attr) +{ + if (wireload_) { + float fanout, length; + bool exists; + getAttrFloat2(attr, fanout, length, exists); + if (exists) + wireload_->addFanoutLength(fanout, length); + else + libWarn(attr, "fanout_length is missing length and fanout.\n"); + } +} + +void +LibertyReader::beginWireloadSelection(LibertyGroup *group) +{ + if (library_) { + const char *name = group->firstName(); + if (name) { + wireload_selection_ = new WireloadSelection(name); + library_->addWireloadSelection(wireload_selection_); + } + } + else + libWarn(group, "wire_load_selection does not have a name.\n"); +} + +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(attr, "wireload %s not found.\n", wireload_name); + } + else + libWarn(attr, + "wire_load_from_area wireload name not a string.\n"); + } + else + libWarn(attr, "wire_load_from_area min not a float.\n"); + } + else + libWarn(attr, "wire_load_from_area max not a float.\n"); + } + else + libWarn(attr, "wire_load_from_area missing parameters.\n"); + } + else + libWarn(attr, "wire_load_from_area missing parameters.\n"); + } +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::beginCell(LibertyGroup *group) +{ + const char *name = group->firstName(); + if (name) { + debugPrint1(debug_, "liberty", 1, "cell %s\n", name); + cell_ = builder_->makeCell(library_, name, filename_); + in_bus_ = false; + in_bundle_ = false; + } + else + libWarn(group, "cell does not have a name.\n"); +} + +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(); + // Parse functions defined inside of port groups that reference other ports + // and replace the references with the parsed expressions. + parseCellFuncs(); + makeLeakagePowers(); + finishPortGroups(); + + 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(group, "cell %s ocv_derate_group %s not found.\n", + cell_->name(), ocv_derate_name_); + stringDelete(ocv_derate_name_); + ocv_derate_name_ = nullptr; + } + cell_->finish(infer_latches_, report_, debug_); + cell_ = nullptr; + } +} + +void +LibertyReader::finishPortGroups() +{ + PortGroupSeq::Iterator group_iter(cell_port_groups_); + while (group_iter.hasNext()) { + PortGroup *port_group = group_iter.next(); + int line = port_group->line(); + LibertyPortSeq::Iterator port_iter(port_group->ports()); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + checkPort(port, line); + } + makeTimingArcs(port_group); + makeInternalPowers(port_group); + delete port_group; + } + cell_port_groups_.clear(); +} + +void +LibertyReader::checkPort(LibertyPort *port, + int line) +{ + FuncExpr *func_expr = port->function(); + if (func_expr) { + if (func_expr->checkSize(port)) { + libWarn(line, "port %s function size does not match port size.\n", + port->name()); + } + } + if (port->tristateEnable() + && port->direction() == PortDirection::output()) + port->setDirection(PortDirection::tristate()); +} + +void +LibertyReader::makeTimingArcs(PortGroup *port_group) +{ + TimingGroupSeq::Iterator timing_iter(port_group->timingGroups()); + while (timing_iter.hasNext()) { + TimingGroup *timing = timing_iter.next(); + timing->makeTimingModels(library_, this); + + LibertyPortSeq::Iterator port_iter(port_group->ports()); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + makeTimingArcs(port, timing); + } + cell_->addTimingArcAttrs(timing); + } +} + +void +LibertyReader::makeInternalPowers(PortGroup *port_group) +{ + InternalPowerGroupSeq::Iterator power_iter(port_group->internalPowerGroups()); + while (power_iter.hasNext()) { + InternalPowerGroup *power_group = power_iter.next(); + LibertyPortSeq::Iterator port_iter(port_group->ports()); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + makeInternalPowers(port, power_group); + } + cell_->addInternalPowerAttrs(power_group); + } +} + +void +LibertyReader::makeCellSequentials() +{ + SequentialGroupSeq::Iterator seq_iter(cell_sequentials_); + while (seq_iter.hasNext()) { + SequentialGroup *seq = seq_iter.next(); + 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(line, "%s %s bus width mismatch.\n", 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(line, "%s %s bus width mismatch.\n", 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(line, "%s %s bus width mismatch.\n", 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(line, "%s %s bus width mismatch.\n", type, "preset"); + preset_expr->deleteSubexprs(); + preset_expr = nullptr; + } + } + 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::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(line, "latch enable function is non-unate for port %s.\n", + enable_port->name()); + break; + case TimingSense::none: + case TimingSense::unknown: + libWarn(line, "latch enable function is unknown for port %s.\n", + enable_port->name()); + break; + } + } +} + +void +LibertyReader::makeLeakagePowers() +{ + LeakagePowerGroupSeq::Iterator power_iter(leakage_powers_); + while (power_iter.hasNext()) { + LeakagePowerGroup *power_group = power_iter.next(); + builder_->makeLeakagePower(cell_, power_group); + 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, + FuncExpr *&func_ref, + bool invert, + const char *attr_name, + LibertyStmt *stmt) +{ + LibertyFunc *func = new LibertyFunc(expr, func_ref, invert, attr_name, + stmt->line()); + cell_funcs_.push_back(func); +} + +void +LibertyReader::parseCellFuncs() +{ + LibertyFuncSeq::Iterator func_iter(cell_funcs_); + while (func_iter.hasNext()) { + LibertyFunc *func = func_iter.next(); + FuncExpr *expr = parseFunc(func->expr(), func->attrName(), func->line()); + if (func->invert()) { + if (expr->op() == FuncExpr::op_not) { + FuncExpr *inv = expr; + expr = expr->left(); + delete inv; + } + else + expr = FuncExpr::makeNot(expr); + } + if (expr) { + FuncExpr *prev_func = func->funcRef(); + if (prev_func) + prev_func->deleteSubexprs(); + func->funcRef() = 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_) { + debugPrint2(debug_, "liberty", 1, "scaled cell %s %s\n", + name, op_cond_name); + cell_ = library_->makeScaledCell(name, filename_); + } + else + libWarn(group, "operating conditions %s not found.\n", op_cond_name); + } + else + libWarn(group, "scaled_cell does not have an operating condition.\n"); + } + else + libWarn(group, "scaled_cell cell %s has not been defined.\n", name); + } + else + libWarn(group, "scaled_cell does not have a name.\n"); +} + +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 (!equivCellPortsAndFuncs(cell_, scaled_cell_owner_)) + libWarn(group, "scaled_cell %s, %s port functions do not match cell port functions.\n", + cell_->name(), + op_cond_->name()); + } + else + libWarn(group, "scaled_cell ports do not match cell ports.\n"); + if (!equivCellTimingArcSets(cell_, scaled_cell_owner_)) + libWarn(group, "scaled_cell %s, %s timing does not match cell timing.\n", + cell_->name(), + op_cond_->name()); +} + +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(); + // Should be more comprehensive (timing checks on inputs, etc). + TimingType type = timing->timingType(); + if (type == TimingType::combinational && + to_port_dir->isInput()) + libWarn(line, "combinational timing to an input port.\n"); + StringSeq::Iterator related_port_iter(timing->relatedPortNames()); + while (related_port_iter.hasNext()) { + const char *from_port_name = related_port_iter.next(); + PortNameBitIterator from_port_iter(cell_, from_port_name, this, line); + if (from_port_iter.hasNext()) { + debugPrint2(debug_, "liberty", 2, " timing %s -> %s\n", + from_port_name, to_port->name()); + makeTimingArcs(from_port_name, from_port_iter, to_port, + related_out_port, timing); + } + } +} + +void +TimingGroup::makeTimingModels(LibertyLibrary *library, + LibertyReader *visitor) +{ + switch (library->delayModelType()) { + case DelayModelType::cmos_linear: + makeLinearModels(library); + break; + case DelayModelType::table: + makeTableModels(visitor); + break; + case DelayModelType::cmos_pwl: + case DelayModelType::cmos2: + case DelayModelType::polynomial: + case DelayModelType::dcm: + break; + } +} + +void +TimingGroup::makeLinearModels(LibertyLibrary *library) +{ + for (auto tr : TransRiseFall::range()) { + int tr_index = tr->index(); + float intr = intrinsic_[tr_index]; + bool intr_exists = intrinsic_exists_[tr_index]; + if (!intr_exists) + library->defaultIntrinsic(tr, intr, intr_exists); + TimingModel *model = nullptr; + if (timingTypeIsCheck(timing_type_)) { + if (intr_exists) + model = new CheckLinearModel(intr); + } + else { + float res = resistance_[tr_index]; + bool res_exists = resistance_exists_[tr_index]; + if (!res_exists) + library->defaultPinResistance(tr, PortDirection::output(), + res, res_exists); + if (!res_exists) + res = 0.0F; + if (intr_exists) + model = new GateLinearModel(intr, res); + } + models_[tr_index] = model; + } +} + +void +TimingGroup::makeTableModels(LibertyReader *visitor) +{ + for (auto tr : TransRiseFall::range()) { + int tr_index = tr->index(); + TableModel *cell = cell_[tr_index]; + TableModel *constraint = constraint_[tr_index]; + TableModel *transition = transition_[tr_index]; + if (cell || transition) { + models_[tr_index] = new GateTableModel(cell, delay_sigma_[tr_index], + transition, slew_sigma_[tr_index]); + 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) + visitor->libWarn(line_, "missing %s_transition.\n", tr->name()); + if (cell == nullptr) + visitor->libWarn(line_, "missing cell_%s.\n", tr->name()); + } + } + if (constraint) + models_[tr_index] = new CheckTableModel(constraint, + constraint_sigma_[tr_index]); + } +} + +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(); + builder_->makeTimingArcs(cell_, from_port, to_port, + related_out_port, timing); + } + } + else if (from_port_iter.size() > 1 && !to_port->hasMembers()) { + // bus -> one + while (from_port_iter.hasNext()) { + LibertyPort *from_port = from_port_iter.next(); + builder_->makeTimingArcs(cell_, from_port, to_port, + related_out_port, timing); + } + } + else if (from_port_iter.size() == 1 && to_port->hasMembers()) { + // one -> bus + if (from_port_iter.hasNext()) { + LibertyPort *from_port = from_port_iter.next(); + LibertyPortMemberIterator bit_iter(to_port); + while (bit_iter.hasNext()) { + LibertyPort *to_port_bit = bit_iter.next(); + builder_->makeTimingArcs(cell_, from_port, to_port_bit, + related_out_port, timing); + } + } + } + else { + // bus -> bus + if (timing->isOneToOne()) { + if (static_cast(from_port_iter.size()) == to_port->size()) { + LibertyPortMemberIterator to_iter(to_port); + while (from_port_iter.hasNext() && to_iter.hasNext()) { + LibertyPort *from_port_bit = from_port_iter.next(); + LibertyPort *to_port_bit = to_iter.next(); + builder_->makeTimingArcs(cell_, from_port_bit, to_port_bit, + related_out_port, timing); + } + } + else + libWarn(timing->line(), + "timing port %s and related port %s are different sizes.\n", + from_port_name, + to_port->name()); + } + else { + while (from_port_iter.hasNext()) { + LibertyPort *from_port_bit = from_port_iter.next(); + LibertyPortMemberIterator to_iter(to_port); + while (to_iter.hasNext()) { + LibertyPort *to_port_bit = to_iter.next(); + builder_->makeTimingArcs(cell_, from_port_bit, to_port_bit, + related_out_port, timing); + } + } + } + } +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::makeInternalPowers(LibertyPort *port, + InternalPowerGroup *power_group) +{ + int line = power_group->line(); + StringSeq *related_port_names = power_group->relatedPortNames(); + if (related_port_names) { + StringSeq::Iterator related_port_iter(related_port_names); + while (related_port_iter.hasNext()) { + const char *related_port_name = related_port_iter.next(); + PortNameBitIterator related_port_iter(cell_, related_port_name, this, line); + if (related_port_iter.hasNext()) { + debugPrint2(debug_, "liberty", 2, " power %s -> %s\n", + 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(power_group->line(), + "internal_power port %s and related port %s are different sizes.\n", + 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::visitIsPad(LibertyAttr *attr) +{ + if (cell_) { + bool is_pad, exists; + getAttrBool(attr, is_pad, exists); + if (exists) + cell_->setIsPad(is_pad); + } +} + +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(attr, "scaling_factors %s not found.\n", 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::beginPin(LibertyGroup *group) +{ + if (cell_) { + if (in_bus_) { + saved_ports_ = ports_; + saved_port_group_ = port_group_; + ports_ = new LibertyPortSeq; + LibertyAttrValueIterator param_iter(group->params()); + while (param_iter.hasNext()) { + LibertyAttrValue *param = param_iter.next(); + if (param->isString()) { + const char *name = param->stringValue(); + debugPrint1(debug_, "liberty", 1, " port %s\n", name); + PortNameBitIterator port_iter(cell_, name, this, group->line()); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + ports_->push_back(port); + setPortDefaults(port); + } + } + else + libWarn(group, "pin name is not a string.\n"); + } + } + else if (in_bundle_) { + saved_ports_ = ports_; + saved_port_group_ = port_group_; + ports_ = new LibertyPortSeq; + LibertyAttrValueIterator param_iter(group->params()); + while (param_iter.hasNext()) { + LibertyAttrValue *param = param_iter.next(); + if (param->isString()) { + const char *name = param->stringValue(); + debugPrint1(debug_, "liberty", 1, " port %s\n", name); + LibertyPort *port = findPort(name); + if (port == nullptr) + port = builder_->makePort(cell_, name); + ports_->push_back(port); + setPortDefaults(port); + } + else + libWarn(group, "pin name is not a string.\n"); + } + } + else { + ports_ = new LibertyPortSeq; + char brkt_left = library_->busBrktLeft(); + char brkt_right = library_->busBrktRight(); + // Multiple port names can share group def. + LibertyAttrValueIterator param_iter(group->params()); + while (param_iter.hasNext()) { + LibertyAttrValue *param = param_iter.next(); + if (param->isString()) { + const char *name = param->stringValue(); + debugPrint1(debug_, "liberty", 1, " port %s\n", name); + if (isBusName(name, brkt_left, brkt_right, escape_)) + // Pins not inside a bus group with bus names are not really + // busses, so escape the brackets. + name = escapeChars(name, brkt_left, brkt_right, escape_); + LibertyPort *port = builder_->makePort(cell_, name); + ports_->push_back(port); + setPortDefaults(port); + } + else + libWarn(group, "pin name is not a string.\n"); + } + } + port_group_ = new PortGroup(ports_, group->line()); + cell_port_groups_.push_back(port_group_); + } + if (test_cell_) { + const char *pin_name = group->firstName(); + if (pin_name) + port_ = findPort(save_cell_, pin_name); + } +} + +void +LibertyReader::setPortDefaults(LibertyPort *port) +{ + float fanout; + bool exists; + library_->defaultMaxFanout(fanout, exists); + if (exists) + port->setFanoutLimit(fanout, MinMax::max()); + float slew; + library_->defaultMaxSlew(slew, exists); + if (exists) + port->setSlewLimit(slew, MinMax::max()); + float max_cap; + library_->defaultMaxCapacitance(max_cap, exists); + if (exists) + port->setCapacitanceLimit(slew, MinMax::max()); +} + +void +LibertyReader::endPin(LibertyGroup *) +{ + if (cell_) { + endPorts(); + if (in_bus_ || in_bundle_) { + ports_ = saved_ports_; + port_group_ = saved_port_group_; + } + } + port_ = nullptr; +} + +void +LibertyReader::endPorts() +{ + // Capacitances default based on direction so wait until the end + // of the pin group to set them. + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + 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 : TransRiseFall::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(group, "bus %s bus_type not found.\n", group->firstName()); + endBusOrBundle(); + in_bus_ = false; + } +} + +void +LibertyReader::beginBusOrBundle(LibertyGroup *group) +{ + // Multiple port names can share group def. + LibertyAttrValueIterator param_iter(group->params()); + while (param_iter.hasNext()) { + LibertyAttrValue *param = param_iter.next(); + 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) { + StringSeq::Iterator name_iter(bus_names_); + while (name_iter.hasNext()) { + const char *name = name_iter.next(); + debugPrint1(debug_, "liberty", 1, " bus %s\n", name); + LibertyPort *port = builder_->makeBusPort(cell_, name, + bus_dcl->from(), + bus_dcl->to()); + ports_->push_back(port); + } + } + else + libWarn(attr, "bus_type %s not found.\n", bus_type); + } + else + libWarn(attr, "bus_type is not a string.\n"); + } +} + +void +LibertyReader::beginBundle(LibertyGroup *group) +{ + if (cell_) { + beginBusOrBundle(group); + in_bundle_ = true; + } +} + +void +LibertyReader::endBundle(LibertyGroup *group) +{ + if (cell_) { + if (ports_->empty()) + libWarn(group, "bundle %s member not found.\n", group->firstName()); + endBusOrBundle(); + in_bundle_ = false; + } +} + +void +LibertyReader::visitMembers(LibertyAttr *attr) +{ + if (cell_) { + if (attr->isComplex()) { + StringSeq::Iterator name_iter(bus_names_); + while (name_iter.hasNext()) { + const char *name = name_iter.next(); + debugPrint1(debug_, "liberty", 1, " bundle %s\n", name); + ConcretePortSeq *members = new ConcretePortSeq; + LibertyAttrValueIterator value_iter(attr->values()); + while (value_iter.hasNext()) { + LibertyAttrValue *value = value_iter.next(); + if (value->isString()) { + const char *port_name = value->stringValue(); + LibertyPort *port = findPort(port_name); + if (port == nullptr) + port = builder_->makePort(cell_, port_name); + members->push_back(port); + } + else + libWarn(attr, "member is not a string.\n"); + } + LibertyPort *port = builder_->makeBundlePort(cell_, name, members); + ports_->push_back(port); + } + } + else + libWarn(attr,"members attribute is missing values.\n"); + } +} + +LibertyPort * +LibertyReader::findPort(const char *port_name) +{ + return findPort(cell_, port_name); +} + +LibertyPort * +LibertyReader::findPort(LibertyCell *cell, + const char *port_name) +{ + LibertyPort *port = cell->findLibertyPort(port_name); + if (port == nullptr) { + char brkt_left = library_->busBrktLeft(); + char brkt_right = library_->busBrktRight(); + if (isBusName(port_name, brkt_left, brkt_right, escape_)) { + // Pins at top level with bus names have escaped brackets. + port_name = escapeChars(port_name, brkt_left, brkt_right, escape_); + port = cell->findLibertyPort(port_name); + } + } + return port; +} + +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(attr, "unknown port direction.\n"); + + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + if (!port->direction()->isTristate()) + // Tristate enable function sets direction to tristate; don't + // clobber it. + port->setDirection(port_dir); + } + } + } +} + +void +LibertyReader::visitFunction(LibertyAttr *attr) +{ + if (ports_) { + const char *func = getAttrString(attr); + if (func) { + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + makeLibertyFunc(func, port->functionRef(), false, "function", attr); + } + } + } +} + +void +LibertyReader::visitThreeState(LibertyAttr *attr) +{ + if (ports_) { + const char *three_state = getAttrString(attr); + if (three_state) { + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + makeLibertyFunc(three_state, port->tristateEnableRef(), true, + "three_state", attr); + } + } + } +} + +void +LibertyReader::visitClock(LibertyAttr *attr) +{ + if (ports_) { + bool is_clk, exists; + getAttrBool(attr, is_clk, exists); + if (exists) { + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setIsClock(is_clk); + } + } + } +} + +void +LibertyReader::visitCapacitance(LibertyAttr *attr) +{ + if (ports_) { + float cap; + bool exists; + getAttrFloat(attr, cap, exists); + if (exists) { + cap *= cap_scale_; + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + 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_; + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setCapacitance(TransRiseFall::rise(), MinMax::min(), cap); + port->setCapacitance(TransRiseFall::rise(), MinMax::max(), cap); + } + } + } +} + +void +LibertyReader::visitFallCap(LibertyAttr *attr) +{ + if (ports_) { + float cap; + bool exists; + getAttrFloat(attr, cap, exists); + if (exists) { + cap *= cap_scale_; + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setCapacitance(TransRiseFall::fall(), MinMax::min(), cap); + port->setCapacitance(TransRiseFall::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_; + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setCapacitance(TransRiseFall::rise(), MinMax::min(), min); + port->setCapacitance(TransRiseFall::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_; + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setCapacitance(TransRiseFall::fall(), MinMax::min(), min); + port->setCapacitance(TransRiseFall::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::visitMaxFanout(LibertyAttr *attr) +{ + visitFanout(attr, MinMax::max()); +} + +void +LibertyReader::visitMinFanout(LibertyAttr *attr) +{ + visitFanout(attr, MinMax::min()); +} + +void +LibertyReader::visitFanout(LibertyAttr *attr, + MinMax *min_max) +{ + if (ports_) { + float fanout; + bool exists; + getAttrFloat(attr, fanout, exists); + if (exists) { + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + 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, MinMax *min_max) +{ + if (cell_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) { + value *= time_scale_; + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + 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, + MinMax *min_max) +{ + if (cell_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) { + value *= cap_scale_; + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setCapacitanceLimit(value, min_max); + } + } + } +} + +void +LibertyReader::visitMinPeriod(LibertyAttr *attr) +{ + if (cell_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) { + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setMinPeriod(value * time_scale_); + } + } + } +} + +void +LibertyReader::visitMinPulseWidthLow(LibertyAttr *attr) +{ + visitMinPulseWidth(attr, TransRiseFall::fall()); +} + +void +LibertyReader::visitMinPulseWidthHigh(LibertyAttr *attr) +{ + visitMinPulseWidth(attr, TransRiseFall::rise()); +} + +void +LibertyReader::visitMinPulseWidth(LibertyAttr *attr, + TransRiseFall *tr) +{ + if (cell_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) { + value *= time_scale_; + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setMinPulseWidth(tr, value); + } + } + } +} + +void +LibertyReader::visitPulseClock(LibertyAttr *attr) +{ + if (cell_) { + const char *pulse_clk = getAttrString(attr); + if (pulse_clk) { + TransRiseFall *trigger = nullptr; + TransRiseFall *sense = nullptr; + if (stringEq(pulse_clk, "rise_triggered_high_pulse")) { + trigger = TransRiseFall::rise(); + sense = TransRiseFall::rise(); + } + else if (stringEq(pulse_clk, "rise_triggered_low_pulse")) { + trigger = TransRiseFall::rise(); + sense = TransRiseFall::fall(); + } + else if (stringEq(pulse_clk, "fall_triggered_high_pulse")) { + trigger = TransRiseFall::fall(); + sense = TransRiseFall::rise(); + } + else if (stringEq(pulse_clk, "fall_triggered_low_pulse")) { + trigger = TransRiseFall::fall(); + sense = TransRiseFall::fall(); + } + else + libWarn(attr, "pulse_latch unknown pulse type.\n"); + if (trigger) { + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setPulseClk(trigger, sense); + } + } + } + } +} + +void +LibertyReader::visitClockGateClockPin(LibertyAttr *attr) +{ + if (cell_) { + bool value, exists; + getAttrBool(attr, value, exists); + if (exists) { + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setIsClockGateClockPin(value); + } + } + } +} + +void +LibertyReader::visitClockGateEnablePin(LibertyAttr *attr) +{ + if (cell_) { + bool value, exists; + getAttrBool(attr, value, exists); + if (exists) { + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setIsClockGateEnablePin(value); + } + } + } +} + +void +LibertyReader::visitClockGateOutPin(LibertyAttr *attr) +{ + if (cell_) { + bool value, exists; + getAttrBool(attr, value, exists); + if (exists) { + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setIsClockGateOutPin(value); + } + } + } +} + +void +LibertyReader::visitIsPllFeedbackPin(LibertyAttr *attr) +{ + if (cell_) { + bool value, exists; + getAttrBool(attr, value, exists); + if (exists) { + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setIsPllFeedbackPin(value); + } + } + } +} + +void +LibertyReader::visitSignalType(LibertyAttr *attr) +{ + if (test_cell_) { + const char *type = getAttrString(attr); + if (type) { + if (stringEq(type, "test_scan_enable")) + test_cell_->setScanEnable(port_); + if (stringEq(type, "test_scan_in")) + test_cell_->setScanIn(port_); + if (stringEq(type, "test_scan_out")) + test_cell_->setScanOut(port_); + if (stringEq(type, "test_scan_out_inverted")) + test_cell_->setScanOutInv(port_); + } + } +} + +//////////////////////////////////////////////////////////////// + +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 = builder_->makeBusPort(cell_, out_name, size - 1, 0); + else + out_port = builder_->makePort(cell_,out_name); + out_port->setDirection(PortDirection::internal()); + } + if (out_inv_name) { + if (has_size) + out_port_inv = builder_->makeBusPort(cell_, out_inv_name, size - 1, 0); + else + out_port_inv = builder_->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) +{ + int i = 0; + out_name = nullptr; + out_inv_name = nullptr; + size = 1; + has_size = false; + LibertyAttrValueIterator param_iter(group->params()); + while (param_iter.hasNext()) { + LibertyAttrValue *value = param_iter.next(); + if (i == 0) + out_name = value->stringValue(); + else if (i == 1) + out_inv_name = value->stringValue(); + else if (i == 2) { + size = static_cast(value->floatValue()); + has_size = true; + } + i++; + } +} + +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)); + } + if (test_cell_) { + const char *next_state = getAttrString(attr); + LibertyPort *port = findPort(save_cell_, next_state); + if (port) + test_cell_->setDataIn(port); + } +} + +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::beginTiming(LibertyGroup *group) +{ + if (port_group_) { + timing_ = makeTimingGroup(group->line()); + port_group_->addTimingGroup(timing_); + } +} + +TimingGroup * +LibertyReader::makeTimingGroup(int line) +{ + return new TimingGroup(line); +} + +void +LibertyReader::endTiming(LibertyGroup *) +{ + if (timing_) { + // Set scale factor type in constraint tables. + for (auto tr : TransRiseFall::range()) { + TableModel *model = timing_->constraint(tr); + if (model) { + ScaleFactorType type=timingTypeScaleFactorType(timing_->timingType()); + model->setScaleFactorType(type); + } + } + timing_ = 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; +} + +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(attr, "unknown timing_type %s.\n", type_name); + else + timing_->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_->setTimingSense(TimingSense::non_unate); + else if (stringEq(sense_name, "positive_unate")) + timing_->setTimingSense(TimingSense::positive_unate); + else if (stringEq(sense_name, "negative_unate")) + timing_->setTimingSense(TimingSense::negative_unate); + else + libWarn(attr, "unknown timing_sense %s.\n", sense_name); + } + } +} + +void +LibertyReader::visitSdfCondStart(LibertyAttr *attr) +{ + if (timing_) { + const char *cond = getAttrString(attr); + if (cond) + timing_->setSdfCondStart(cond); + } +} + +void +LibertyReader::visitSdfCondEnd(LibertyAttr *attr) +{ + if (timing_) { + const char *cond = getAttrString(attr); + if (cond) + timing_->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_->setModeName(value->stringValue()); + if (value_iter.hasNext()) { + value = value_iter.next(); + if (value->isString()) + timing_->setModeValue(value->stringValue()); + else + libWarn(attr, "mode value is not a string.\n"); + } + else + libWarn(attr, "missing mode value.\n"); + } + else + libWarn(attr, "mode name is not a string.\n"); + } + else + libWarn(attr, "mode missing values.\n"); + } + else + libWarn(attr, "mode missing mode name and value.\n"); + } +} + +void +LibertyReader::visitIntrinsicRise(LibertyAttr *attr) +{ + visitIntrinsic(attr, TransRiseFall::rise()); +} + +void +LibertyReader::visitIntrinsicFall(LibertyAttr *attr) +{ + visitIntrinsic(attr, TransRiseFall::fall()); +} + +void +LibertyReader::visitIntrinsic(LibertyAttr *attr, + TransRiseFall *tr) +{ + if (timing_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + timing_->setIntrinsic(tr, value * time_scale_); + } +} + +void +LibertyReader::visitRiseResistance(LibertyAttr *attr) +{ + visitRiseFallResistance(attr, TransRiseFall::rise()); +} + +void +LibertyReader::visitFallResistance(LibertyAttr *attr) +{ + visitRiseFallResistance(attr, TransRiseFall::fall()); +} + +void +LibertyReader::visitRiseFallResistance(LibertyAttr *attr, + TransRiseFall *tr) +{ + if (timing_) { + float value; + bool exists; + getAttrFloat(attr, value, exists); + if (exists) + timing_->setResistance(tr, value * res_scale_); + } +} + +void +LibertyReader::beginCellRise(LibertyGroup *group) +{ + beginTimingTableModel(group, TransRiseFall::rise(), ScaleFactorType::cell); +} + +void +LibertyReader::beginCellFall(LibertyGroup *group) +{ + beginTimingTableModel(group, TransRiseFall::fall(), ScaleFactorType::cell); +} + +void +LibertyReader::endCellRiseFall(LibertyGroup *group) +{ + if (table_) { + if (GateTableModel::checkAxes(table_)) { + TableModel *table_model = new TableModel(table_, scale_factor_type_, tr_); + timing_->setCell(tr_, table_model); + } + else { + libWarn(group, "unsupported model axis.\n"); + delete table_; + } + } + endTableModel(); +} + +void +LibertyReader::beginRiseTransition(LibertyGroup *group) +{ + beginTimingTableModel(group, TransRiseFall::rise(), ScaleFactorType::transition); +} + +void +LibertyReader::beginFallTransition(LibertyGroup *group) +{ + beginTimingTableModel(group, TransRiseFall::fall(), ScaleFactorType::transition); +} + +void +LibertyReader::endRiseFallTransition(LibertyGroup *group) +{ + if (table_) { + if (GateTableModel::checkAxes(table_)) { + TableModel *table_model = new TableModel(table_, scale_factor_type_, tr_); + timing_->setTransition(tr_, table_model); + } + else { + libWarn(group, "unsupported model axis.\n"); + delete table_; + } + } + endTableModel(); +} + +void +LibertyReader::beginRiseConstraint(LibertyGroup *group) +{ + // Scale factor depends on timing_type, which may follow this stmt. + beginTimingTableModel(group, TransRiseFall::rise(), ScaleFactorType::unknown); +} + +void +LibertyReader::beginFallConstraint(LibertyGroup *group) +{ + // Scale factor depends on timing_type, which may follow this stmt. + beginTimingTableModel(group, TransRiseFall::fall(), ScaleFactorType::unknown); +} + +void +LibertyReader::endRiseFallConstraint(LibertyGroup *group) +{ + if (table_) { + if (CheckTableModel::checkAxes(table_)) { + TableModel *table_model = new TableModel(table_, scale_factor_type_, tr_); + timing_->setConstraint(tr_, table_model); + } + else { + libWarn(group, "unsupported model axis.\n"); + delete table_; + } + } + endTableModel(); +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::beginRiseTransitionDegredation(LibertyGroup *group) +{ + if (library_) + beginTableModel(group, TableTemplateType::delay, + TransRiseFall::rise(), time_scale_, + ScaleFactorType::transition); +} + +void +LibertyReader::beginFallTransitionDegredation(LibertyGroup *group) +{ + if (library_) + beginTableModel(group, TableTemplateType::delay, + TransRiseFall::fall(), time_scale_, + ScaleFactorType::transition); +} + +void +LibertyReader::endRiseFallTransitionDegredation(LibertyGroup *group) +{ + if (table_) { + if (LibertyLibrary::checkSlewDegradationAxes(table_)) { + TableModel *table_model = new TableModel(table_, scale_factor_type_, tr_); + library_->setWireSlewDegradationTable(table_model, tr_); + } + else { + libWarn(group, "unsupported model axis.\n"); + delete table_; + } + } + endTableModel(); +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::beginTimingTableModel(LibertyGroup *group, + TransRiseFall *tr, + ScaleFactorType scale_factor_type) +{ + if (timing_) + beginTableModel(group, TableTemplateType::delay, tr, + time_scale_, scale_factor_type); +} + +void +LibertyReader::beginTableModel(LibertyGroup *group, + TableTemplateType type, + TransRiseFall *tr, + float scale, + ScaleFactorType scale_factor_type) +{ + beginTable(group, type, scale); + tr_ = tr; + scale_factor_type_ = scale_factor_type; + sigma_type_ = EarlyLateAll::all(); +} + +void +LibertyReader::endTableModel() +{ + endTable(); +} + +void +LibertyReader::beginTable(LibertyGroup *group, + TableTemplateType type, + float scale) +{ + const char *template_name = group->firstName(); + if (template_name) { + tbl_template_ = library_->findTableTemplate(template_name, type); + if (tbl_template_) { + axis_[0] = tbl_template_->axis1(); + axis_[1] = tbl_template_->axis2(); + axis_[2] = tbl_template_->axis3(); + } + else { + libWarn(group, "table template %s not found.\n", template_name); + axis_[0] = axis_[1] = axis_[2] = nullptr; + } + clearAxisValues(); + own_axis_[0] = own_axis_[1] = own_axis_[2] = false; + table_ = nullptr; + table_model_scale_ = scale; + } +} + +void +LibertyReader::endTable() +{ + table_ = nullptr; + tbl_template_ = 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. + && !stringEq(libertyGroup()->type(), "ecsm_waveform")) + makeTable(attr, table_model_scale_); +} + +void +LibertyReader::makeTable(LibertyAttr *attr, + float scale) +{ + if (attr->isComplex()) { + makeTableAxis(0); + makeTableAxis(1); + makeTableAxis(2); + if (axis_[0] && axis_[1] && axis_[2]) { + // Column index1*size(index2) + index2 + // Row index3 + FloatTable *table = makeFloatTable(attr, + axis_[0]->size()*axis_[1]->size(), + axis_[2]->size(), scale); + if (table) + table_ = new Table3(table, + axis_[0], own_axis_[0], + axis_[1], own_axis_[1], + axis_[2], own_axis_[2]); + } + else if (axis_[0] && axis_[1]) { + // Row variable1/axis[0] + // Column variable2/axis[1] + FloatTable *table = makeFloatTable(attr, axis_[0]->size(), + axis_[1]->size(), scale); + if (table) + table_ = new Table2(table, + axis_[0], own_axis_[0], + axis_[1], own_axis_[1]); + } + else if (axis_[0]) { + FloatTable *table = makeFloatTable(attr, 1, axis_[0]->size(), scale); + if (table) { + FloatSeq *values = (*table)[0]; + delete table; + table_ = new Table1(values, axis_[0], own_axis_[0]); + } + } + else { + FloatTable *table = makeFloatTable(attr, 1, 1, scale); + if (table) { + float value = (*(*table)[0])[0]; + delete (*table)[0]; + delete table; + table_ = new Table0(value); + } + } + } + else + libWarn(attr, "%s is missing values.\n", attr->name()); +} + +FloatTable * +LibertyReader::makeFloatTable(LibertyAttr *attr, + size_t rows, + size_t cols, + float scale) +{ + FloatTable *table = new FloatTable; + table->reserve(rows); + LibertyAttrValueIterator value_iter(attr->values()); + while (value_iter.hasNext()) { + LibertyAttrValue *value = value_iter.next(); + 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()); + else + libWarn(attr, "%s is not a list of floats.\n", attr->name()); + if (row->size() != cols) { + libWarn(attr, "table row has %u columns but axis has %d.\n", + // size_t is long on 64 bit ports. + static_cast(row->size()), + static_cast(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(attr, "table has %u rows but axis has %d.\n", + // size_t is long on 64 bit ports. + static_cast(table->size()), + static_cast(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) +{ + 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] = new TableAxis(var, values); + own_axis_[index] = true; + } +} + +//////////////////////////////////////////////////////////////// + +// Define lut output variables as internal ports. +// I can't find any documentation for this group. +void +LibertyReader::beginLut(LibertyGroup *group) +{ + if (cell_) { + LibertyAttrValueIterator param_iter(group->params()); + while (param_iter.hasNext()) { + LibertyAttrValue *param = param_iter.next(); + 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 = builder_->makePort(cell_, name); + port->setDirection(PortDirection::internal()); + } + } + } + else + libWarn(group, "lut output is not a string.\n"); + } + } +} + +void +LibertyReader::endLut(LibertyGroup *) +{ +} + +//////////////////////////////////////////////////////////////// + +// Find scan ports in test_cell group. +void +LibertyReader::beginTestCell(LibertyGroup *) +{ + test_cell_ = new TestCell; + cell_->setTestCell(test_cell_); + save_cell_ = cell_; + cell_ = nullptr; +} + +void +LibertyReader::endTestCell(LibertyGroup *) +{ + cell_ = save_cell_; + test_cell_ = nullptr; +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::beginModeDef(LibertyGroup *group) +{ + const char *name = group->firstName(); + if (name) + mode_def_ = cell_->makeModeDef(name); + else + libWarn(group, "mode definition does not have a name.\n"); +} + +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(group, "mode value does not have a name.\n"); + } +} + +void + LibertyReader::endModeValue(LibertyGroup *) +{ + mode_value_ = nullptr; +} + +void +LibertyReader::visitWhen(LibertyAttr *attr) +{ + if (tbl_template_) + libWarn(attr, "when attribute inside table model.\n"); + if (mode_value_) { + const char *func = getAttrString(attr); + if (func) + makeLibertyFunc(func, mode_value_->condRef(), false, "when", attr); + } + if (timing_) { + const char *func = getAttrString(attr); + if (func) + makeLibertyFunc(func, timing_->condRef(), false, "when", attr); + } + if (internal_power_) { + const char *func = getAttrString(attr); + if (func) + makeLibertyFunc(func, internal_power_->whenRef(), false, "when", attr); + } + if (leakage_power_) { + const char *func = getAttrString(attr); + if (func) + makeLibertyFunc(func, leakage_power_->whenRef(), 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_->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(attr, "%s attribute is not a string.\n", attr->name()); + } + else + libWarn(attr, "%s is not a simple attribute.\n", 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(attr, "%s attribute is not an integer.\n",attr->name()); + } + else + libWarn(attr, "%s is not a simple attribute.\n", 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(attr, "%s is not a simple attribute.\n", 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)) + libWarn(attr, "%s value %s is not a float.\n", + 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(attr, "%s missing values.\n", attr->name()); + } + } + else + libWarn(attr, "%s missing values.\n", attr->name()); + } + else + libWarn(attr, "%s is not a complex attribute.\n", 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(attr, "%s is not a float.\n", 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 + libWarn(attr, "%s is missing values.\n", attr->name()); + } + if (value_iter.hasNext()) + libWarn(attr, "%s has more than one string.\n", attr->name()); + } + else { + LibertyAttrValue *value = attr->firstValue(); + if (value->isString()) { + values = new FloatSeq; + parseStringFloatList(value->stringValue(), scale, values, attr); + } + else + libWarn(attr, "%s is missing values.\n", 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(attr, "%s attribute is not boolean.\n", attr->name()); + } + else + libWarn(attr, "%s attribute is not boolean.\n", attr->name()); + } + else + libWarn(attr, "%s is not a simple attribute.\n", 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(attr, "attribute %s value %s not recognized.\n", + attr->name(), str); + // fall thru + } + return LogicValue::unknown; +} + +FuncExpr * +LibertyReader::parseFunc(const char *func, + const char *attr_name, + int line) +{ + const char *error_msg = stringPrintTmp("%s, line %d %s", + filename_, + line, + attr_name); + return parseFuncExpr(func, cell_, error_msg, report_); +} + +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(attr, "unknown early/late value.\n"); + return EarlyLateAll::all(); + } +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::visitVariable(LibertyVariable *var) +{ + if (var_map_ == nullptr) + var_map_ = new LibertyVariableMap; + const char *var_name = var->variable(); + const char *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_)[stringCopy(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(LibertyStmt *stmt, + const char *fmt, + ...) +{ + va_list args; + va_start(args, fmt); + report_->vfileWarn(filename_, stmt->line(), fmt, args); + va_end(args); +} + +void +LibertyReader::libWarn(int line, + const char *fmt, + ...) +{ + va_list args; + va_start(args, fmt); + report_->vfileWarn(filename_, line, fmt, args); + va_end(args); +} + +void +LibertyReader::libError(LibertyStmt *stmt, + const char *fmt, + ...) +{ + va_list args; + va_start(args, fmt); + report_->vfileError(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, + TransRiseFall::fall(), energy_scale_, + ScaleFactorType::internal_power); +} + +void +LibertyReader::beginRisePower(LibertyGroup *group) +{ + if (internal_power_) + beginTableModel(group, TableTemplateType::power, + TransRiseFall::rise(), energy_scale_, + ScaleFactorType::internal_power); +} + +void +LibertyReader::endRiseFallPower(LibertyGroup *) +{ + if (table_) { + TableModel *table_model = new TableModel(table_, scale_factor_type_, tr_); + internal_power_->setModel(tr_, new InternalPowerModel(table_model)); + } + endTableModel(); +} + +void +LibertyReader::visitRelatedGroundPin(LibertyAttr *attr) +{ + if (ports_) { + const char *related_ground_pin = getAttrString(attr); + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setRelatedGroundPin(related_ground_pin); + } + } +} + +void +LibertyReader::visitRelatedPowerPin(LibertyAttr *attr) +{ + if (ports_) { + const char *related_power_pin = getAttrString(attr); + LibertyPortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setRelatedPowerPin(related_power_pin); + } + } +} + +void +LibertyReader::visitRelatedPgPin(LibertyAttr *attr) +{ + if (internal_power_) + internal_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_->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(attr, "OCV derate group named %s not found.\n", 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(group, "ocv_derate does not have a name.\n"); +} + +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_ = TransRiseFallBoth::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 tr : rf_type_->range()) { + if (path_type_ == PathType::clk_and_data) { + ocv_derate_->setDerateTable(tr, early_late, PathType::clk, table_); + ocv_derate_->setDerateTable(tr, early_late, PathType::data, table_); + } + else + ocv_derate_->setDerateTable(tr, early_late, path_type_, table_); + } + } + } + endTable(); +} + +void +LibertyReader::visitRfType(LibertyAttr *attr) +{ + const char *tr_name = getAttrString(attr); + if (stringEq(tr_name, "rise")) + rf_type_ = TransRiseFallBoth::rise(); + else if (stringEq(tr_name, "fall")) + rf_type_ = TransRiseFallBoth::fall(); + else if (stringEq(tr_name, "rise_and_fall")) + rf_type_ = TransRiseFallBoth::riseFall(); + else + libError(attr, "unknown rf_type.\n"); +} + +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(attr, "unknown derate type.\n"); +} + +//////////////////////////////////////////////////////////////// + +void +LibertyReader::beginOcvSigmaCellRise(LibertyGroup *group) +{ + beginTimingTableModel(group, TransRiseFall::rise(), ScaleFactorType::unknown); +} + +void +LibertyReader::beginOcvSigmaCellFall(LibertyGroup *group) +{ + beginTimingTableModel(group, TransRiseFall::fall(), ScaleFactorType::unknown); +} + +void +LibertyReader::endOcvSigmaCell(LibertyGroup *group) +{ + if (table_) { + if (GateTableModel::checkAxes(table_)) { + TableModel *table_model = new TableModel(table_, scale_factor_type_, tr_); + if (sigma_type_ == EarlyLateAll::all()) { + timing_->setDelaySigma(tr_, EarlyLate::min(), table_model); + timing_->setDelaySigma(tr_, EarlyLate::max(), table_model); + } + else + timing_->setDelaySigma(tr_, sigma_type_->asMinMax(), table_model); + } + else { + libWarn(group, "unsupported model axis.\n"); + delete table_; + } + } + endTableModel(); +} + +void +LibertyReader::beginOcvSigmaRiseTransition(LibertyGroup *group) +{ + beginTimingTableModel(group, TransRiseFall::rise(), ScaleFactorType::unknown); +} + +void +LibertyReader::beginOcvSigmaFallTransition(LibertyGroup *group) +{ + beginTimingTableModel(group, TransRiseFall::fall(), ScaleFactorType::unknown); +} + +void +LibertyReader::endOcvSigmaTransition(LibertyGroup *group) +{ + if (table_) { + if (GateTableModel::checkAxes(table_)) { + TableModel *table_model = new TableModel(table_, scale_factor_type_, tr_); + if (sigma_type_ == EarlyLateAll::all()) { + timing_->setSlewSigma(tr_, EarlyLate::min(), table_model); + timing_->setSlewSigma(tr_, EarlyLate::max(), table_model); + } + else + timing_->setSlewSigma(tr_, sigma_type_->asMinMax(), table_model); + } + else { + libWarn(group, "unsupported model axis.\n"); + delete table_; + } + } + endTableModel(); +} + +void +LibertyReader::beginOcvSigmaRiseConstraint(LibertyGroup *group) +{ + beginTimingTableModel(group, TransRiseFall::rise(), ScaleFactorType::unknown); +} + +void +LibertyReader::beginOcvSigmaFallConstraint(LibertyGroup *group) +{ + beginTimingTableModel(group, TransRiseFall::fall(), ScaleFactorType::unknown); +} + +void +LibertyReader::endOcvSigmaConstraint(LibertyGroup *group) +{ + if (table_) { + if (CheckTableModel::checkAxes(table_)) { + TableModel *table_model = new TableModel(table_, scale_factor_type_, tr_); + if (sigma_type_ == EarlyLateAll::all()) { + timing_->setConstraintSigma(tr_, EarlyLate::min(), table_model); + timing_->setConstraintSigma(tr_, EarlyLate::max(), table_model); + } + else + timing_->setConstraintSigma(tr_, sigma_type_->asMinMax(), table_model); + } + else { + libWarn(group, "unsupported model axis.\n"); + delete table_; + } + } + 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_ = new LibertyPgPort(name, cell_); + cell_->addPgPort(pg_port_); + } +} + +void +LibertyReader::endPgPin(LibertyGroup *) +{ + pg_port_ = nullptr; +} + +void +LibertyReader::visitPgType(LibertyAttr *attr) +{ + if (pg_port_) { + const char *type_name = getAttrString(attr); + LibertyPgPort::PgType type = LibertyPgPort::PgType::unknown; + if (stringEqual(type_name, "primary_ground")) + type = LibertyPgPort::PgType::primary_ground; + else if (stringEqual(type_name, "primary_power")) + type = LibertyPgPort::PgType::primary_power; + + else if (stringEqual(type_name, "backup_ground")) + type = LibertyPgPort::PgType::backup_ground; + else if (stringEqual(type_name, "backup_power")) + type = LibertyPgPort::PgType::backup_power; + + else if (stringEqual(type_name, "internal_ground")) + type = LibertyPgPort::PgType::internal_ground; + else if (stringEqual(type_name, "internal_power")) + type = LibertyPgPort::PgType::internal_power; + + else if (stringEqual(type_name, "nwell")) + type = LibertyPgPort::PgType::nwell; + else if (stringEqual(type_name, "pwell")) + type = LibertyPgPort::PgType::pwell; + + else if (stringEqual(type_name, "deepnwell")) + type = LibertyPgPort::PgType::deepnwell; + else if (stringEqual(type_name, "deeppwell")) + type = LibertyPgPort::PgType::deeppwell; + + else + libError(attr, "unknown pg_type.\n"); + pg_port_->setPgType(type); + } +} + +void +LibertyReader::visitVoltageName(LibertyAttr *attr) +{ + if (pg_port_) { + const char *voltage_name = getAttrString(attr); + pg_port_->setVoltageName(voltage_name); + } +} + +//////////////////////////////////////////////////////////////// + +LibertyFunc::LibertyFunc(const char *expr, + FuncExpr *&func_ref, + bool invert, + const char *attr_name, + int line) : + expr_(stringCopy(expr)), + func_ref_(func_ref), + 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() +{ + // TimingGroups and IntternalPower are NOT deleted because ownership is transfered + // to LibertyCell::timing_arc_attrs_ by LibertyReader::makeTimingArcs. + 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; +} + +//////////////////////////////////////////////////////////////// + +RelatedPortGroup::RelatedPortGroup(int line) : + related_port_names_(nullptr), + line_(line) +{ +} + +RelatedPortGroup::~RelatedPortGroup() +{ + if (related_port_names_) { + deleteContents(related_port_names_); + delete related_port_names_; + } +} + +void +RelatedPortGroup::setRelatedPortNames(StringSeq *names) +{ + related_port_names_ = names; +} + +void +RelatedPortGroup::setIsOneToOne(bool one) +{ + is_one_to_one_ = one; +} + +//////////////////////////////////////////////////////////////// + +TimingGroup::TimingGroup(int line) : + TimingArcAttrs(), + RelatedPortGroup(line), + related_output_port_name_(nullptr) +{ + for (auto tr_index : TransRiseFall::rangeIndex()) { + cell_[tr_index] = nullptr; + constraint_[tr_index] = nullptr; + transition_[tr_index] = nullptr; + intrinsic_[tr_index] = 0.0F; + intrinsic_exists_[tr_index] = false; + resistance_[tr_index] = 0.0F; + resistance_exists_[tr_index] = false; + + for (auto el_index : EarlyLate::rangeIndex()) { + delay_sigma_[tr_index][el_index] = nullptr; + slew_sigma_[tr_index][el_index] = nullptr; + constraint_sigma_[tr_index][el_index] = nullptr; + } + } +} + +TimingGroup::~TimingGroup() +{ + // TimingAttrs contents are not deleted because they are referenced + // by TimingArcSets. + if (related_output_port_name_) + stringDelete(related_output_port_name_); +} + +void +TimingGroup::setRelatedOutputPortName(const char *name) +{ + related_output_port_name_ = stringCopy(name); +} + +void +TimingGroup::setIntrinsic(TransRiseFall *tr, + float value) +{ + int tr_index = tr->index(); + intrinsic_[tr_index] = value; + intrinsic_exists_[tr_index] = true; +} + +void +TimingGroup::intrinsic(TransRiseFall *tr, + // Return values. + float &value, + bool &exists) +{ + int tr_index = tr->index(); + value = intrinsic_[tr_index]; + exists = intrinsic_exists_[tr_index]; +} + +void +TimingGroup::setResistance(TransRiseFall *tr, + float value) +{ + int tr_index = tr->index(); + resistance_[tr_index] = value; + resistance_exists_[tr_index] = true; +} + +void +TimingGroup::resistance(TransRiseFall *tr, + // Return values. + float &value, + bool &exists) +{ + int tr_index = tr->index(); + value = resistance_[tr_index]; + exists = resistance_exists_[tr_index]; +} + +TableModel * +TimingGroup::cell(TransRiseFall *tr) +{ + return cell_[tr->index()]; +} + +void +TimingGroup::setCell(TransRiseFall *tr, + TableModel *model) +{ + cell_[tr->index()] = model; +} + +TableModel * +TimingGroup::constraint(TransRiseFall *tr) +{ + return constraint_[tr->index()]; +} + +void +TimingGroup::setConstraint(TransRiseFall *tr, + TableModel *model) +{ + constraint_[tr->index()] = model; +} + +TableModel * +TimingGroup::transition(TransRiseFall *tr) +{ + return transition_[tr->index()]; +} + +void +TimingGroup::setTransition(TransRiseFall *tr, + TableModel *model) +{ + transition_[tr->index()] = model; +} + +void +TimingGroup::setDelaySigma(TransRiseFall *tr, + EarlyLate *early_late, + TableModel *model) +{ + delay_sigma_[tr->index()][early_late->index()] = model; +} + +void +TimingGroup::setSlewSigma(TransRiseFall *tr, + EarlyLate *early_late, + TableModel *model) +{ + slew_sigma_[tr->index()][early_late->index()] = model; +} + +void +TimingGroup::setConstraintSigma(TransRiseFall *tr, + EarlyLate *early_late, + TableModel *model) +{ + constraint_sigma_[tr->index()][early_late->index()] = model; +} + +//////////////////////////////////////////////////////////////// + +InternalPowerGroup::InternalPowerGroup(int line) : + InternalPowerAttrs(), + RelatedPortGroup(line) +{ +} + +InternalPowerGroup::~InternalPowerGroup() +{ +} + +//////////////////////////////////////////////////////////////// + +LeakagePowerGroup::LeakagePowerGroup(int line) : + LeakagePowerAttrs(), + line_(line) +{ +} + +LeakagePowerGroup::~LeakagePowerGroup() +{ +} + +//////////////////////////////////////////////////////////////// + +PortNameBitIterator::PortNameBitIterator(LibertyCell *cell, + const char *port_name, + LibertyReader *visitor, + int line) : + cell_(cell), + visitor_(visitor), + line_(line), + port_(nullptr), + bit_iterator_(nullptr), + range_bus_port_(nullptr), + range_bus_name_(nullptr), + range_name_next_(nullptr), + size_(0) +{ + init(port_name); +} + +void +PortNameBitIterator::init(const char *port_name) +{ + LibertyPort *port = visitor_->findPort(cell_, port_name); + if (port) { + if (port->isBus()) + bit_iterator_ = new LibertyPortMemberIterator(port); + else + port_ = port; + size_ = port->size(); + } + else { + // Check for bus range. + LibertyLibrary *library = visitor_->library(); + int from, to; + char *bus_name; + parseBusRange(port_name, library->busBrktLeft(), library->busBrktRight(), + '\\', bus_name, from, to); + if (bus_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; + delete [] bus_name; + } + else + visitor_->libWarn(line_, "port %s subscript out of range.\n", + port_name); + } + else + visitor_->libWarn(line_, "port range %s of non-bus port %s.\n", + port_name, + bus_name); + } + else { + range_bus_name_ = bus_name; + range_from_ = from; + range_to_ = to; + range_bit_ = from; + findRangeBusNameNext(); + } + size_ = abs(from - to) + 1; + } + else + visitor_->libWarn(line_, "port %s not found.\n", port_name); + } +} + +PortNameBitIterator::~PortNameBitIterator() +{ + stringDelete(range_bus_name_); + delete bit_iterator_; +} + +bool +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_bus_name_ + && range_name_next_); +} + +LibertyPort * +PortNameBitIterator::next() +{ + if (port_) { + LibertyPort *next = port_; + port_ = nullptr; + return next; + } + else if (bit_iterator_) + return bit_iterator_->next(); + else if (range_bus_port_) { + LibertyPort *next = range_bus_port_->findLibertyBusBit(range_bit_); + if (range_from_ > range_to_) + range_bit_--; + else + range_bit_++; + return next; + } + else if (range_bus_name_) { + LibertyPort *next = range_name_next_; + findRangeBusNameNext(); + return next; + } + else + return nullptr; +} + +void +PortNameBitIterator::findRangeBusNameNext() +{ + if ((range_from_ > range_to_) + ? range_bit_ >= range_to_ + : range_bit_ <= range_to_) { + LibertyLibrary *library = visitor_->library(); + const char *bus_bit_name = stringPrintTmp("%s%c%d%c", + range_bus_name_, + library->busBrktLeft(), + range_bit_, + library->busBrktRight()); + range_name_next_ = visitor_->findPort(cell_, bus_bit_name); + if (range_name_next_) { + if (range_from_ > range_to_) + range_bit_--; + else + range_bit_++; + } + else + visitor_->libWarn(line_, "port %s not found.\n", bus_bit_name); + } + else + range_name_next_ = nullptr; +} + +} // namespace diff --git a/liberty/LibertyReader.hh b/liberty/LibertyReader.hh new file mode 100644 index 0000000..cd5628d --- /dev/null +++ b/liberty/LibertyReader.hh @@ -0,0 +1,31 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_LIBERTY_READER_H +#define STA_LIBERTY_READER_H + +namespace sta { + +class Network; +class LibertyLibrary; + +LibertyLibrary * +readLibertyFile(const char *filename, + bool infer_latches, + Network *network); + +} // namespace +#endif diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh new file mode 100644 index 0000000..b13cf05 --- /dev/null +++ b/liberty/LibertyReaderPvt.hh @@ -0,0 +1,829 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_LIBERTY_READER_PVT +#define STA_LIBERTY_READER_PVT + +#include "DisallowCopyAssign.hh" +#include "Vector.hh" +#include "Map.hh" +#include "StringSeq.hh" +#include "Transition.hh" +#include "MinMax.hh" +#include "TimingArc.hh" +#include "InternalPower.hh" +#include "LeakagePower.hh" +#include "Liberty.hh" +#include "LibertyParser.hh" +#include "LibertyReader.hh" +#include "NetworkClass.hh" + +namespace sta { + +class LibertyBuilder; +class LibertyReader; +class LibertyFunc; +class PortGroup; +class SequentialGroup; +class RelatedPortGroup; +class TimingGroup; +class InternalPowerGroup; +class LeakagePowerGroup; +class PortNameBitIterator; +class TimingArcBuilder; +class LibertyAttr; + +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; + +class LibertyReader : public LibertyGroupVisitor +{ +public: + explicit LibertyReader(LibertyBuilder *builder); + virtual ~LibertyReader(); + virtual LibertyLibrary *readLibertyFile(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, + TransRiseFall *tr); + virtual void visitDefaultInoutPinRiseRes(LibertyAttr *attr); + virtual void visitDefaultInoutPinFallRes(LibertyAttr *attr); + virtual void visitDefaultInoutPinRes(LibertyAttr *attr, + TransRiseFall *tr); + virtual void visitDefaultOutputPinRiseRes(LibertyAttr *attr); + virtual void visitDefaultOutputPinFallRes(LibertyAttr *attr); + virtual void visitDefaultOutputPinRes(LibertyAttr *attr, + TransRiseFall *tr); + 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, + TransRiseFall *tr); + virtual void visitOutputThresholdPctFall(LibertyAttr *attr); + virtual void visitOutputThresholdPctRise(LibertyAttr *attr); + virtual void visitOutputThresholdPct(LibertyAttr *attr, + TransRiseFall *tr); + virtual void visitSlewLowerThresholdPctFall(LibertyAttr *attr); + virtual void visitSlewLowerThresholdPctRise(LibertyAttr *attr); + virtual void visitSlewLowerThresholdPct(LibertyAttr *attr, + TransRiseFall *tr); + virtual void visitSlewUpperThresholdPctFall(LibertyAttr *attr); + virtual void visitSlewUpperThresholdPctRise(LibertyAttr *attr); + virtual void visitSlewUpperThresholdPct(LibertyAttr *attr, + TransRiseFall *tr); + virtual void visitSlewDerateFromLibrary(LibertyAttr *attr); + + 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 makeLeakagePowers(); + virtual void parseCellFuncs(); + virtual void makeLibertyFunc(const char *expr, + FuncExpr *&func_ref, + 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 visitClockGatingIntegratedCell(LibertyAttr *attr); + virtual void visitArea(LibertyAttr *attr); + virtual void visitDontUse(LibertyAttr *attr); + virtual void visitIsMacro(LibertyAttr *attr); + virtual void visitIsPad(LibertyAttr *attr); + virtual void visitInterfaceTiming(LibertyAttr *attr); + virtual void visitScalingFactors(LibertyAttr *attr); + virtual void visitCellLeakagePower(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 setPortDefaults(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 visitMaxFanout(LibertyAttr *attr); + virtual void visitMinFanout(LibertyAttr *attr); + virtual void visitFanout(LibertyAttr *attr, + MinMax *min_max); + virtual void visitMaxTransition(LibertyAttr *attr); + virtual void visitMinTransition(LibertyAttr *attr); + virtual void visitMinMaxTransition(LibertyAttr *attr, + MinMax *min_max); + virtual void visitMaxCapacitance(LibertyAttr *attr); + virtual void visitMinCapacitance(LibertyAttr *attr); + virtual void visitMinMaxCapacitance(LibertyAttr *attr, + MinMax *min_max); + virtual void visitMinPeriod(LibertyAttr *attr); + virtual void visitMinPulseWidthLow(LibertyAttr *attr); + virtual void visitMinPulseWidthHigh(LibertyAttr *attr); + virtual void visitMinPulseWidth(LibertyAttr *attr, + TransRiseFall *tr); + 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); + EarlyLateAll *getAttrEarlyLate(LibertyAttr *attr); + virtual void visitClock(LibertyAttr *attr); + + 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 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 beginTiming(LibertyGroup *group); + virtual void endTiming(LibertyGroup *group); + virtual TimingGroup *makeTimingGroup(int line); + 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, + TransRiseFall *tr); + virtual void visitRiseResistance(LibertyAttr *attr); + virtual void visitFallResistance(LibertyAttr *attr); + virtual void visitRiseFallResistance(LibertyAttr *attr, + TransRiseFall *tr); + 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, + TransRiseFall *tr, + float scale, + ScaleFactorType scale_factor_type); + virtual void endTableModel(); + virtual void beginTimingTableModel(LibertyGroup *group, + TransRiseFall *tr, + 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 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); + + // Visitors for derived classes to overload. + virtual void beginGroup1(LibertyGroup *) {} + virtual void beginGroup2(LibertyGroup *) {} + virtual void beginGroup3(LibertyGroup *) {} + virtual void beginGroup4(LibertyGroup *) {} + virtual void beginGroup5(LibertyGroup *) {} + virtual void endGroup1(LibertyGroup *) {} + virtual void endGroup2(LibertyGroup *) {} + virtual void endGroup3(LibertyGroup *) {} + virtual void endGroup4(LibertyGroup *) {} + virtual void endGroup5(LibertyGroup *) {} + virtual void visitAttr1(LibertyAttr *) {} + virtual void visitAttr2(LibertyAttr *) {} + virtual void visitAttr3(LibertyAttr *) {} + virtual void visitAttr4(LibertyAttr *) {} + virtual void visitAttr5(LibertyAttr *) {} + virtual void visitAttr6(LibertyAttr *) {} + virtual void visitAttr7(LibertyAttr *) {} + virtual void visitAttr8(LibertyAttr *) {} + virtual void visitAttr9(LibertyAttr *) {} + +protected: + void setEnergyScale(); + 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); + StringSeq *parseNameList(const char *name_list); + LibertyPort *findPort(const char *port_name); + LibertyPort *findPort(LibertyCell *cell, + const char *port_name); + float defaultCap(LibertyPort *port); + virtual void visitVariable(LibertyVariable *var); + 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); + void makeAxis(int index, + LibertyGroup *group, + TableAxis *&axis); + 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(LibertyStmt *stmt, + const char *fmt, + ...) + __attribute__((format (printf, 3, 4))); + void libWarn(int line, + const char *fmt, + ...) + __attribute__((format (printf, 3, 4))); + void libError(LibertyStmt *stmt, + const char *fmt, ...) + __attribute__((format (printf, 3, 4))); + + const char *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_[TransRiseFall::index_count]; + bool have_output_threshold_[TransRiseFall::index_count]; + bool have_slew_lower_threshold_[TransRiseFall::index_count]; + bool have_slew_upper_threshold_[TransRiseFall::index_count]; + TableTemplate *tbl_template_; + LibertyCell *cell_; + LibertyCell *save_cell_; + LibertyCell *scaled_cell_owner_; + TestCell *test_cell_; + const char *ocv_derate_name_; + PortGroupSeq cell_port_groups_; + OperatingConditions *op_cond_; + LibertyPortSeq *ports_; + LibertyPort *port_; // Used by test_cell. + PortGroup *port_group_; + LibertyPortSeq *saved_ports_; + PortGroup *saved_port_group_; + StringSeq bus_names_; + bool in_bus_; + bool in_bundle_; + 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_; + TimingGroup *timing_; + InternalPowerGroup *internal_power_; + LeakagePowerGroup *leakage_power_; + LeakagePowerGroupSeq leakage_powers_; + TransRiseFall *tr_; + OcvDerate *ocv_derate_; + TransRiseFallBoth *rf_type_; + EarlyLateAll *derate_type_; + EarlyLateAll *sigma_type_; + PathType path_type_; + LibertyPgPort *pg_port_; + ScaleFactorType scale_factor_type_; + TableAxis *axis_[3]; + bool own_axis_[3]; + Table *table_; + float table_model_scale_; + ModeDef *mode_def_; + ModeValueDef *mode_value_; + LibertyFuncSeq cell_funcs_; + float time_scale_; + float cap_scale_; + float res_scale_; + float volt_scale_; + float current_scale_; + float power_scale_; + float energy_scale_; + float distance_scale_; + bool have_resistance_unit_; + static constexpr char escape_ = '\\'; + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyReader); + + 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, + FuncExpr *&func_ref, + bool invert, + const char *attr_name, + int line); + ~LibertyFunc(); + const char *expr() const { return expr_; } + FuncExpr *&funcRef() const { return func_ref_; } + bool invert() const { return invert_; } + const char *attrName() const { return attr_name_; } + int line() const { return line_; } + +protected: + const char *expr_; + FuncExpr *&func_ref_; + bool invert_; + const char *attr_name_; + int line_; + +private: + DISALLOW_COPY_AND_ASSIGN(LibertyFunc); +}; + +// 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); + int line() const { return line_; } + +private: + DISALLOW_COPY_AND_ASSIGN(PortGroup); + + LibertyPortSeq *ports_; + TimingGroupSeq timings_; + InternalPowerGroupSeq internal_power_groups_; + 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_; + +private: + DISALLOW_COPY_AND_ASSIGN(SequentialGroup); +}; + +// 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_; + +private: + DISALLOW_COPY_AND_ASSIGN(RelatedPortGroup); +}; + +class TimingGroup : public TimingArcAttrs, public RelatedPortGroup +{ +public: + explicit TimingGroup(int line); + virtual ~TimingGroup(); + const char *relatedOutputPortName()const {return related_output_port_name_;} + void setRelatedOutputPortName(const char *name); + void intrinsic(TransRiseFall *tr, + // Return values. + float &value, + bool &exists); + void setIntrinsic(TransRiseFall *tr, + float value); + void resistance(TransRiseFall *tr, + // Return values. + float &value, + bool &exists); + void setResistance(TransRiseFall *tr, + float value); + TableModel *cell(TransRiseFall *tr); + void setCell(TransRiseFall *tr, + TableModel *model); + TableModel *constraint(TransRiseFall *tr); + void setConstraint(TransRiseFall *tr, + TableModel *model); + TableModel *transition(TransRiseFall *tr); + void setTransition(TransRiseFall *tr, + TableModel *model); + void makeTimingModels(LibertyLibrary *library, + LibertyReader *visitor); + void setDelaySigma(TransRiseFall *tr, + EarlyLate *early_late, + TableModel *model); + void setSlewSigma(TransRiseFall *tr, + EarlyLate *early_late, + TableModel *model); + void setConstraintSigma(TransRiseFall *tr, + EarlyLate *early_late, + TableModel *model); + +protected: + void makeLinearModels(LibertyLibrary *library); + void makeTableModels(LibertyReader *visitor); + + const char *related_output_port_name_; + float intrinsic_[TransRiseFall::index_count]; + bool intrinsic_exists_[TransRiseFall::index_count]; + float resistance_[TransRiseFall::index_count]; + bool resistance_exists_[TransRiseFall::index_count]; + TableModel *cell_[TransRiseFall::index_count]; + TableModel *constraint_[TransRiseFall::index_count]; + TableModel *constraint_sigma_[TransRiseFall::index_count][EarlyLate::index_count]; + TableModel *transition_[TransRiseFall::index_count]; + TableModel *delay_sigma_[TransRiseFall::index_count][EarlyLate::index_count]; + TableModel *slew_sigma_[TransRiseFall::index_count][EarlyLate::index_count]; + +private: + DISALLOW_COPY_AND_ASSIGN(TimingGroup); +}; + +class InternalPowerGroup : public InternalPowerAttrs, public RelatedPortGroup +{ +public: + explicit InternalPowerGroup(int line); + virtual ~InternalPowerGroup(); + +protected: + +private: + DISALLOW_COPY_AND_ASSIGN(InternalPowerGroup); +}; + +class LeakagePowerGroup : public LeakagePowerAttrs +{ +public: + explicit LeakagePowerGroup(int line); + virtual ~LeakagePowerGroup(); + +protected: + int line_; + +private: + DISALLOW_COPY_AND_ASSIGN(LeakagePowerGroup); +}; + +// Named port iterator. Port name can be: +// Single bit port name - iterates over port. +// Bus port name - iterates over bus bit ports. +// Bus range - iterates over bus bit ports. +class PortNameBitIterator : public Iterator +{ +public: + PortNameBitIterator(LibertyCell *cell, + const char *port_name, + LibertyReader *visitor, + int line); + ~PortNameBitIterator(); + virtual bool hasNext(); + virtual LibertyPort *next(); + unsigned size() const { return size_; } + +protected: + void findRangeBusNameNext(); + + void init(const char *port_name); + LibertyCell *cell_; + LibertyReader *visitor_; + int line_; + LibertyPort *port_; + LibertyPortMemberIterator *bit_iterator_; + LibertyPort *range_bus_port_; + const char *range_bus_name_; + LibertyPort *range_name_next_; + int range_from_; + int range_to_; + int range_bit_; + unsigned size_; + +private: + DISALLOW_COPY_AND_ASSIGN(PortNameBitIterator); +}; + +} // namespace +#endif diff --git a/liberty/LinearModel.cc b/liberty/LinearModel.cc new file mode 100644 index 0000000..a6afb5f --- /dev/null +++ b/liberty/LinearModel.cc @@ -0,0 +1,124 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Units.hh" +#include "Liberty.hh" +#include "LinearModel.hh" + +namespace sta { + +GateLinearModel::GateLinearModel(float intrinsic, + float resistance) : + intrinsic_(intrinsic), + resistance_(resistance) +{ +} + +void +GateLinearModel::gateDelay(const LibertyCell *, + const Pvt *, + float, + float load_cap, + float, + bool, + // return values + ArcDelay &gate_delay, + Slew &drvr_slew) const +{ + gate_delay = intrinsic_ + resistance_ * load_cap; + drvr_slew = 0.0; +} + +void +GateLinearModel::reportGateDelay(const LibertyCell *cell, + const Pvt *, + float, + float load_cap, + float, + bool, + int digits, + string *result) 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(); + *result += "Delay = "; + *result += time_unit->asString(intrinsic_, digits); + *result += " + "; + *result += res_unit->asString(resistance_, digits); + *result += " * "; + *result += cap_unit->asString(load_cap, digits); + *result += " = "; + float delay = intrinsic_ + resistance_ * load_cap; + *result += time_unit->asString(delay, digits); +} + +float +GateLinearModel::driveResistance(const LibertyCell *, + const Pvt *) const +{ + return resistance_; +} + +void +GateLinearModel::setIsScaled(bool) +{ +} + +CheckLinearModel::CheckLinearModel(float intrinsic) : + intrinsic_(intrinsic) +{ +} + +void +CheckLinearModel::checkDelay(const LibertyCell *, + const Pvt *, + float, + float, + float, + bool, + ArcDelay &margin) const +{ + margin = intrinsic_; +} + +void +CheckLinearModel::reportCheckDelay(const LibertyCell *cell, + const Pvt *, + float, + const char *, + float, + float, + bool, + int digits, + string *result) const +{ + const LibertyLibrary *library = cell->libertyLibrary(); + const Units *units = library->units(); + const Unit *time_unit = units->timeUnit(); + *result += "Check = "; + *result += time_unit->asString(intrinsic_, digits); +} + +void +CheckLinearModel::setIsScaled(bool) +{ +} + +} // namespace diff --git a/liberty/LinearModel.hh b/liberty/LinearModel.hh new file mode 100644 index 0000000..4baef95 --- /dev/null +++ b/liberty/LinearModel.hh @@ -0,0 +1,91 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_LINEAR_MODEL_H +#define STA_LINEAR_MODEL_H + +#include "DisallowCopyAssign.hh" +#include "TimingModel.hh" + +namespace sta { + +class GateLinearModel : public GateTimingModel +{ +public: + GateLinearModel(float intrinsic, float resistance); + // gate delay calculation + virtual void gateDelay(const LibertyCell *cell, + const Pvt *pvt, + float load_cap, float in_slew, + float related_out_cap, + bool pocv_enabled, + // return values + ArcDelay &gate_delay, + Slew &drvr_slew) const; + virtual void reportGateDelay(const LibertyCell *cell, + const Pvt *pvt, + float load_cap, + float in_slew, + float related_out_cap, + bool pocv_enabled, + int digits, + string *result) const; + virtual float driveResistance(const LibertyCell *cell, + const Pvt *pvt) const; + +protected: + virtual void setIsScaled(bool is_scaled); + + float intrinsic_; + float resistance_; + +private: + DISALLOW_COPY_AND_ASSIGN(GateLinearModel); +}; + +class CheckLinearModel : public CheckTimingModel +{ +public: + explicit CheckLinearModel(float intrinsic); + // Timing check margin delay calculation. + virtual void checkDelay(const LibertyCell *cell, + const Pvt *pvt, + float from_slew, + float to_slew, + float related_out_cap, + bool pocv_enabled, + ArcDelay &margin) const; + virtual void reportCheckDelay(const LibertyCell *cell, + const Pvt *pvt, + float from_slew, + const char *from_slew_annotation, + float to_slew, + float related_out_cap, + bool pocv_enabled, + int digits, + string *result) const; + +protected: + virtual void setIsScaled(bool is_scaled); + + float intrinsic_; + +private: + DISALLOW_COPY_AND_ASSIGN(CheckLinearModel); +}; + +} // namespace +#endif diff --git a/liberty/Makefile.am b/liberty/Makefile.am new file mode 100644 index 0000000..5c0ff65 --- /dev/null +++ b/liberty/Makefile.am @@ -0,0 +1,103 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +lib_LTLIBRARIES = libliberty.la + +include_HEADERS = \ + EquivCells.hh \ + FuncExpr.hh \ + InternalPower.hh \ + LeakagePower.hh \ + Liberty.hh \ + LibertyBuilder.hh \ + LibertyClass.hh \ + LibertyParser.hh \ + LibertyReader.hh \ + LibertyReaderPvt.hh \ + LinearModel.hh \ + Sequential.hh \ + TableModel.hh \ + TimingArc.hh \ + TimingModel.hh \ + TimingRole.hh \ + Transition.hh \ + Units.hh \ + Wireload.hh + +libliberty_la_SOURCES = \ + EquivCells.cc \ + FuncExpr.cc \ + InternalPower.cc \ + LeakagePower.cc \ + Liberty.cc \ + LibertyBuilder.cc \ + LibertyExpr.cc \ + LibertyExpr.hh \ + LibertyExprLex.ll \ + LibertyExprParse.yy \ + LibertyExprPvt.hh \ + LibertyLex.ll \ + LibertyParse.yy \ + LibertyParser.cc \ + LibertyReader.cc \ + LinearModel.cc \ + Sequential.cc \ + TableModel.cc \ + TimingArc.cc \ + TimingRole.cc \ + Transition.cc \ + Units.cc \ + Wireload.cc + +LibertyExprLex.ll: LibertyExprParse.hh + +LibertyExprLex.cc: LibertyExprLex.ll + $(LEX) $(LFLAGS) -o LibertyExprLex.cc --prefix=LibertyExprLex_ --header-file=LibertyExprLex.hh LibertyExprLex.ll + +LibertyLex.cc: LibertyLex.ll + $(LEX) $(LFLAGS) -o LibertyLex.cc --prefix=LibertyLex_ --header-file=LibertyLex.hh LibertyLex.ll + +LibertyLex.ll: LibertyParse.hh + +# Rules to support automake pre 1.12 that name header .h instead of .hh +LibertyExprParse.hh: LibertyExprParse.cc + if test -f LibertyExprParse.h; then \ + cp LibertyExprParse.h LibertyExprParse.hh; \ + fi + +LibertyParse.hh: LibertyParse.cc + if test -f LibertyParse.h; then \ + cp LibertyParse.h LibertyParse.hh; \ + fi + +EXTRA_DIST = \ + LibertyExt.cc \ + LibertyParse.hh \ + LibertyExprParse.hh + +MAINTAINERCLEANFILES = \ + LibertyLex.cc \ + LibertyParse.hh \ + LibertyParse.cc \ + LibertyExprLex.cc \ + LibertyExprParse.hh \ + LibertyExprParse.cc + +libs: $(lib_LTLIBRARIES) + +xtags: $(SOURCES) $(HEADERS) + etags -a -o ../TAGS $(SOURCES) $(HEADERS) + diff --git a/liberty/Sequential.cc b/liberty/Sequential.cc new file mode 100644 index 0000000..26ab603 --- /dev/null +++ b/liberty/Sequential.cc @@ -0,0 +1,56 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "FuncExpr.hh" +#include "Sequential.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) : + is_register_(is_register), + clock_(clock), + data_(data), + clear_(clear), + preset_(preset), + clr_preset_out_(clr_preset_out), + clr_preset_out_inv_(clr_preset_out_inv), + output_(output), + output_inv_(output_inv) +{ +} + +Sequential::~Sequential() +{ + if (clock_) + clock_->deleteSubexprs(); + if (data_) + data_->deleteSubexprs(); + if (clear_) + clear_->deleteSubexprs(); + if (preset_) + preset_->deleteSubexprs(); +} + +} // namespace diff --git a/liberty/Sequential.hh b/liberty/Sequential.hh new file mode 100644 index 0000000..edcb545 --- /dev/null +++ b/liberty/Sequential.hh @@ -0,0 +1,75 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SEQUENTIAL_H +#define STA_SEQUENTIAL_H + +#include "DisallowCopyAssign.hh" +#include "NetworkClass.hh" +#include "LibertyClass.hh" + +namespace sta { + +// Register/Latch +class Sequential +{ +public: + ~Sequential(); + bool isLatch() const { return !is_register_; } + bool isRegister() const { return is_register_; } + FuncExpr *clock() const { return clock_; } + FuncExpr *data() const { return data_; } + FuncExpr *clear() const { return clear_; } + FuncExpr *preset() const { return preset_; } + // State of output when clear and preset are both true. + LogicValue clearPresetOutput() const { return clr_preset_out_; } + // State of outputInv when clear and preset are both true. + LogicValue clearPresetOutputInv() const {return clr_preset_out_inv_;} + 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); + + 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_; + +private: + DISALLOW_COPY_AND_ASSIGN(Sequential); + + friend class LibertyCell; +}; + +} // namespace +#endif diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc new file mode 100644 index 0000000..4b91294 --- /dev/null +++ b/liberty/TableModel.cc @@ -0,0 +1,1409 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Report.hh" +#include "Error.hh" +#include "EnumNameMap.hh" +#include "Units.hh" +#include "Liberty.hh" +#include "TableModel.hh" + +namespace sta { + +static void +deleteSigmaModels(TableModel *models[EarlyLate::index_count]); +static void +reportPvt(const LibertyLibrary *library, + const Pvt *pvt, + int digits, + string *result); +static void +appendSpaces(string *result, + int count); + +GateTableModel::GateTableModel(TableModel *delay_model, + TableModel *delay_sigma_models[EarlyLate::index_count], + TableModel *slew_model, + TableModel *slew_sigma_models[EarlyLate::index_count]) : + delay_model_(delay_model), + slew_model_(slew_model) +{ + for (auto el_index : EarlyLate::rangeIndex()) { + slew_sigma_models_[el_index] = slew_sigma_models[el_index]; + delay_sigma_models_[el_index] = delay_sigma_models[el_index]; + } +} + +GateTableModel::~GateTableModel() +{ + delete delay_model_; + delete slew_model_; + deleteSigmaModels(slew_sigma_models_); + deleteSigmaModels(delay_sigma_models_); +} + +static void +deleteSigmaModels(TableModel *models[EarlyLate::index_count]) +{ + 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; + } +} + +void +GateTableModel::setIsScaled(bool is_scaled) +{ + if (delay_model_) + delay_model_->setIsScaled(is_scaled); + if (slew_model_) + slew_model_->setIsScaled(is_scaled); +} + +void +GateTableModel::gateDelay(const LibertyCell *cell, + const Pvt *pvt, + float in_slew, + float load_cap, + float related_out_cap, + bool pocv_enabled, + // return values + ArcDelay &gate_delay, + Slew &drvr_slew) const +{ + const LibertyLibrary *library = cell->libertyLibrary(); + float delay = findValue(library, cell, pvt, delay_model_, in_slew, + load_cap, related_out_cap); + float sigma_early = 0.0; + float sigma_late = 0.0; + if (pocv_enabled && delay_sigma_models_[EarlyLate::earlyIndex()]) + sigma_early = findValue(library, cell, pvt, + delay_sigma_models_[EarlyLate::earlyIndex()], + in_slew, load_cap, related_out_cap); + if (pocv_enabled && delay_sigma_models_[EarlyLate::lateIndex()]) + sigma_late = findValue(library, cell, pvt, + delay_sigma_models_[EarlyLate::earlyIndex()], + in_slew, load_cap, related_out_cap); + gate_delay = makeDelay(delay, sigma_early, sigma_late); + + float slew = findValue(library, cell, pvt, slew_model_, in_slew, + load_cap, related_out_cap); + if (pocv_enabled && slew_sigma_models_[EarlyLate::earlyIndex()]) + sigma_early = findValue(library, cell, pvt, + slew_sigma_models_[EarlyLate::earlyIndex()], + in_slew, load_cap, related_out_cap); + if (pocv_enabled && slew_sigma_models_[EarlyLate::lateIndex()]) + sigma_late = findValue(library, cell, pvt, + slew_sigma_models_[EarlyLate::earlyIndex()], + in_slew, load_cap, related_out_cap); + // Clip negative slews to zero. + if (slew < 0.0) + slew = 0.0; + drvr_slew = makeDelay(slew, sigma_early, sigma_late); +} + +void +GateTableModel::reportGateDelay(const LibertyCell *cell, + const Pvt *pvt, + float in_slew, + float load_cap, + float related_out_cap, + bool pocv_enabled, + int digits, + string *result) const +{ + const LibertyLibrary *library = cell->libertyLibrary(); + reportPvt(library, pvt, digits, result); + reportTableLookup("Delay", library, cell, pvt, delay_model_, in_slew, + load_cap, related_out_cap, digits, result); + if (pocv_enabled && delay_sigma_models_[EarlyLate::earlyIndex()]) + reportTableLookup("Delay sigma(early)", library, cell, pvt, + delay_sigma_models_[EarlyLate::earlyIndex()], + in_slew, load_cap, related_out_cap, digits, result); + if (pocv_enabled && delay_sigma_models_[EarlyLate::lateIndex()]) + reportTableLookup("Delay sigma(late)", library, cell, pvt, + delay_sigma_models_[EarlyLate::lateIndex()], + in_slew, load_cap, related_out_cap, digits, result); + *result += '\n'; + reportTableLookup("Slew", library, cell, pvt, slew_model_, in_slew, + load_cap, related_out_cap, digits, result); + if (pocv_enabled && slew_sigma_models_[EarlyLate::earlyIndex()]) + reportTableLookup("Slew sigma(early)", library, cell, pvt, + slew_sigma_models_[EarlyLate::earlyIndex()], + in_slew, load_cap, related_out_cap, digits, result); + if (pocv_enabled && slew_sigma_models_[EarlyLate::lateIndex()]) + reportTableLookup("Slew sigma(late)", library, cell, pvt, + slew_sigma_models_[EarlyLate::lateIndex()], + in_slew, load_cap, related_out_cap, digits, result); + float drvr_slew = findValue(library, cell, pvt, slew_model_, in_slew, + load_cap, related_out_cap); + if (drvr_slew < 0.0) + *result += "Negative slew clipped to 0.0\n"; +} + +void +GateTableModel::reportTableLookup(const char *result_name, + const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + const TableModel *model, + float in_slew, + float load_cap, + float related_out_cap, + int digits, + string *result) 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); + model->reportValue(result_name, library, cell, pvt, + axis_value1, nullptr, axis_value2, axis_value3, + digits, result); + } +} + +float +GateTableModel::findValue(const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + 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); + return model->findValue(library, cell, pvt, + axis_value1, axis_value2, axis_value3); + } + else + return 0.0; +} + +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 +{ + 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: + internalError("unsupported table order"); + } +} + +// Use slew/Cload for the highest Cload, which approximates output +// admittance as the "drive". +float +GateTableModel::driveResistance(const LibertyCell *cell, + const Pvt *pvt) const +{ + float slew, cap; + maxCapSlew(cell, 0.0, pvt, slew, cap); + return slew / cap; +} + +void +GateTableModel::maxCapSlew(const LibertyCell *cell, + float in_slew, + const Pvt *pvt, + float &slew, + float &cap) const +{ + const LibertyLibrary *library = cell->libertyLibrary(); + TableAxis *axis1 = slew_model_->axis1(); + TableAxis *axis2 = slew_model_->axis2(); + TableAxis *axis3 = slew_model_->axis3(); + if (axis1 + && axis1->variable() == TableAxisVariable::total_output_net_capacitance) { + cap = axis1->axisValue(axis1->size() - 1); + slew = findValue(library, cell, pvt, slew_model_, + in_slew, cap, 0.0); + } + else if (axis2 + && axis2->variable()==TableAxisVariable::total_output_net_capacitance) { + cap = axis2->axisValue(axis2->size() - 1); + slew = findValue(library, cell, pvt, slew_model_, + in_slew, cap, 0.0); + } + else if (axis3 + && axis3->variable()==TableAxisVariable::total_output_net_capacitance) { + cap = axis3->axisValue(axis3->size() - 1); + slew = findValue(library, cell, pvt, slew_model_, + in_slew, cap, 0.0); + } + else { + // Table not dependent on capacitance. + cap = 1.0; + slew = 0.0; + } + // Clip negative slews to zero. + if (slew < 0.0) + slew = 0.0; +} + +float +GateTableModel::axisValue(TableAxis *axis, + float in_slew, + float load_cap, + float related_out_cap) const +{ + TableAxisVariable var = axis->variable(); + if (var == TableAxisVariable::input_transition_time + || var == TableAxisVariable::input_net_transition) + return in_slew; + else if (var == TableAxisVariable::total_output_net_capacitance) + return load_cap; + else if (var == TableAxisVariable::related_out_total_output_net_capacitance) + return related_out_cap; + else { + internalError("unsupported table axes"); + return 0.0; + } +} + +bool +GateTableModel::checkAxes(const Table *table) +{ + TableAxis *axis1 = table->axis1(); + TableAxis *axis2 = table->axis2(); + TableAxis *axis3 = table->axis3(); + bool axis_ok = true; + if (axis1) + axis_ok &= checkAxis(table->axis1()); + if (axis2) + axis_ok &= checkAxis(table->axis2()); + if (axis3) + axis_ok &= checkAxis(table->axis3()); + return axis_ok; +} + +bool +GateTableModel::checkAxis(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; +} + +//////////////////////////////////////////////////////////////// + +CheckTableModel::CheckTableModel(TableModel *model, + TableModel *sigma_models[EarlyLate::index_count]) : + model_(model) +{ + for (auto el_index : EarlyLate::rangeIndex()) + sigma_models_[el_index] = sigma_models[el_index]; +} + +CheckTableModel::~CheckTableModel() +{ + delete model_; + deleteSigmaModels(sigma_models_); +} + +void +CheckTableModel::setIsScaled(bool is_scaled) +{ + model_->setIsScaled(is_scaled); +} + +void +CheckTableModel::checkDelay(const LibertyCell *cell, + const Pvt *pvt, + float from_slew, + float to_slew, + float related_out_cap, + bool pocv_enabled, + // Return values. + ArcDelay &margin) const +{ + if (model_) { + const LibertyLibrary *library = cell->libertyLibrary(); + float mean = findValue(library, cell, 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(library, cell, pvt, + sigma_models_[EarlyLate::earlyIndex()], + from_slew, to_slew, related_out_cap); + if (pocv_enabled && sigma_models_[EarlyLate::lateIndex()]) + sigma_late = findValue(library, cell, pvt, + sigma_models_[EarlyLate::earlyIndex()], + from_slew, to_slew, related_out_cap); + margin = makeDelay(mean, sigma_early, sigma_late); + } + else + margin = 0.0; +} + +float +CheckTableModel::findValue(const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + 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); + return model->findValue(library, cell, pvt, + axis_value1, axis_value2, axis_value3); + } + else + return 0.0; +} + +void +CheckTableModel::reportCheckDelay(const LibertyCell *cell, + const Pvt *pvt, + float from_slew, + const char *from_slew_annotation, + float to_slew, + float related_out_cap, + bool pocv_enabled, + int digits, + string *result) const +{ + const LibertyLibrary *library = cell->libertyLibrary(); + reportTableDelay("Check", library, cell, pvt, model_, + from_slew, from_slew_annotation, to_slew, + related_out_cap, digits, result); + if (pocv_enabled && sigma_models_[EarlyLate::earlyIndex()]) + reportTableDelay("Check sigma early", library, cell, pvt, + sigma_models_[EarlyLate::earlyIndex()], + from_slew, from_slew_annotation, to_slew, + related_out_cap, digits, result); + if (pocv_enabled && sigma_models_[EarlyLate::lateIndex()]) + reportTableDelay("Check sigma late", library, cell, pvt, + sigma_models_[EarlyLate::lateIndex()], + from_slew, from_slew_annotation, to_slew, + related_out_cap, digits, result); +} + +void +CheckTableModel::reportTableDelay(const char *result_name, + const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + const TableModel *model, + float from_slew, + const char *from_slew_annotation, + float to_slew, + float related_out_cap, + int digits, + string *result) const +{ + if (model) { + float axis_value1, axis_value2, axis_value3; + findAxisValues(from_slew, to_slew, related_out_cap, + axis_value1, axis_value2, axis_value3); + reportPvt(library, pvt, digits, result); + model_->reportValue(result_name, library, cell, pvt, + axis_value1, from_slew_annotation, axis_value2, + axis_value3, digits, result); + } +} + +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()) { + 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_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_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); + break; + default: + internalError("unsupported table order"); + } +} + +float +CheckTableModel::axisValue(TableAxis *axis, + float from_slew, + float to_slew, + float related_out_cap) const +{ + TableAxisVariable var = axis->variable(); + if (var == TableAxisVariable::related_pin_transition) + return from_slew; + else if (var == TableAxisVariable::constrained_pin_transition) + return to_slew; + else if (var == TableAxisVariable::related_out_total_output_net_capacitance) + return related_out_cap; + else { + internalError("unsupported table axes"); + return 0.0; + } +} + +bool +CheckTableModel::checkAxes(const Table *table) +{ + TableAxis *axis1 = table->axis1(); + TableAxis *axis2 = table->axis2(); + TableAxis *axis3 = table->axis3(); + bool axis_ok = true; + if (axis1) + axis_ok &= checkAxis(table->axis1()); + if (axis2) + axis_ok &= checkAxis(table->axis2()); + if (axis3) + axis_ok &= checkAxis(table->axis3()); + return axis_ok; +} + +bool +CheckTableModel::checkAxis(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; +} + +//////////////////////////////////////////////////////////////// + +TableModel::TableModel(Table *table, + ScaleFactorType scale_factor_type, + TransRiseFall *tr) : + table_(table), + scale_factor_type_(int(scale_factor_type)), + tr_index_(tr->index()), + is_scaled_(false) +{ +} + +TableModel::~TableModel() +{ + delete table_; +} + +int +TableModel::order() const +{ + return table_->order(); +} + +void +TableModel::setScaleFactorType(ScaleFactorType type) +{ + scale_factor_type_ = int(type); +} + +void +TableModel::setIsScaled(bool is_scaled) +{ + is_scaled_ = is_scaled; +} + +TableAxis * +TableModel::axis1() const +{ + return table_->axis1(); +} + +TableAxis * +TableModel::axis2() const +{ + return table_->axis2(); +} + +TableAxis * +TableModel::axis3() const +{ + return table_->axis3(); +} + +float +TableModel::findValue(const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + float value1, + float value2, + float value3) const +{ + return table_->findValue(value1, value2, value3) + * scaleFactor(library, cell, pvt); +} + +float +TableModel::scaleFactor(const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt) const +{ + if (is_scaled_) + // Scaled tables are not derated because scale factors are wrt + // nominal pvt. + return 1.0F; + else + return library->scaleFactor(static_cast(scale_factor_type_), + tr_index_, cell, pvt); +} + +void +TableModel::reportValue(const char *result_name, + const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + float value1, + const char *comment1, + float value2, + float value3, + int digits, + string *result) const +{ + table_->reportValue("Table value", library, cell, pvt, value1, + comment1, value2, value3, digits, result); + + reportPvtScaleFactor(library, cell, pvt, digits, result); + + const Units *units = library->units(); + const Unit *table_unit = units->timeUnit(); + *result += result_name; + *result += " = "; + *result += table_unit->asString(findValue(library, cell, pvt, + value1, value2, value3), digits); + *result += '\n'; +} + +static void +reportPvt(const LibertyLibrary *library, + const Pvt *pvt, + int digits, + string *result) +{ + if (pvt == nullptr) + pvt = library->defaultOperatingConditions(); + if (pvt) { + string pvt_str; + stringPrint(pvt_str, "P = %.*f V = %.*f T = %.*f\n", + digits, pvt->process(), + digits, pvt->voltage(), + digits, pvt->temperature()); + *result += pvt_str; + } +} + +void +TableModel::reportPvtScaleFactor(const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + int digits, + string *result) const +{ + if (pvt == nullptr) + pvt = library->defaultOperatingConditions(); + if (pvt) { + string scale_str; + stringPrint(scale_str, "PVT scale factor = %.*f\n", + digits, + scaleFactor(library, cell, pvt)); + *result += scale_str; + } +} + +//////////////////////////////////////////////////////////////// + +Table0::Table0(float value) : + Table(), + value_(value) +{ +} + +float +Table0::findValue(float, + float, + float) const +{ + return value_; +} + +void +Table0::reportValue(const char *result_name, + const LibertyLibrary *library, + const LibertyCell *, + const Pvt *, + float value1, + const char *comment1, + float value2, + float value3, + int digits, + string *result) const +{ + const Units *units = library->units(); + const Unit *table_unit = units->timeUnit(); + *result += result_name; + *result += " constant = "; + *result += table_unit->asString(findValue(value1, value2, value3), digits); + if (comment1) + *result += comment1; + *result += '\n'; +} + +void +Table0::report(const Units *units, + Report *report) const +{ + int digits = 4; + const Unit *table_unit = units->timeUnit(); + report->print("%s\n", table_unit->asString(value_, digits)); +} + +//////////////////////////////////////////////////////////////// + +Table1::Table1(FloatSeq *values, + TableAxis *axis1, + bool own_axis1) : + Table(), + values_(values), + axis1_(axis1), + own_axis1_(own_axis1) +{ +} + +Table1::~Table1() +{ + delete values_; + if (own_axis1_) + delete axis1_; +} + +float +Table1::tableValue(size_t index1) const +{ + return (*values_)[index1]; +} + +float +Table1::findValue(float value1, + float, + float) const +{ + if (axis1_->size() == 1) + return tableValue(value1); + else { + size_t index1 = axis1_->findAxisIndex(value1); + float x1 = value1; + float x1l = axis1_->axisValue(index1); + float x1u = axis1_->axisValue(index1 + 1); + float y1 = tableValue(index1); + float y2 = tableValue(index1 + 1); + float dx1 = (x1 - x1l) / (x1u - x1l); + return (1 - dx1) * y1 + dx1 * y2; + } +} + +void +Table1::reportValue(const char *result_name, const + LibertyLibrary *library, + const LibertyCell *, + const Pvt *, + float value1, + const char *comment1, + float value2, + float value3, + int digits, + string *result) const +{ + TableAxisVariable var1 = axis1_->variable(); + const Units *units = library->units(); + const Unit *table_unit = units->timeUnit(); + const Unit *unit1 = tableVariableUnit(var1, units); + *result += "Table is indexed by\n"; + *result += " "; + *result += tableVariableString(var1); + *result += " = "; + *result += unit1->asString(value1, digits); + if (comment1) + *result += comment1; + *result += '\n'; + + if (axis1_->size() != 1) { + size_t index1 = axis1_->findAxisIndex(value1); + *result += " "; + *result += unit1->asString(axis1_->axisValue(index1), digits); + *result += " "; + *result += unit1->asString(axis1_->axisValue(index1 + 1), digits); + *result += '\n'; + + *result += " --------------------\n"; + + *result += "| "; + *result += table_unit->asString(tableValue(index1), digits); + *result += " "; + *result += table_unit->asString(tableValue(index1 + 1), + digits); + *result += '\n'; + } + + *result += result_name; + *result += " = "; + *result += table_unit->asString(findValue(value1, value2, value3), digits); + *result += '\n'; +} + +void +Table1::report(const Units *units, + Report *report) const +{ + int digits = 4; + const Unit *unit1 = tableVariableUnit(axis1_->variable(), units); + const Unit *table_unit = units->timeUnit(); + report->print("%s\n", tableVariableString(axis1_->variable())); + report->print("------------------------------\n"); + for (size_t index1 = 0; index1 < axis1_->size(); index1++) + report->print("%s ", unit1->asString(axis1_->axisValue(index1), digits)); + report->print("\n"); + + for (size_t index1 = 0; index1 < axis1_->size(); index1++) + report->print("%s ", table_unit->asString(tableValue(index1),digits)); + report->print("\n"); +} + +//////////////////////////////////////////////////////////////// + +Table2::Table2(FloatTable *values, + TableAxis *axis1, + bool own_axis1, + TableAxis *axis2, + bool own_axis2) : + Table(), + values_(values), + axis1_(axis1), + own_axis1_(own_axis1), + axis2_(axis2), + own_axis2_(own_axis2) +{ +} + +Table2::~Table2() +{ + values_->deleteContents(); + delete values_; + if (own_axis1_) + delete axis1_; + if (own_axis2_) + delete axis2_; +} + +float +Table2::tableValue(size_t index1, + size_t index2) const +{ + FloatSeq *row = (*values_)[index1]; + return (*row)[index2]; +} + +// Bilinear Interpolation. +float +Table2::findValue(float value1, + float value2, + float) const +{ + size_t size1 = axis1_->size(); + size_t size2 = axis2_->size(); + if (size1 == 1) { + if (size2 == 1) + return tableValue(0, 0); + else { + size_t index2 = axis2_->findAxisIndex(value2); + float x2 = value2; + float y00 = tableValue(0, index2); + float x2l = axis2_->axisValue(index2); + float x2u = axis2_->axisValue(index2 + 1); + float dx2 = (x2 - x2l) / (x2u - x2l); + float y01 = tableValue(0, index2 + 1); + float tbl_value + = (1 - dx2) * y00 + + dx2 * y01; + return tbl_value; + } + } + else if (size2 == 1) { + size_t index1 = axis1_->findAxisIndex(value1); + float x1 = value1; + float y00 = tableValue(index1, 0); + float x1l = axis1_->axisValue(index1); + float x1u = axis1_->axisValue(index1 + 1); + float dx1 = (x1 - x1l) / (x1u - x1l); + float y10 = tableValue(index1 + 1, 0); + float tbl_value + = (1 - dx1) * y00 + + dx1 * y10; + return tbl_value; + } + else { + size_t index1 = axis1_->findAxisIndex(value1); + size_t index2 = axis2_->findAxisIndex(value2); + float x1 = value1; + float x2 = value2; + float y00 = tableValue(index1, index2); + float x1l = axis1_->axisValue(index1); + float x1u = axis1_->axisValue(index1 + 1); + float dx1 = (x1 - x1l) / (x1u - x1l); + float y10 = tableValue(index1 + 1, index2); + float y11 = tableValue(index1 + 1, index2 + 1); + float x2l = axis2_->axisValue(index2); + float x2u = axis2_->axisValue(index2 + 1); + float dx2 = (x2 - x2l) / (x2u - x2l); + float y01 = tableValue(index1, index2 + 1); + float tbl_value + = (1 - dx1) * (1 - dx2) * y00 + + dx1 * (1 - dx2) * y10 + + dx1 * dx2 * y11 + + (1 - dx1) * dx2 * y01; + return tbl_value; + } +} + +void +Table2::reportValue(const char *result_name, + const LibertyLibrary *library, + const LibertyCell *, + const Pvt *, + float value1, + const char *comment1, + float value2, + float value3, + int digits, + string *result) const +{ + TableAxisVariable var1 = axis1_->variable(); + TableAxisVariable var2 = axis2_->variable(); + const Units *units = library->units(); + const Unit *table_unit = units->timeUnit(); + const Unit *unit1 = tableVariableUnit(var1, units); + const Unit *unit2 = tableVariableUnit(var2, units); + *result += "------- "; + *result += tableVariableString(var1), + *result += " = "; + *result += unit1->asString(value1, digits); + if (comment1) + *result += comment1; + *result += '\n'; + + *result += "| "; + *result += tableVariableString(var2); + *result += " = "; + *result += unit2->asString(value2, digits); + *result += '\n'; + + size_t index1 = axis1_->findAxisIndex(value1); + size_t index2 = axis2_->findAxisIndex(value2); + *result += "| "; + *result += unit2->asString(axis2_->axisValue(index2), digits); + if (axis2_->size() != 1) { + *result += " "; + *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(tableValue(index1, index2), digits); + if (axis2_->size() != 1) { + *result += " "; + *result += table_unit->asString(tableValue(index1, index2 + 1), digits); + } + *result += '\n'; + + if (axis1_->size() != 1) { + *result += unit1->asString(axis1_->axisValue(index1 + 1), digits); + *result += " | "; + *result += table_unit->asString(tableValue(index1 + 1, index2), digits); + if (axis2_->size() != 1) { + *result += " "; + *result +=table_unit->asString(tableValue(index1 + 1, index2 + 1),digits); + } + } + *result += '\n'; + + *result += result_name; + *result += " = "; + *result += table_unit->asString(findValue(value1, value2, value3), digits); + *result += '\n'; +} + +void +Table2::report(const Units *units, + Report *report) const +{ + int digits = 4; + const Unit *table_unit = units->timeUnit(); + const Unit *unit1 = tableVariableUnit(axis1_->variable(), units); + const Unit *unit2 = tableVariableUnit(axis2_->variable(), units); + report->print("%s\n", tableVariableString(axis2_->variable())); + report->print(" ------------------------------\n"); + report->print(" "); + for (size_t index2 = 0; index2 < axis2_->size(); index2++) + report->print("%s ", unit2->asString(axis2_->axisValue(index2), digits)); + report->print("\n"); + + for (size_t index1 = 0; index1 < axis1_->size(); index1++) { + report->print("%s |", unit1->asString(axis1_->axisValue(index1), digits)); + for (size_t index2 = 0; index2 < axis2_->size(); index2++) + report->print("%s ", table_unit->asString(tableValue(index1, index2), + digits)); + report->print("\n"); + } +} + +//////////////////////////////////////////////////////////////// + +Table3::Table3(FloatTable *values, + TableAxis *axis1, + bool own_axis1, + TableAxis *axis2, + bool own_axis2, + TableAxis *axis3, + bool own_axis3) : + Table2(values, axis1, own_axis1, axis2, own_axis2), + axis3_(axis3), + own_axis3_(own_axis3) +{ +} + +Table3::~Table3() +{ + if (own_axis3_) + delete axis3_; +} + +float +Table3::tableValue(size_t index1, + size_t index2, + size_t index3) const +{ + size_t row = index1 * axis2_->size() + index2; + return values_->operator[](row)->operator[](index3); +} + +// Bilinear Interpolation. +float +Table3::findValue(float value1, + float value2, + float value3) const +{ + size_t index1 = axis1_->findAxisIndex(value1); + size_t index2 = axis2_->findAxisIndex(value2); + size_t index3 = axis3_->findAxisIndex(value3); + float x1 = value1; + float x2 = value2; + float x3 = value3; + float dx1 = 0.0; + float dx2 = 0.0; + float dx3 = 0.0; + float y000 = tableValue(index1, index2, index3); + float y001 = 0.0; + float y010 = 0.0; + float y011 = 0.0; + float y100 = 0.0; + float y101 = 0.0; + float y110 = 0.0; + float y111 = 0.0; + + if (axis1_->size() != 1) { + float x1l = axis1_->axisValue(index1); + float x1u = axis1_->axisValue(index1 + 1); + dx1 = (x1 - x1l) / (x1u - x1l); + y100 = tableValue(index1 + 1, index2, index3); + if (axis3_->size() != 1) + y101 = tableValue(index1 + 1, index2, index3 + 1); + if (axis2_->size() != 1) { + y110 = tableValue(index1 + 1, index2 + 1, index3); + if (axis3_->size() != 1) + y111 = tableValue(index1 + 1, index2 + 1, index3 + 1); + } + } + if (axis2_->size() != 1) { + float x2l = axis2_->axisValue(index2); + float x2u = axis2_->axisValue(index2 + 1); + dx2 = (x2 - x2l) / (x2u - x2l); + y010 = tableValue(index1, index2 + 1, index3); + if (axis3_->size() != 1) + y011 = tableValue(index1, index2 + 1, index3 + 1); + } + if (axis3_->size() != 1) { + float x3l = axis3_->axisValue(index3); + float x3u = axis3_->axisValue(index3 + 1); + dx3 = (x3 - x3l) / (x3u - x3l); + y001 = tableValue(index1, index2, index3 + 1); + } + + float 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 +void +Table3::reportValue(const char *result_name, + const LibertyLibrary *library, + const LibertyCell *, + const Pvt *, + float value1, + const char *comment1, + float value2, + float value3, + int digits, + string *result) const +{ + TableAxisVariable var1 = axis1_->variable(); + TableAxisVariable var2 = axis2_->variable(); + TableAxisVariable var3 = axis3_->variable(); + const Units *units = library->units(); + const Unit *table_unit = units->timeUnit(); + const Unit *unit1 = tableVariableUnit(var1, units); + const Unit *unit2 = tableVariableUnit(var2, units); + const Unit *unit3 = tableVariableUnit(var3, units); + + *result += " --------- "; + *result += tableVariableString(var1), + *result += " = "; + *result += unit1->asString(value1, digits); + if (comment1) + *result += comment1; + *result += '\n'; + + *result += " | ---- "; + *result += tableVariableString(var2), + *result += " = "; + *result += unit2->asString(value2, digits); + *result += '\n'; + + *result += " | | "; + *result += tableVariableString(var3); + *result += " = "; + *result += unit3->asString(value3, digits); + *result += '\n'; + + size_t index1 = axis1_->findAxisIndex(value1); + size_t index2 = axis2_->findAxisIndex(value2); + size_t index3 = axis3_->findAxisIndex(value3); + + *result += " | | "; + *result += unit3->asString(axis3_->axisValue(index3), digits); + if (axis3_->size() != 1) { + *result += " "; + *result += unit3->asString(axis3_->axisValue(index3 + 1), digits); + } + *result += '\n'; + + *result += " v | --------------------\n"; + + if (axis1_->size() != 1) { + *result += " "; + *result += unit1->asString(axis1_->axisValue(index1+1), digits); + *result += " v / "; + *result += table_unit->asString(tableValue(index1+1,index2,index3), + digits); + if (axis3_->size() != 1) { + *result += " "; + *result += table_unit->asString(tableValue(index1+1,index2,index3+1), + digits); + } + } + else { + appendSpaces(result, digits+3); + *result += " v / "; + } + *result += '\n'; + + *result += unit1->asString(axis1_->axisValue(index1), digits); + *result += " "; + *result += unit2->asString(axis2_->axisValue(index2), digits); + *result += " | "; + *result += table_unit->asString(tableValue(index1, index2, index3), digits); + if (axis3_->size() != 1) { + *result += " "; + *result += table_unit->asString(tableValue(index1, index2, index3+1), + digits); + } + *result += '\n'; + + *result += " |/ "; + if (axis1_->size() != 1 + && axis2_->size() != 1) { + *result += table_unit->asString(tableValue(index1+1,index2+1,index3), + digits); + if (axis3_->size() != 1) { + *result += " "; + *result +=table_unit->asString(tableValue(index1+1,index2+1,index3+1), + digits); + } + } + *result += '\n'; + + *result += " "; + *result += unit2->asString(axis2_->axisValue(index2 + 1), digits); + *result += " | "; + if (axis2_->size() != 1) { + *result += table_unit->asString(tableValue(index1, index2+1, index3), + digits); + if (axis3_->size() != 1) { + *result += " "; + *result +=table_unit->asString(tableValue(index1, index2+1,index3+1), + digits); + } + } + *result += '\n'; + + *result += result_name; + *result += " = "; + *result += table_unit->asString(findValue(value1, value2, value3), digits); + *result += '\n'; +} + +static void +appendSpaces(string *result, + int count) +{ + while (count--) + *result += ' '; +} + +void +Table3::report(const Units *units, + Report *report) const +{ + int digits = 4; + const Unit *table_unit = units->timeUnit(); + const Unit *unit1 = tableVariableUnit(axis1_->variable(), units); + const Unit *unit2 = tableVariableUnit(axis2_->variable(), units); + const Unit *unit3 = tableVariableUnit(axis3_->variable(), units); + for (size_t index1 = 0; index1 < axis1_->size(); index1++) { + report->print("%s %s\n", tableVariableString(axis1_->variable()), + unit1->asString(axis1_->axisValue(index1), digits)); + + report->print("%s\n", tableVariableString(axis3_->variable())); + report->print(" ------------------------------\n"); + report->print(" "); + for (size_t index3 = 0; index3 < axis3_->size(); index3++) + report->print("%s ", unit3->asString(axis3_->axisValue(index3), digits)); + report->print("\n"); + + for (size_t index2 = 0; index2 < axis2_->size(); index2++) { + report->print("%s |", unit2->asString(axis2_->axisValue(index2),digits)); + for (size_t index3 = 0; index3 < axis3_->size(); index3++) + report->print("%s ", table_unit->asString(tableValue(index1, index2, + index3), + digits)); + report->print("\n"); + } + } +} + +//////////////////////////////////////////////////////////////// + +TableAxis::TableAxis(TableAxisVariable variable, + FloatSeq *values) : + variable_(variable), + values_(values) +{ +} + +TableAxis::~TableAxis() +{ + delete values_; +} + +// Bisection search. +size_t +TableAxis::findAxisIndex(float value) const +{ + int max = static_cast(values_->size()) - 1; + if (value <= (*values_)[0] || max == 0) + return 0; + else if (value >= (*values_)[max]) + // Return max-1 for value too large so interpolation pts are index,index+1. + return max - 1; + else { + int lower = -1; + int upper = max + 1; + bool ascend = ((*values_)[max] >= (*values_)[0]); + while (upper - lower > 1) { + int mid = (upper + lower) >> 1; + if ((value >= (*values_)[mid]) == ascend) + lower = mid; + else + upper = mid; + } + return lower; + } +} + +//////////////////////////////////////////////////////////////// + +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) +{ + return table_axis_variable_map.find(variable, TableAxisVariable::unknown); +} + +const char * +tableVariableString(TableAxisVariable variable) +{ + return table_axis_variable_map.find(variable); +} + +const Unit * +tableVariableUnit(TableAxisVariable variable, + 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(); + } + // Prevent warnings from lame compilers. + return nullptr; +} + +} // namespace diff --git a/liberty/TableModel.hh b/liberty/TableModel.hh new file mode 100644 index 0000000..764f1f0 --- /dev/null +++ b/liberty/TableModel.hh @@ -0,0 +1,461 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_TABLE_MODEL_H +#define STA_TABLE_MODEL_H + +#include +#include "DisallowCopyAssign.hh" +#include "MinMax.hh" +#include "Vector.hh" +#include "Transition.hh" +#include "LibertyClass.hh" +#include "TimingModel.hh" + +namespace sta { + +using std::string; + +class Unit; +class Units; +class Report; +class Table; + +TableAxisVariable +stringTableAxisVariable(const char *variable); +const char * +tableVariableString(TableAxisVariable variable); +const Unit * +tableVariableUnit(TableAxisVariable variable, + const Units *units); + +class GateTableModel : public GateTimingModel +{ +public: + GateTableModel(TableModel *delay_model, + TableModel *delay_sigma_models[EarlyLate::index_count], + TableModel *slew_model, + TableModel *slew_sigma_models[EarlyLate::index_count]); + virtual ~GateTableModel(); + virtual void gateDelay(const LibertyCell *cell, + const Pvt *pvt, + float in_slew, + float load_cap, + float related_out_cap, + bool pocv_enabled, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) const; + virtual void reportGateDelay(const LibertyCell *cell, + const Pvt *pvt, + float in_slew, + float load_cap, + float related_out_cap, + bool pocv_enabled, + int digits, + string *result) const; + virtual float driveResistance(const LibertyCell *cell, + const Pvt *pvt) const; + + const TableModel *delayModel() const { return delay_model_; } + const TableModel *slewModel() const { return slew_model_; } + // Check the axes before making the model. + // Return true if the model axes are supported. + static bool checkAxes(const Table *table); + +protected: + void maxCapSlew(const LibertyCell *cell, + float in_slew, + const Pvt *pvt, + float &slew, + float &cap) const; + virtual void setIsScaled(bool is_scaled); + float axisValue(TableAxis *axis, + float load_cap, + float in_slew, + float related_out_cap) const; + float findValue(const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + const TableModel *model, + float in_slew, + float load_cap, + float related_out_cap) const; + void reportTableLookup(const char *result_name, + const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + const TableModel *model, + float in_slew, + float load_cap, + float related_out_cap, + int digits, + string *result) 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; + static bool checkAxis(TableAxis *axis); + + TableModel *delay_model_; + TableModel *delay_sigma_models_[EarlyLate::index_count]; + TableModel *slew_model_; + TableModel *slew_sigma_models_[EarlyLate::index_count]; + +private: + DISALLOW_COPY_AND_ASSIGN(GateTableModel); +}; + +class CheckTableModel : public CheckTimingModel +{ +public: + explicit CheckTableModel(TableModel *model, + TableModel *sigma_models[EarlyLate::index_count]); + virtual ~CheckTableModel(); + virtual void checkDelay(const LibertyCell *cell, + const Pvt *pvt, + float from_slew, + float to_slew, + float related_out_cap, + bool pocv_enabled, + // Return values. + ArcDelay &margin) const; + virtual void reportCheckDelay(const LibertyCell *cell, + const Pvt *pvt, + float from_slew, + const char *from_slew_annotation, + float to_slew, + float related_out_cap, + bool pocv_enabled, + int digits, + string *result) const; + + // Check the axes before making the model. + // Return true if the model axes are supported. + static bool checkAxes(const Table *table); + +protected: + virtual void setIsScaled(bool is_scaled); + float findValue(const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + 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 axisValue(TableAxis *axis, + float load_cap, + float in_slew, + float related_out_cap) const; + void reportTableDelay(const char *result_name, + const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + const TableModel *model, + float from_slew, + const char *from_slew_annotation, + float to_slew, + float related_out_cap, + int digits, + string *result) const; + static bool checkAxis(TableAxis *axis); + + TableModel *model_; + TableModel *sigma_models_[EarlyLate::index_count]; + +private: + DISALLOW_COPY_AND_ASSIGN(CheckTableModel); +}; + +// Wrapper class for Table to apply scale factors. +class TableModel +{ +public: + TableModel(Table *table, + ScaleFactorType scale_factor_type, + TransRiseFall *tr); + ~TableModel(); + void setScaleFactorType(ScaleFactorType type); + int order() const; + TableAxis *axis1() const; + TableAxis *axis2() const; + TableAxis *axis3() const; + void setIsScaled(bool is_scaled); + // Table interpolated lookup. + float findValue(float value1, + float value2, + float value3) const; + // Table interpolated lookup with scale factor. + float findValue(const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + float value1, + float value2, + float value3) const; + void reportValue(const char *result_name, + const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + float value1, + const char *comment1, + float value2, + float value3, + int digits, + string *result) const; + void report(const Units *units, + Report *report) const; + +protected: + float scaleFactor(const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt) const; + void reportPvtScaleFactor(const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + int digits, + string *result) const; + + Table *table_; + // ScaleFactorType gcc barfs if this is dcl'd. + unsigned scale_factor_type_:scale_factor_bits; + unsigned tr_index_:TransRiseFall::index_bit_count; + bool is_scaled_:1; + +private: + DISALLOW_COPY_AND_ASSIGN(TableModel); +}; + +// Abstract base class for tables. +class Table +{ +public: + Table() {} + virtual ~Table() {} + void setScaleFactorType(ScaleFactorType type); + virtual int order() const = 0; + virtual TableAxis *axis1() const { return nullptr; } + virtual TableAxis *axis2() const { return nullptr; } + virtual TableAxis *axis3() const { return nullptr; } + void setIsScaled(bool is_scaled); + // Table interpolated lookup. + virtual float findValue(float value1, + float value2, + float value3) const = 0; + // Table interpolated lookup with scale factor. + float findValue(const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + float value1, + float value2, + float value3) const; + virtual void reportValue(const char *result_name, + const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + float value1, + const char *comment1, + float value2, + float value3, + int digits, + string *result) const = 0; + virtual void report(const Units *units, + Report *report) const = 0; + +private: + DISALLOW_COPY_AND_ASSIGN(Table); +}; + +// Zero dimension (scalar) table. +class Table0 : public Table +{ +public: + Table0(float value); + virtual int order() const { return 0; } + virtual float findValue(float value1, + float value2, + float value3) const; + virtual void reportValue(const char *result_name, + const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + float value1, + const char *comment1, + float value2, + float value3, + int digits, + string *result) const; + virtual void report(const Units *units, + Report *report) const; + using Table::findValue; + +private: + DISALLOW_COPY_AND_ASSIGN(Table0); + float value_; +}; + +// One dimensional table. +class Table1 : public Table +{ +public: + Table1(FloatSeq *values, + TableAxis *axis1, + bool own_axis1); + virtual ~Table1(); + virtual int order() const { return 1; } + virtual TableAxis *axis1() const { return axis1_; } + float tableValue(size_t index1) const; + virtual float findValue(float value1, + float value2, + float value3) const; + virtual void reportValue(const char *result_name, + const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + float value1, + const char *comment1, + float value2, + float value3, + int digits, + string *result) const; + virtual void report(const Units *units, + Report *report) const; + using Table::findValue; + +private: + DISALLOW_COPY_AND_ASSIGN(Table1); + + FloatSeq *values_; + TableAxis *axis1_; + bool own_axis1_; +}; + +// Two dimensional table. +class Table2 : public Table +{ +public: + Table2(FloatTable *values, + TableAxis *axis1, + bool own_axis1, + TableAxis *axis2, + bool own_axis2); + virtual ~Table2(); + virtual int order() const { return 2; } + TableAxis *axis1() const { return axis1_; } + TableAxis *axis2() const { return axis2_; } + float tableValue(size_t index1, + size_t index2) const; + virtual float findValue(float value1, + float value2, + float value3) const; + virtual void reportValue(const char *result_name, + const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + float value1, + const char *comment1, + float value2, + float value3, + int digits, + string *result) const; + virtual void report(const Units *units, + Report *report) const; + using Table::findValue; + +protected: + DISALLOW_COPY_AND_ASSIGN(Table2); + + FloatTable *values_; + // Row. + TableAxis *axis1_; + bool own_axis1_; + // Column. + TableAxis *axis2_; + bool own_axis2_; +}; + +// Three dimensional table. +class Table3 : public Table2 +{ +public: + Table3(FloatTable *values, + TableAxis *axis1, + bool own_axis1, + TableAxis *axis2, + bool own_axis2, + TableAxis *axis3, + bool own_axis3); + virtual ~Table3(); + virtual int order() const { return 3; } + TableAxis *axis3() const { return axis3_; } + float tableValue(size_t index1, + size_t index2, + size_t index3) const; + virtual float findValue(float value1, + float value2, + float value3) const; + virtual void reportValue(const char *result_name, + const LibertyLibrary *library, + const LibertyCell *cell, + const Pvt *pvt, + float value1, + const char *comment1, + float value2, + float value3, + int digits, + string *result) const; + virtual void report(const Units *units, + Report *report) const; + using Table::findValue; + +private: + DISALLOW_COPY_AND_ASSIGN(Table3); + + TableAxis *axis3_; + bool own_axis3_; +}; + +class TableAxis +{ +public: + TableAxis(TableAxisVariable variable, + FloatSeq *values); + ~TableAxis(); + TableAxisVariable variable() const { return variable_; } + size_t size() const { return values_->size(); } + 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; + +private: + DISALLOW_COPY_AND_ASSIGN(TableAxis); + + TableAxisVariable variable_; + FloatSeq *values_; +}; + +} // namespace +#endif diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc new file mode 100644 index 0000000..9e61ad0 --- /dev/null +++ b/liberty/TimingArc.cc @@ -0,0 +1,767 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "EnumNameMap.hh" +#include "FuncExpr.hh" +#include "TimingRole.hh" +#include "Liberty.hh" +#include "TimingModel.hh" +#include "TimingArc.hh" + +namespace sta { + +static bool +timingArcsEquiv(const TimingArcSet *set1, + const TimingArcSet *set2); +static bool +timingArcsLess(const TimingArcSet *set1, + const TimingArcSet *set2); + +//////////////////////////////////////////////////////////////// + +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} +{ +} + +// Destructor does NOT delete contents because it is a component +// of TimingGroup (that is deleted after building the LibertyCell) +// and (potentially) multiple TimingArcSets. +TimingArcAttrs::~TimingArcAttrs() +{ +} + +void +TimingArcAttrs::deleteContents() +{ + if (cond_) + cond_->deleteSubexprs(); + stringDelete(sdf_cond_); + stringDelete(sdf_cond_start_); + stringDelete(sdf_cond_end_); + stringDelete(mode_name_); + stringDelete(mode_value_); + delete models_[TransRiseFall::riseIndex()]; + delete models_[TransRiseFall::fallIndex()]; +} + +void +TimingArcAttrs::setTimingType(TimingType type) +{ + timing_type_ = type; +} + +void +TimingArcAttrs::setTimingSense(TimingSense sense) +{ + timing_sense_ = sense; +} + +void +TimingArcAttrs::setSdfCond(const char *cond) +{ + stringDelete(sdf_cond_); + sdf_cond_ = stringCopy(cond); +} + +void +TimingArcAttrs::setSdfCondStart(const char *cond) +{ + stringDelete(sdf_cond_start_); + sdf_cond_start_ = stringCopy(cond); +} + +void +TimingArcAttrs::setSdfCondEnd(const char *cond) +{ + stringDelete(sdf_cond_end_); + sdf_cond_end_ = stringCopy(cond); +} + +void +TimingArcAttrs::setModeName(const char *name) +{ + stringDelete(mode_name_); + mode_name_ = stringCopy(name); +} + +void +TimingArcAttrs::setModeValue(const char *value) +{ + stringDelete(mode_value_); + mode_value_ = stringCopy(value); +} + +TimingModel * +TimingArcAttrs::model(TransRiseFall *tr) const +{ + return models_[tr->index()]; +} + +void +TimingArcAttrs::setModel(TransRiseFall *tr, + TimingModel *model) +{ + models_[tr->index()] = model; +} + +void +TimingArcAttrs::setOcvArcDepth(float depth) +{ + ocv_arc_depth_ = depth; +} + +//////////////////////////////////////////////////////////////// + +TimingArcSet *TimingArcSet::wire_timing_arc_set_ = nullptr; + +TimingArcSet::TimingArcSet(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + TimingRole *role, + TimingArcAttrs *attrs) : + from_(from), + to_(to), + related_out_(related_out), + role_(role), + cond_(attrs->cond()), + is_cond_default_(false), + sdf_cond_start_(attrs->sdfCondStart()), + sdf_cond_end_(attrs->sdfCondEnd()), + mode_name_(attrs->modeName()), + mode_value_(attrs->modeValue()), + ocv_arc_depth_(attrs->ocvArcDepth()), + index_(0), + is_disabled_constraint_(false) +{ + const char *sdf_cond = attrs->sdfCond(); + if (sdf_cond) + sdf_cond_start_ = sdf_cond_end_ = sdf_cond; + + init(cell); +} + +TimingArcSet::TimingArcSet(TimingRole *role) : + from_(nullptr), + to_(nullptr), + related_out_(nullptr), + role_(role), + cond_(nullptr), + is_cond_default_(false), + sdf_cond_start_(nullptr), + sdf_cond_end_(nullptr), + mode_name_(nullptr), + mode_value_(nullptr), + index_(0), + is_disabled_constraint_(false) +{ + init(nullptr); +} + +void +TimingArcSet::init(LibertyCell *cell) +{ + if (cell) + index_ = cell->addTimingArcSet(this); + + for (auto tr_index : TransRiseFall::rangeIndex()) { + from_arc1_[tr_index] = nullptr; + from_arc2_[tr_index] = nullptr; + } +} + +TimingArcSet::~TimingArcSet() +{ + arcs_.deleteContents(); +} + +LibertyCell * +TimingArcSet::libertyCell() const +{ + if (from_) + return from_->libertyCell(); + else + // Wire timing arc set. + return nullptr; +} + +TimingArcSetArcIterator * +TimingArcSet::timingArcIterator() +{ + return new TimingArcSetArcIterator(this); +} + +TimingArcIndex +TimingArcSet::addTimingArc(TimingArc *arc) +{ + TimingArcIndex arc_index = arcs_.size(); + if (arc_index > timing_arc_index_max) + internalError("timing arc max index exceeded\n"); + arcs_.push_back(arc); + + int from_tr_index = arc->fromTrans()->asRiseFall()->index(); + if (from_arc1_[from_tr_index] == nullptr) + from_arc1_[from_tr_index] = arc; + else if (from_arc2_[from_tr_index] == nullptr) + from_arc2_[from_tr_index] = arc; + + return arc_index; +} + +void +TimingArcSet::deleteTimingArc(TimingArc *arc) +{ + TimingArc *last_arc = arcs_.back(); + if (arc == last_arc) + arcs_.pop_back(); + else { + last_arc->setIndex(arc->index()); + arcs_[arc->index()] = last_arc; + arcs_.pop_back(); + } + int from_tr_index = arc->fromTrans()->asRiseFall()->index(); + if (from_arc1_[from_tr_index] == arc) { + from_arc1_[from_tr_index] = from_arc2_[from_tr_index]; + from_arc2_[from_tr_index] = nullptr; + } + else if (from_arc2_[from_tr_index] == arc) + from_arc2_[from_tr_index] = nullptr; + delete arc; +} + +TimingArc * +TimingArcSet::findTimingArc(unsigned arc_index) +{ + return arcs_[arc_index]; +} + +void +TimingArcSet::setRole(TimingRole *role) +{ + role_ = role; +} + +void +TimingArcSet::setIsCondDefault(bool is_default) +{ + is_cond_default_ = is_default; +} + +void +TimingArcSet::arcsFrom(const TransRiseFall *from_tr, + // Return values. + TimingArc *&arc1, + TimingArc *&arc2) +{ + int tr_index = from_tr->index(); + arc1 = from_arc1_[tr_index]; + arc2 = from_arc2_[tr_index]; +} + +TimingSense +TimingArcSet::sense() const +{ + if (arcs_.size() == 1) + return arcs_[0]->sense(); + else if (arcs_.size() == 2 && arcs_[0]->sense() == arcs_[1]->sense()) + return arcs_[0]->sense(); + else + return TimingSense::non_unate; +} + +TransRiseFall * +TimingArcSet::isRisingFallingEdge() const +{ + int arc_count = arcs_.size(); + if (arc_count == 2) { + TransRiseFall *from_tr1 = arcs_[0]->fromTrans()->asRiseFall(); + TransRiseFall *from_tr2 = arcs_[1]->fromTrans()->asRiseFall(); + if (from_tr1 == from_tr2) + return from_tr1; + } + if (arcs_.size() == 1) + return arcs_[0]->fromTrans()->asRiseFall(); + else + return nullptr; +} + +void +TimingArcSet::setIsDisabledConstraint(bool is_disabled) +{ + is_disabled_constraint_ = is_disabled; +} + +float +TimingArcSet::ocvArcDepth() const +{ + if (from_) { + if (ocv_arc_depth_ != 0.0) + return ocv_arc_depth_; + else { + LibertyCell *cell = from_->libertyCell(); + float depth = cell->ocvArcDepth(); + if (depth != 0.0) + return depth; + else { + float depth = cell->libertyLibrary()->ocvArcDepth(); + if (depth != 0.0) + return depth; + } + } + } + // Wire timing arc set. + return 1.0; +} + +bool +TimingArcSet::equiv(const TimingArcSet *set1, + 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()) + && timingArcsEquiv(set1, set2); +} + +static bool +timingArcsEquiv(const TimingArcSet *set1, + const TimingArcSet *set2) +{ + TimingArcSetArcIterator arc_iter1(set1); + TimingArcSetArcIterator arc_iter2(set2); + while (arc_iter1.hasNext() && arc_iter2.hasNext()) { + TimingArc *arc1 = arc_iter1.next(); + TimingArc *arc2 = arc_iter2.next(); + if (!TimingArc::equiv(arc1, arc2)) + return false; + } + return !arc_iter1.hasNext() && !arc_iter2.hasNext(); +} + +bool +TimingArcSet::less(const TimingArcSet *set1, + const TimingArcSet *set2) +{ + return timingArcSetLess(set1, set2); +} + +bool +timingArcSetLess(const TimingArcSet *set1, + const TimingArcSet *set2) +{ + LibertyPort *from1 = set1->from(); + LibertyPort *from2 = set2->from(); + if (LibertyPort::equiv(from1, from2)) { + LibertyPort *to1 = set1->to(); + LibertyPort *to2 = set2->to(); + if (LibertyPort::equiv(to1, to2)) { + TimingRole *role1 = set1->role(); + 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); + } + else + return TimingRole::less(role1, role2); + } + else + return LibertyPort::less(to1, to2); + } + else + return LibertyPort::less(from1, from2); +} + +static bool +timingArcsLess(const TimingArcSet *set1, + const TimingArcSet *set2) +{ + TimingArcSetArcIterator arc_iter1(set1); + TimingArcSetArcIterator arc_iter2(set2); + while (arc_iter1.hasNext() && arc_iter2.hasNext()) { + TimingArc *arc1 = arc_iter1.next(); + TimingArc *arc2 = arc_iter2.next(); + int from_index1 = arc1->fromTrans()->index(); + int from_index2 = arc2->fromTrans()->index(); + if (from_index1 < from_index2) + return true; + if (from_index1 > from_index2) + return false; + // from_index1 == from_index2 + int to_index1 = arc1->toTrans()->index(); + int to_index2 = arc2->toTrans()->index(); + if (to_index1 < to_index2) + return true; + if (to_index1 > to_index2) + return false; + // Continue if arc transitions are equal. + } + return !arc_iter1.hasNext() && arc_iter2.hasNext(); +} + +//////////////////////////////////////////////////////////////// + +int +TimingArcSet::wireArcIndex(const TransRiseFall *tr) +{ + return tr->index(); +} + +void +TimingArcSet::init() +{ + wire_timing_arc_set_ = new TimingArcSet(TimingRole::wire()); + new TimingArc(wire_timing_arc_set_, Transition::rise(), + Transition::rise(), nullptr); + new TimingArc(wire_timing_arc_set_, Transition::fall(), + Transition::fall(), nullptr); +} + +void +TimingArcSet::destroy() +{ + delete wire_timing_arc_set_; + wire_timing_arc_set_ = nullptr; +} + +//////////////////////////////////////////////////////////////// + +TimingArcSetArcIterator::TimingArcSetArcIterator(const TimingArcSet *set) : + TimingArcSeq::ConstIterator(set->arcs()) +{ +} + +//////////////////////////////////////////////////////////////// + +TimingArc::TimingArc(TimingArcSet *set, + Transition *from_tr, + Transition *to_tr, + TimingModel *model) : + set_(set), + from_tr_(from_tr), + to_tr_(to_tr), + model_(model), + scaled_models_(nullptr) +{ + index_ = set->addTimingArc(this); +} + +TimingArc::~TimingArc() +{ + // The models referenced by scaled_models_ are owned by the scaled + // cells and are deleted by ~LibertyCell. + delete scaled_models_; +} + +TimingModel * +TimingArc::model(const OperatingConditions *op_cond) const +{ + if (scaled_models_) { + TimingModel *model = scaled_models_->findKey(op_cond); + if (model) + return model; + else + return model_; + } + else + return model_; +} + +void +TimingArc::addScaledModel(const OperatingConditions *op_cond, + TimingModel *scaled_model) +{ + if (scaled_models_ == nullptr) + scaled_models_ = new ScaledTimingModelMap; + (*scaled_models_)[op_cond] = scaled_model; +} + +bool +TimingArc::equiv(const TimingArc *arc1, + const TimingArc *arc2) +{ + return arc1->fromTrans() == arc2->fromTrans() + && arc1->toTrans() == arc2->toTrans(); +} + +void +TimingArc::setIndex(unsigned index) +{ + index_ = index; +} + +TimingArc * +TimingArc::cornerArc(int ap_index) +{ + if (ap_index < static_cast(corner_arcs_.size())) { + TimingArc *corner_arc = corner_arcs_[ap_index]; + if (corner_arc) + return corner_arc; + } + return this; +} + +void +TimingArc::setCornerArc(TimingArc *corner_arc, + int ap_index) +{ + if (ap_index >= static_cast(corner_arcs_.size())) + corner_arcs_.resize(ap_index + 1); + corner_arcs_[ap_index] = corner_arc; +} + +//////////////////////////////////////////////////////////////// + +TimingSense +TimingArc::sense() const +{ + if ((from_tr_ == Transition::rise() + && to_tr_ == Transition::rise()) + || (from_tr_ == Transition::fall() + && to_tr_ == Transition::fall())) + return TimingSense::positive_unate; + else if ((from_tr_ == Transition::rise() + && to_tr_ == Transition::fall()) + || (from_tr_ == Transition::fall() + && to_tr_ == Transition::rise())) + return TimingSense::negative_unate; + else + return TimingSense::non_unate; +} + +static EnumNameMap timing_sense_name_map = + {{TimingSense::positive_unate, "positive_unate"}, + {TimingSense::negative_unate, "negative_unate"}, + {TimingSense::non_unate, "non_unate"}, + {TimingSense::none, "none"}, + {TimingSense::unknown, "unknown"} + }; + +const char * +timingSenseString(TimingSense sense) +{ + return timing_sense_name_map.find(sense); +} + +TimingSense +timingSenseOpposite(TimingSense sense) +{ + switch (sense) { + case TimingSense::positive_unate: + return TimingSense::negative_unate; + case TimingSense::negative_unate: + return TimingSense::positive_unate; + case TimingSense::non_unate: + return TimingSense::non_unate; + case TimingSense::unknown: + return TimingSense::unknown; + case TimingSense::none: + return TimingSense::none; + } + // Prevent warnings from lame compilers. + return TimingSense::unknown; +} + +//////////////////////////////////////////////////////////////// + +// Same order as enum TimingType. +EnumNameMap timing_type_name_map = + {{TimingType::clear, "clear"}, + {TimingType::combinational, "combinational"}, + {TimingType::combinational_fall, "combinational_fall"}, + {TimingType::combinational_rise, "combinational_rise"}, + {TimingType::falling_edge, "falling_edge"}, + {TimingType::hold_falling, "hold_falling"}, + {TimingType::hold_rising, "hold_rising"}, + {TimingType::min_pulse_width, "min_pulse_width"}, + {TimingType::minimum_period, "minimum_period"}, + {TimingType::nochange_high_high, "nochange_high_high"}, + {TimingType::nochange_high_low, "nochange_high_low"}, + {TimingType::nochange_low_high, "nochange_low_high"}, + {TimingType::nochange_low_low, "nochange_low_low"}, + {TimingType::non_seq_hold_falling, "non_seq_hold_falling"}, + {TimingType::non_seq_hold_rising, "non_seq_hold_rising"}, + {TimingType::non_seq_setup_falling, "non_seq_setup_falling"}, + {TimingType::non_seq_setup_rising, "non_seq_setup_rising"}, + {TimingType::preset, "preset"}, + {TimingType::recovery_falling, "recovery_falling"}, + {TimingType::recovery_rising, "recovery_rising"}, + {TimingType::removal_falling, "removal_falling"}, + {TimingType::removal_rising, "removal_rising"}, + {TimingType::retaining_time, "retaining_time"}, + {TimingType::rising_edge, "rising_edge"}, + {TimingType::setup_falling, "setup_falling"}, + {TimingType::setup_rising, "setup_rising"}, + {TimingType::skew_falling, "skew_falling"}, + {TimingType::skew_rising, "skew_rising"}, + {TimingType::three_state_disable, "three_state_disable"}, + {TimingType::three_state_disable_fall, "three_state_disable_fall"}, + {TimingType::three_state_disable_rise, "three_state_disable_rise"}, + {TimingType::three_state_enable, "three_state_enable"}, + {TimingType::three_state_enable_fall, "three_state_enable_fall"}, + {TimingType::three_state_enable_rise, "three_state_enable_rise"}, + {TimingType::min_clock_tree_path, "min_clock_tree_path"}, + {TimingType::max_clock_tree_path, "max_clock_tree_path"}, + {TimingType::unknown, "unknown"} + }; + +const char * +timingTypeString(TimingType type) +{ + return timing_type_name_map.find(type); +} + +TimingType +findTimingType(const char *type_name) +{ + return timing_type_name_map.find(type_name, TimingType::unknown); +} + +bool +timingTypeIsCheck(TimingType type) +{ + switch (type) { + case TimingType::hold_falling: + case TimingType::hold_rising: + case TimingType::min_pulse_width: + case TimingType::minimum_period: + case TimingType::nochange_high_high: + case TimingType::nochange_high_low: + case TimingType::nochange_low_high: + case TimingType::nochange_low_low: + case TimingType::non_seq_hold_falling: + case TimingType::non_seq_hold_rising: + case TimingType::non_seq_setup_falling: + case TimingType::non_seq_setup_rising: + case TimingType::recovery_falling: + case TimingType::recovery_rising: + case TimingType::removal_falling: + case TimingType::removal_rising: + case TimingType::retaining_time: + case TimingType::setup_falling: + case TimingType::setup_rising: + case TimingType::skew_falling: + case TimingType::skew_rising: + return true; + default: + return false; + } +} + +ScaleFactorType +timingTypeScaleFactorType(TimingType type) +{ + switch (type) { + case TimingType::non_seq_setup_falling: + case TimingType::non_seq_setup_rising: + case TimingType::setup_falling: + case TimingType::setup_rising: + return ScaleFactorType::setup; + case TimingType::hold_falling: + case TimingType::hold_rising: + case TimingType::non_seq_hold_falling: + case TimingType::non_seq_hold_rising: + return ScaleFactorType::hold; + case TimingType::recovery_falling: + case TimingType::recovery_rising: + return ScaleFactorType::recovery; + case TimingType::removal_falling: + case TimingType::removal_rising: + return ScaleFactorType::removal; + case TimingType::skew_falling: + case TimingType::skew_rising: + return ScaleFactorType::skew; + case TimingType::minimum_period: + return ScaleFactorType::min_period; + case TimingType::nochange_high_high: + case TimingType::nochange_high_low: + case TimingType::nochange_low_high: + case TimingType::nochange_low_low: + return ScaleFactorType::nochange; + case TimingType::min_pulse_width: + return ScaleFactorType::min_pulse_width; + case TimingType::clear: + case TimingType::combinational: + case TimingType::combinational_fall: + case TimingType::combinational_rise: + case TimingType::falling_edge: + case TimingType::preset: + case TimingType::retaining_time: + case TimingType::rising_edge: + case TimingType::three_state_disable: + case TimingType::three_state_disable_fall: + case TimingType::three_state_disable_rise: + case TimingType::three_state_enable: + case TimingType::three_state_enable_fall: + case TimingType::three_state_enable_rise: + case TimingType::min_clock_tree_path: + case TimingType::max_clock_tree_path: + return ScaleFactorType::cell; + case TimingType::unknown: + return ScaleFactorType::unknown; + } + return ScaleFactorType::unknown; +} + +} // namespace diff --git a/liberty/TimingArc.hh b/liberty/TimingArc.hh new file mode 100644 index 0000000..a6507e7 --- /dev/null +++ b/liberty/TimingArc.hh @@ -0,0 +1,286 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_TIMING_ARC_H +#define STA_TIMING_ARC_H + +#include "DisallowCopyAssign.hh" +#include "Vector.hh" +#include "Transition.hh" +#include "LibertyClass.hh" + +namespace sta { + +class TimingArcAttrs; +class WireTimingArc; +class WireTimingArcSetArcIterator; +class TimingArcSetArcIterator; + +typedef int TimingArcIndex; +typedef Vector TimingArcSeq; +typedef Map ScaledTimingModelMap; + +enum class TimingType { + clear, + combinational, + combinational_fall, + combinational_rise, + falling_edge, + hold_falling, + hold_rising, + min_pulse_width, + minimum_period, + nochange_high_high, + nochange_high_low, + nochange_low_high, + nochange_low_low, + non_seq_hold_falling, + non_seq_hold_rising, + non_seq_setup_falling, + non_seq_setup_rising, + preset, + recovery_falling, + recovery_rising, + removal_falling, + removal_rising, + retaining_time, + rising_edge, + setup_falling, + setup_rising, + skew_falling, + skew_rising, + three_state_disable, + three_state_disable_fall, + three_state_disable_rise, + three_state_enable, + three_state_enable_fall, + three_state_enable_rise, + min_clock_tree_path, + max_clock_tree_path, + unknown +}; + +const char * +timingTypeString(TimingType type); +TimingType +findTimingType(const char *string); +bool +timingTypeIsCheck(TimingType type); +ScaleFactorType +timingTypeScaleFactorType(TimingType type); + +//////////////////////////////////////////////////////////////// + +class TimingArcAttrs +{ +public: + TimingArcAttrs(); + virtual ~TimingArcAttrs(); + void deleteContents(); + 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_; } + FuncExpr *&condRef() { return 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); + TimingModel *model(TransRiseFall *tr) const; + void setModel(TransRiseFall *tr, + TimingModel *model); + float ocvArcDepth() const { return ocv_arc_depth_; } + void setOcvArcDepth(float depth); + +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_; + float ocv_arc_depth_; + TimingModel *models_[TransRiseFall::index_count]; + +private: + DISALLOW_COPY_AND_ASSIGN(TimingArcAttrs); +}; + +// A timing arc set is a group of related timing arcs between from/to +// a pair of cell ports. Wire timing arcs are a special set owned by +// the TimingArcSet class. +// +// See ~LibertyCell for delete of TimingArcSet members. +class TimingArcSet +{ +public: + TimingArcSet(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to, + LibertyPort *related_out, + TimingRole *role, + TimingArcAttrs *attrs); + virtual ~TimingArcSet(); + LibertyCell *libertyCell() const; + LibertyPort *from() const { return from_; } + LibertyPort *to() const { return to_; } + LibertyPort *relatedOut() const { return related_out_; } + TimingRole *role() const { return role_; }; + TimingSense sense() const; + // Rise/fall if the arc set is rising_edge or falling_edge. + TransRiseFall *isRisingFallingEdge() const; + size_t arcCount() const { return arcs_.size(); } + TimingArcSeq &arcs() { return arcs_; } + // Return 1 or 2 arcs matching from transition. + void arcsFrom(const TransRiseFall *from_tr, + // Return values. + TimingArc *&arc1, + TimingArc *&arc2); + const TimingArcSeq &arcs() const { return arcs_; } + TimingArcSetArcIterator *timingArcIterator(); + TimingArcIndex addTimingArc(TimingArc *arc); + void deleteTimingArc(TimingArc *arc); + TimingArc *findTimingArc(unsigned arc_index); + void setRole(TimingRole *role); + FuncExpr *cond() const { return cond_; } + // Cond default is the timing arcs with no condition when there are + // other conditional timing arcs between the same pins. + bool isCondDefault() const { return is_cond_default_; } + void setIsCondDefault(bool is_default); + // SDF IOPATHs match sdfCond. + // sdfCond (IOPATH) reuses sdfCondStart (timing check) variable. + const char *sdfCond() const { return sdf_cond_start_; } + // SDF timing checks match sdfCondStart/sdfCondEnd. + const char *sdfCondStart() const { return sdf_cond_start_; } + const char *sdfCondEnd() const { return sdf_cond_end_; } + const char *modeName() const { return mode_name_; } + const char *modeValue() const { return mode_value_; } + // Timing arc set index in cell. + TimingArcIndex index() const { return index_; } + bool isDisabledConstraint() const { return is_disabled_constraint_; } + void setIsDisabledConstraint(bool is_disabled); + // OCV arc depth from timing/cell/library. + float ocvArcDepth() const; + + static bool equiv(const TimingArcSet *set1, + const TimingArcSet *set2); + static bool less(const TimingArcSet *set1, + const TimingArcSet *set2); + + static void init(); + static void destroy(); + + // Psuedo definition for wire arcs. + static TimingArcSet *wireTimingArcSet() { return wire_timing_arc_set_; } + static int wireArcIndex(const TransRiseFall *tr); + static int wireArcCount() { return 2; } + +protected: + void init(LibertyCell *cell); + TimingArcSet(TimingRole *role); + + LibertyPort *from_; + LibertyPort *to_; + LibertyPort *related_out_; + TimingRole *role_; + TimingArcSeq arcs_; + FuncExpr *cond_; + bool is_cond_default_; + const char *sdf_cond_start_; + const char *sdf_cond_end_; + const char *mode_name_; + const char *mode_value_; + float ocv_arc_depth_; + unsigned index_; + bool is_disabled_constraint_; + TimingArc *from_arc1_[TransRiseFall::index_count]; + TimingArc *from_arc2_[TransRiseFall::index_count]; + + static TimingArcSet *wire_timing_arc_set_; + +private: + DISALLOW_COPY_AND_ASSIGN(TimingArcSet); +}; + +class TimingArcSetArcIterator : public TimingArcSeq::ConstIterator +{ +public: + TimingArcSetArcIterator(const TimingArcSet *set); + +private: + DISALLOW_COPY_AND_ASSIGN(TimingArcSetArcIterator); +}; + +// A timing arc is a single from/to transition between two ports. +// The timing model parameters used for delay calculation are also found here. +class TimingArc +{ +public: + TimingArc(TimingArcSet *set, + Transition *from_tr, + Transition *to_tr, + TimingModel *model); + ~TimingArc(); + LibertyPort *from() const { return set_->from(); } + LibertyPort *to() const { return set_->to(); } + Transition *fromTrans() const { return from_tr_; } + Transition *toTrans() const { return to_tr_; } + TimingRole *role() const { return set_->role(); } + TimingArcSet *set() const { return set_; } + TimingSense sense() const; + // Index in TimingArcSet. + unsigned index() const { return index_; } + TimingModel *model(const OperatingConditions *op_cond) const; + TimingModel *model() const { return model_; } + TimingArc *cornerArc(int ap_index); + void setCornerArc(TimingArc *corner_arc, + int ap_index); + + static bool equiv(const TimingArc *arc1, + const TimingArc *arc2); + +protected: + void setIndex(unsigned index); + void addScaledModel(const OperatingConditions *op_cond, + TimingModel *scaled_model); + + TimingArcSet *set_; + Transition *from_tr_; + Transition *to_tr_; + unsigned index_; + TimingModel *model_; + ScaledTimingModelMap *scaled_models_; + Vector corner_arcs_; + +private: + DISALLOW_COPY_AND_ASSIGN(TimingArc); + + friend class LibertyLibrary; + friend class LibertyCell; + friend class TimingArcSet; +}; + +} // namespace +#endif diff --git a/liberty/TimingModel.hh b/liberty/TimingModel.hh new file mode 100644 index 0000000..2ec345c --- /dev/null +++ b/liberty/TimingModel.hh @@ -0,0 +1,91 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_TIMING_MODEL_H +#define STA_TIMING_MODEL_H + +#include +#include "Delay.hh" +#include "LibertyClass.hh" + +namespace sta { + +using std::string; + +// Abstract base class for timing models. +class TimingModel +{ +public: + virtual ~TimingModel() {} + +protected: + virtual void setIsScaled(bool is_scaled) = 0; + + friend class LibertyCell; +}; + +// Abstract base class for gate timing models. +class GateTimingModel : public TimingModel +{ +public: + // Gate delay calculation. + virtual void gateDelay(const LibertyCell *cell, + const Pvt *pvt, + float in_slew, + float load_cap, + float related_out_cap, + bool pocv_enabled, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) const = 0; + virtual void reportGateDelay(const LibertyCell *cell, + const Pvt *pvt, + float in_slew, + float load_cap, + float related_out_cap, + bool pocv_enabled, + int digits, + string *result) const = 0; + virtual float driveResistance(const LibertyCell *cell, + const Pvt *pvt) const = 0; +}; + +// Abstract base class for timing check timing models. +class CheckTimingModel : public TimingModel +{ +public: + // Timing check margin delay calculation. + virtual void checkDelay(const LibertyCell *cell, + const Pvt *pvt, + float from_slew, + float to_slew, + float related_out_cap, + bool pocv_enabled, + // Return values. + ArcDelay &margin) const = 0; + virtual void reportCheckDelay(const LibertyCell *cell, + const Pvt *pvt, + float from_slew, + const char *from_slew_annotation, + float to_slew, + float related_out_cap, + bool pocv_enabled, + int digits, + string *result) const = 0; +}; + +} // namespace +#endif diff --git a/liberty/TimingRole.cc b/liberty/TimingRole.cc new file mode 100644 index 0000000..f23b5d0 --- /dev/null +++ b/liberty/TimingRole.cc @@ -0,0 +1,252 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "TimingRole.hh" + +namespace sta { + +TimingRole *TimingRole::wire_; +TimingRole *TimingRole::combinational_; +TimingRole *TimingRole::tristate_enable_; +TimingRole *TimingRole::tristate_disable_; +TimingRole *TimingRole::reg_clk_q_; +TimingRole *TimingRole::reg_set_clr_; +TimingRole *TimingRole::latch_en_q_; +TimingRole *TimingRole::latch_d_q_; +TimingRole *TimingRole::sdf_iopath_; +TimingRole *TimingRole::setup_; +TimingRole *TimingRole::hold_; +TimingRole *TimingRole::recovery_; +TimingRole *TimingRole::removal_; +TimingRole *TimingRole::width_; +TimingRole *TimingRole::period_; +TimingRole *TimingRole::skew_; +TimingRole *TimingRole::nochange_; +TimingRole *TimingRole::output_setup_; +TimingRole *TimingRole::output_hold_; +TimingRole *TimingRole::gated_clk_setup_; +TimingRole *TimingRole::gated_clk_hold_; +TimingRole *TimingRole::latch_setup_; +TimingRole *TimingRole::latch_hold_; +TimingRole *TimingRole::data_check_setup_; +TimingRole *TimingRole::data_check_hold_; +TimingRole *TimingRole::non_seq_setup_; +TimingRole *TimingRole::non_seq_hold_; + +TimingRoleMap TimingRole::timing_roles_; + +void +TimingRole::init() +{ + wire_ = new TimingRole("wire", false, false, false, nullptr, nullptr, 0); + combinational_ = new TimingRole("combinational", true, false, false, + nullptr, nullptr, 1); + tristate_enable_ = new TimingRole("tristate enable", + true, false, false, + nullptr, nullptr, 2); + tristate_disable_ = new TimingRole("tristate disable", + true, false, false, + nullptr, nullptr, 3); + reg_clk_q_ = new TimingRole("Reg Clk to Q", true, false, false, + nullptr, nullptr, 4); + reg_set_clr_ = new TimingRole("Reg Set/Clr", true, false, false, + nullptr, nullptr, 5); + latch_en_q_ = new TimingRole("Latch En to Q", true, false, false, + nullptr, TimingRole::regClkToQ(), 6); + latch_d_q_ = new TimingRole("Latch D to Q", true, false, false, + nullptr, nullptr, 7); + + sdf_iopath_ = new TimingRole("sdf IOPATH", true, false, false, + nullptr, nullptr, 8); + + setup_ = new TimingRole("setup", false, true, false, + MinMax::max(), nullptr, 9); + hold_ = new TimingRole("hold", false, true, false, + MinMax::min(), nullptr, 10); + recovery_ = new TimingRole("recovery", false, true, false, + MinMax::max(), TimingRole::setup(), 11); + removal_ = new TimingRole("removal", false, true, false, + MinMax::min(), TimingRole::hold(), 12); + width_ = new TimingRole("width", false, true, false, + nullptr, nullptr, 13); + period_ = new TimingRole("period", false, true, false, + nullptr, nullptr, 14); + skew_ = new TimingRole("skew", false, true, false, + nullptr, nullptr, 15); + nochange_ = new TimingRole("nochange", true, false, false, + nullptr, nullptr, 16); + + output_setup_ = new TimingRole("output setup", false, true, false, + MinMax::max(), TimingRole::setup(), 17); + output_hold_ = new TimingRole("output hold", false, true, false, + MinMax::min(), TimingRole::hold(), 18); + gated_clk_setup_ = new TimingRole("clock gating setup", + false, true, false, + MinMax::max(), TimingRole::setup(),19); + gated_clk_hold_ = new TimingRole("clock gating hold", false, true, false, + MinMax::min(), TimingRole::hold(),20); + latch_setup_ = new TimingRole("latch setup", false, true, false, + MinMax::max(), TimingRole::setup(),21); + latch_hold_ = new TimingRole("latch hold", false, true, false, + MinMax::min(), TimingRole::hold(),22); + data_check_setup_ = new TimingRole("data check setup", + false, true, false, + MinMax::max(),TimingRole::setup(),23); + data_check_hold_ = new TimingRole("data check hold", false, true, false, + MinMax::min(), TimingRole::hold(), 24); + non_seq_setup_ = new TimingRole("setup", false, true, true, + MinMax::max(), TimingRole::setup(), 25); + non_seq_hold_ = new TimingRole("hold", false, true, true, + MinMax::min(), TimingRole::hold(), 26); +} + +void +TimingRole::destroy() +{ + delete wire_; + wire_ = nullptr; + delete combinational_; + combinational_ = nullptr; + delete tristate_enable_; + tristate_enable_ = nullptr; + delete tristate_disable_; + tristate_disable_ = nullptr; + delete reg_clk_q_; + reg_clk_q_ = nullptr; + delete reg_set_clr_; + reg_set_clr_ = nullptr; + delete latch_en_q_; + latch_en_q_ = nullptr; + delete latch_d_q_; + latch_d_q_ = nullptr; + delete sdf_iopath_; + sdf_iopath_ = nullptr; + delete setup_; + setup_ = nullptr; + delete hold_; + hold_ = nullptr; + delete recovery_; + recovery_ = nullptr; + delete removal_; + removal_ = nullptr; + delete width_; + width_ = nullptr; + delete period_; + period_ = nullptr; + delete skew_; + skew_ = nullptr; + delete nochange_; + nochange_ = nullptr; + delete output_setup_; + output_setup_ = nullptr; + delete output_hold_; + output_hold_ = nullptr; + delete gated_clk_setup_; + gated_clk_setup_ = nullptr; + delete gated_clk_hold_; + gated_clk_hold_ = nullptr; + delete latch_setup_; + latch_setup_ = nullptr; + delete latch_hold_; + latch_hold_ = nullptr; + delete data_check_setup_; + data_check_setup_ = nullptr; + delete data_check_hold_; + data_check_hold_ = nullptr; + delete non_seq_setup_; + non_seq_setup_ = nullptr; + delete non_seq_hold_; + non_seq_hold_ = nullptr; + timing_roles_.clear(); +} + +TimingRole::TimingRole(const char *name, + bool is_sdf_iopath, + bool is_timing_check, + bool is_non_seq_check, + MinMax *path_min_max, + TimingRole *generic_role, + int index) : + name_(name), + is_timing_check_(is_timing_check), + is_sdf_iopath_(is_sdf_iopath), + is_non_seq_check_(is_non_seq_check), + generic_role_(generic_role), + index_(index), + path_min_max_(path_min_max) +{ + timing_roles_[name] = this; +} + +TimingRole * +TimingRole::find(const char *name) +{ + return timing_roles_[name]; +} + +const TimingRole * +TimingRole::sdfRole() const +{ + if (is_sdf_iopath_) + return sdf_iopath_; + else + return this; +} + +const TimingRole * +TimingRole::genericRole() const +{ + if (generic_role_ == nullptr) + return this; + else + return generic_role_; +} + +const EarlyLate * +TimingRole::tgtClkEarlyLate() const +{ + return path_min_max_->opposite(); +} + +bool +TimingRole::isWire() const +{ + return this == wire_; +} + +bool +TimingRole::isAsyncTimingCheck() const +{ + return this == recovery_ + || this == removal_; +} + +bool +TimingRole::isDataCheck() const +{ + return this == data_check_setup_ + || this == data_check_hold_; +} + +bool +TimingRole::less(const TimingRole *role1, + const TimingRole *role2) +{ + return role1->index() < role2->index(); +} + +} // namespace diff --git a/liberty/TimingRole.hh b/liberty/TimingRole.hh new file mode 100644 index 0000000..cdcf052 --- /dev/null +++ b/liberty/TimingRole.hh @@ -0,0 +1,138 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_TIMING_ROLE_H +#define STA_TIMING_ROLE_H + +#include "DisallowCopyAssign.hh" +#include "MinMax.hh" +#include "Map.hh" +#include "StringUtil.hh" + +namespace sta { + +class TimingRole; + +typedef Map TimingRoleMap; + +class TimingRole +{ +public: + static void init(); + static void destroy(); + static TimingRole *find(const char *name); + // Singleton accessors. + static TimingRole *wire() { return wire_; } + static TimingRole *combinational() { return combinational_; } + static TimingRole *tristateEnable() { return tristate_enable_; } + static TimingRole *tristateDisable() { return tristate_disable_; } + static TimingRole *regClkToQ() { return reg_clk_q_; } + static TimingRole *regSetClr() { return reg_set_clr_; } + static TimingRole *latchEnToQ() { return latch_en_q_; } + static TimingRole *latchDtoQ() { return latch_d_q_; } + static TimingRole *setup() { return setup_; } + static TimingRole *hold() { return hold_; } + static TimingRole *recovery() { return recovery_; } + static TimingRole *removal() { return removal_; } + static TimingRole *width() { return width_; } + static TimingRole *period() { return period_; } + static TimingRole *skew() { return skew_; } + static TimingRole *nochange() { return nochange_; } + static TimingRole *outputSetup() { return output_setup_; } + static TimingRole *outputHold() { return output_hold_; } + static TimingRole *gatedClockSetup() { return gated_clk_setup_; } + static TimingRole *gatedClockHold() { return gated_clk_hold_; } + static TimingRole *latchSetup() { return latch_setup_; } + static TimingRole *latchHold() { return latch_hold_; } + static TimingRole *dataCheckSetup() { return data_check_setup_; } + static TimingRole *dataCheckHold() { return data_check_hold_; } + static TimingRole *nonSeqSetup() { return non_seq_setup_; } + static TimingRole *nonSeqHold() { return non_seq_hold_; } + const char *asString() const { return name_; } + int index() const { return index_; } + bool isWire() const; + bool isTimingCheck() const { return is_timing_check_; } + bool isAsyncTimingCheck() const; + bool isNonSeqTimingCheck() const { return is_non_seq_check_; } + bool isDataCheck() const; + const TimingRole *genericRole() const; + const TimingRole *sdfRole() const; + // Timing check data path min/max. + MinMax *pathMinMax() const { return path_min_max_; } + // Timing check target clock path insertion delay early/late. + const EarlyLate *tgtClkEarlyLate() const; + + // Pseudo role to match sdf IOPATH. + static TimingRole *sdfIopath() { return sdf_iopath_; } + static bool less(const TimingRole *role1, + 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, + MinMax *path_min_max, + // generic_type = nullptr means type is the same as this. + TimingRole *generic_role, + int index); + DISALLOW_COPY_AND_ASSIGN(TimingRole); + + const char *name_; + bool is_timing_check_; + bool is_sdf_iopath_; + bool is_non_seq_check_; + TimingRole *generic_role_; + unsigned index_; + MinMax *path_min_max_; + + static TimingRole *wire_; + static TimingRole *combinational_; + static TimingRole *tristate_enable_; + static TimingRole *tristate_disable_; + static TimingRole *reg_clk_q_; + static TimingRole *reg_set_clr_; + static TimingRole *latch_en_q_; + static TimingRole *latch_d_q_; + static TimingRole *setup_; + static TimingRole *hold_; + static TimingRole *recovery_; + static TimingRole *removal_; + static TimingRole *width_; + static TimingRole *period_; + static TimingRole *skew_; + static TimingRole *nochange_; + static TimingRole *sdf_iopath_; + static TimingRole *output_setup_; + static TimingRole *output_hold_; + static TimingRole *gated_clk_setup_; + static TimingRole *gated_clk_hold_; + static TimingRole *latch_setup_; + static TimingRole *latch_hold_; + static TimingRole *data_check_setup_; + static TimingRole *data_check_hold_; + static TimingRole *non_seq_setup_; + static TimingRole *non_seq_hold_; + static TimingRoleMap timing_roles_; + + friend class TimingRoleLess; +}; + + + +} // namespace +#endif diff --git a/liberty/Transition.cc b/liberty/Transition.cc new file mode 100644 index 0000000..3e4ef97 --- /dev/null +++ b/liberty/Transition.cc @@ -0,0 +1,277 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Transition.hh" + +namespace sta { + +using std::max; + +TransRiseFall TransRiseFall::rise_("rise", "^", 0); +TransRiseFall TransRiseFall::fall_("fall", "v", 1); +const std::array TransRiseFall::range_{&rise_, &fall_}; +const std::array TransRiseFall::range_index_{rise_.index(), fall_.index()}; + +TransRiseFall::TransRiseFall(const char *name, + const char *short_name, + int sdf_triple_index) : + name_(name), + short_name_(stringCopy(short_name)), + sdf_triple_index_(sdf_triple_index) +{ +} + +TransRiseFall::~TransRiseFall() +{ + stringDelete(short_name_); +} + +void +TransRiseFall::setShortName(const char *short_name) +{ + stringDelete(short_name_); + short_name_ = stringCopy(short_name); +} + +TransRiseFall * +TransRiseFall::opposite() const +{ + if (this == &rise_) + return &fall_; + else + return &rise_; +} + +TransRiseFall * +TransRiseFall::find(const char *tr_str) +{ + if (stringEq(tr_str, rise_.name())) + return &rise_; + else if (stringEq(tr_str, fall_.name())) + return &fall_; + else + return nullptr; +} + +TransRiseFall * +TransRiseFall::find(int index) +{ + if (index == rise_.index()) + return &rise_; + else + return &fall_; +} + +TransRiseFallBoth * +TransRiseFall::asRiseFallBoth() +{ + if (this == &rise_) + return TransRiseFallBoth::rise(); + else + return TransRiseFallBoth::fall(); +} + +const TransRiseFallBoth * +TransRiseFall::asRiseFallBoth() const +{ + if (this == &rise_) + return TransRiseFallBoth::rise(); + else + return TransRiseFallBoth::fall(); +} + +Transition * +TransRiseFall::asTransition() const +{ + if (this == &rise_) + return Transition::rise(); + else + return Transition::fall(); +} + +//////////////////////////////////////////////////////////////// + +TransRiseFallBoth TransRiseFallBoth::rise_("rise", "^", 0, + TransRiseFall::rise(), + {TransRiseFall::rise()}, + {TransRiseFall::riseIndex()}); +TransRiseFallBoth TransRiseFallBoth::fall_("fall", "v", 1, + TransRiseFall::fall(), + {TransRiseFall::fall()}, + {TransRiseFall::fallIndex()}); +TransRiseFallBoth TransRiseFallBoth::rise_fall_("rise_fall", "rf", 2, + nullptr, + {TransRiseFall::rise(), + TransRiseFall::fall()}, + {TransRiseFall::riseIndex(), + TransRiseFall::fallIndex()}); + +TransRiseFallBoth::TransRiseFallBoth(const char *name, + const char *short_name, + int sdf_triple_index, + TransRiseFall *as_rise_fall, + std::vector range, + std::vector range_index) : + name_(name), + short_name_(stringCopy(short_name)), + sdf_triple_index_(sdf_triple_index), + as_rise_fall_(as_rise_fall), + range_(range), + range_index_(range_index) +{ +} + +TransRiseFallBoth::~TransRiseFallBoth() +{ + stringDelete(short_name_); +} + +TransRiseFallBoth * +TransRiseFallBoth::find(const char *tr_str) +{ + if (stringEq(tr_str, rise_.name())) + return &rise_; + else if (stringEq(tr_str, fall_.name())) + return &fall_; + else if (stringEq(tr_str, rise_fall_.name())) + return &rise_fall_; + else + return nullptr; +} + +bool +TransRiseFallBoth::matches(const TransRiseFall *tr) const +{ + return this == &rise_fall_ + || as_rise_fall_ == tr; +} + +bool +TransRiseFallBoth::matches(const Transition *tr) const +{ + return this == &rise_fall_ + || (this == &rise_ + && tr == Transition::rise()) + || (this == &fall_ + && tr == Transition::fall()); +} + +void +TransRiseFallBoth::setShortName(const char *short_name) +{ + stringDelete(short_name_); + short_name_ = stringCopy(short_name); +} + +//////////////////////////////////////////////////////////////// + +TransitionMap Transition::transition_map_; +int Transition::max_index_ = 0; + +// Sdf triple order defined on Sdf 3.0 spec, pg 3-17. +Transition Transition::rise_{ "^", "01", TransRiseFall::rise(), 0}; +Transition Transition::fall_ { "v", "10", TransRiseFall::fall(), 1}; +Transition Transition::tr_0Z_{"0Z", "0Z", TransRiseFall::rise(), 2}; +Transition Transition::tr_Z1_{"Z1", "Z1", TransRiseFall::rise(), 3}; +Transition Transition::tr_1Z_{"1Z", "1Z", TransRiseFall::fall(), 4}; +Transition Transition::tr_Z0_{"Z0", "Z0", TransRiseFall::fall(), 5}; +Transition Transition::tr_0X_{"0X", "0X", TransRiseFall::rise(), 6}; +Transition Transition::tr_X1_{"X1", "X1", TransRiseFall::rise(), 7}; +Transition Transition::tr_1X_{"1X", "1X", TransRiseFall::fall(), 8}; +Transition Transition::tr_X0_{"X0", "X0", TransRiseFall::fall(), 9}; +Transition Transition::tr_XZ_{"XZ", "XZ", nullptr, 10}; +Transition Transition::tr_ZX_{"ZX", "ZX", nullptr, 11}; +Transition Transition::rise_fall_{"*", "**", nullptr, -1}; + +Transition::Transition(const char *name, + const char *init_final, + TransRiseFall *as_rise_fall, + int sdf_triple_index) : + name_(stringCopy(name)), + init_final_(init_final), + as_rise_fall_(as_rise_fall), + sdf_triple_index_(sdf_triple_index) +{ + transition_map_[name_] = this; + transition_map_[init_final_] = this; + max_index_ = max(sdf_triple_index, max_index_); +} + +Transition::~Transition() +{ + stringDelete(name_); +} + +bool +Transition::matches(const Transition *tr) const +{ + return this == riseFall() || tr == this; +} + +Transition * +Transition::find(const char *tr_str) +{ + return transition_map_.findKey(tr_str); +} + +const TransRiseFallBoth * +Transition::asRiseFallBoth() const +{ + return reinterpret_cast(as_rise_fall_); +} + +void +Transition::setName(const char *name) +{ + stringDelete(name_); + name_ = stringCopy(name); +} + +//////////////////////////////////////////////////////////////// + +TransRiseFallIterator::TransRiseFallIterator(const TransRiseFallBoth *tr) +{ + if (tr == TransRiseFallBoth::riseFall()) { + index_ = 0; + index_max_ = TransRiseFall::index_max; + } + else { + index_ = tr->asRiseFall()->index(); + index_max_ = index_; + } +} + +void +TransRiseFallIterator::init() +{ + index_ = 0; + index_max_ = TransRiseFall::index_max; +} + +bool +TransRiseFallIterator::hasNext() +{ + return index_ <= index_max_; +} + +TransRiseFall * +TransRiseFallIterator::next() +{ + return (index_++ == 0) ? TransRiseFall::rise() : TransRiseFall::fall(); +} + +} // namespace diff --git a/liberty/Transition.hh b/liberty/Transition.hh new file mode 100644 index 0000000..579b3f1 --- /dev/null +++ b/liberty/Transition.hh @@ -0,0 +1,224 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_TRANSITION_H +#define STA_TRANSITION_H + +#include +#include +#include "DisallowCopyAssign.hh" +#include "Iterator.hh" +#include "Map.hh" +#include "StringUtil.hh" + +namespace sta { + +class Transition; +class TransRiseFall; +class TransRiseFallBoth; + +typedef Map TransitionMap; + +// Rise/fall transition. +class TransRiseFall +{ +public: + // Singleton accessors. + static TransRiseFall *rise() { return &rise_; } + static TransRiseFall *fall() { return &fall_; } + static int riseIndex() { return rise_.sdf_triple_index_; } + static int fallIndex() { return fall_.sdf_triple_index_; } + const char *asString() const { return short_name_; } + const char *name() const { return name_; } + const char *shortName() const { return short_name_; } + void setShortName(const char *short_name); + int index() const { return sdf_triple_index_; } + TransRiseFallBoth *asRiseFallBoth(); + const TransRiseFallBoth *asRiseFallBoth() const; + Transition *asTransition() const; + // Find transition corresponding to tr_str. + static TransRiseFall *find(const char *tr_str); + // Find transition from index. + static TransRiseFall *find(int index); + TransRiseFall *opposite() const; + + // for range support. + // for (auto tr : TransRiseFall::range()) {} + static const std::array &range() { return range_; } + // for (auto tr_index : TransRiseFall::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; + +protected: + TransRiseFall(const char *name, + const char *short_name, + int sdf_triple_index); + ~TransRiseFall(); + + const char *name_; + const char *short_name_; + const int sdf_triple_index_; + + static TransRiseFall rise_; + static TransRiseFall fall_; + static const std::array range_; + static const std::array range_index_; + +private: + DISALLOW_COPY_AND_ASSIGN(TransRiseFall); +}; + +// Rise/fall/risefall transition. +class TransRiseFallBoth +{ +public: + // Singleton accessors. + static TransRiseFallBoth *rise() { return &rise_; } + static TransRiseFallBoth *fall() { return &fall_; } + static TransRiseFallBoth *riseFall() { return &rise_fall_; } + const char *asString() const { return short_name_; } + const char *name() const { return name_; } + const char *shortName() const { return short_name_; } + void setShortName(const char *short_name); + int index() const { return sdf_triple_index_; } + bool matches(const TransRiseFall *tr) const; + bool matches(const Transition *tr) const; + TransRiseFall *asRiseFall() const { return as_rise_fall_; } + // Find transition corresponding to string. + static TransRiseFallBoth *find(const char *tr_str); + // for (auto tr : min_max->range()) {} + const std::vector &range() const { return range_; } + // for (auto tr_index : min_max->rangeIndex()) {} + 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; + +protected: + TransRiseFallBoth(const char *name, + const char *short_name, + int sdf_triple_index, + TransRiseFall *as_rise_fall, + std::vector range, + std::vector range_index); + ~TransRiseFallBoth(); + + const char *name_; + const char *short_name_; + const int sdf_triple_index_; + TransRiseFall *as_rise_fall_; + const std::vector range_; + const std::vector range_index_; + + static TransRiseFallBoth rise_; + static TransRiseFallBoth fall_; + static TransRiseFallBoth rise_fall_; + +private: + DISALLOW_COPY_AND_ASSIGN(TransRiseFallBoth); +}; + +// General SDF transition. +class Transition +{ +public: + // Singleton accessors. + static Transition *rise() { return &rise_; } + static Transition *fall() { return &fall_; } + static Transition *tr0Z() { return &tr_0Z_; } + static Transition *trZ1() { return &tr_Z1_; } + static Transition *tr1Z() { return &tr_1Z_; } + static Transition *trZ0() { return &tr_Z0_; } + static Transition *tr0X() { return &tr_0X_; } + static Transition *trX1() { return &tr_X1_; } + static Transition *tr1X() { return &tr_1X_; } + static Transition *trX0() { return &tr_X0_; } + static Transition *trXZ() { return &tr_XZ_; } + static Transition *trZX() { return &tr_ZX_; } + void setName(const char *name); + // Matches rise and fall. + static Transition *riseFall() { return &rise_fall_; } + const char *asString() const { return name_; } + // As initial/final value pair. + const char *asInitFinalString() const { return init_final_; } + int sdfTripleIndex() const { return sdf_triple_index_; } + int index() const { return sdf_triple_index_; } + TransRiseFall *asRiseFall() const { return as_rise_fall_; } + const TransRiseFallBoth *asRiseFallBoth() const; + bool matches(const Transition *tr) const; + // Find transition corresponding to string. + static Transition *find(const char *tr_str); + static int maxIndex() { return max_index_; } + +private: + Transition(const char *name, + const char *init_final, + TransRiseFall *as_rise_fall, + int sdf_triple_index); + ~Transition(); + + const char *name_; + const char *init_final_; + TransRiseFall *as_rise_fall_; + const int sdf_triple_index_; + + static Transition rise_; + static Transition fall_; + static Transition tr_0Z_; + static Transition tr_Z1_; + static Transition tr_1Z_; + static Transition tr_Z0_; + static Transition tr_0X_; + static Transition tr_X1_; + static Transition tr_1X_; + static Transition tr_X0_; + static Transition tr_XZ_; + static Transition tr_ZX_; + static 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 TransitionMap transition_map_; + static int max_index_; + +private: + DISALLOW_COPY_AND_ASSIGN(Transition); +}; + +// Obsolete. Use range iteration instead. +// for (auto tr : TransRiseFall::range()) {} +class TransRiseFallIterator : public Iterator +{ +public: + TransRiseFallIterator() : index_(0), index_max_(TransRiseFall::index_max) {} + explicit TransRiseFallIterator(const TransRiseFallBoth *tr); + void init(); + virtual bool hasNext(); + virtual TransRiseFall *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(TransRiseFallIterator); + + int index_; + int index_max_; +}; + +} // namespace +#endif diff --git a/liberty/Units.cc b/liberty/Units.cc new file mode 100644 index 0000000..42cda57 --- /dev/null +++ b/liberty/Units.cc @@ -0,0 +1,149 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include // abs +#include +#include "Machine.hh" +#include "StringUtil.hh" +#include "MinMax.hh" // INF +#include "Units.hh" + +namespace sta { + +using std::abs; + +Unit::Unit() : + scale_(1.0), + suffix_(stringCopy("")), + digits_(4) +{ +} + +Unit::Unit(float scale, + const char *suffix, + int digits) : + scale_(scale), + suffix_(stringCopy(suffix)), + digits_(digits) +{ +} + +Unit::~Unit() +{ + stringDelete(suffix_); +} + +void +Unit::copy(const Unit *unit) +{ + if (suffix_) + stringDelete(suffix_); + scale_ = unit->scale_; + suffix_ = stringCopy(unit->suffix_); + digits_ = unit->digits_; +} + +void +Unit::setScale(float scale) +{ + scale_ = scale; +} + +void +Unit::setSuffix(const char *suffix) +{ + if (suffix_) + stringDelete(suffix_); + suffix_ = stringCopy(suffix); +} + +void +Unit::setDigits(int digits) +{ + digits_ = digits; +} + +int +Unit::width() const +{ + return digits_ + (suffix_ ? strlen(suffix_) : 0) + 2; +} + +const char * +Unit::asString(float value) const +{ + return asString(value, digits_); +} + +const char * +Unit::asString(double value) const +{ + return asString(static_cast(value), digits_); +} + +const char * +Unit::asString(float value, + int digits) const +{ + // Special case INF because it blows up otherwise. + if (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) + scaled_value = 0.0; + return stringPrintTmp("%.*f%s", digits, scaled_value, suffix_); + } +} + +//////////////////////////////////////////////////////////////// + +Unit * +Units::find(const char *unit_name) +{ + if (stringEq(unit_name, "time")) + return &time_unit_; + else if (stringEq(unit_name, "capacitance")) + return &capacitance_unit_; + else if (stringEq(unit_name, "resistance")) + return &resistance_unit_; + else if (stringEq(unit_name, "voltage")) + return &voltage_unit_; + else if (stringEq(unit_name, "current")) + return ¤t_unit_; + else if (stringEq(unit_name, "power")) + return &power_unit_; + else if (stringEq(unit_name, "distance")) + return &distance_unit_; + else + return nullptr; +} + +void +Units::copy(const Units *units) +{ + time_unit_.copy(units->timeUnit()); + capacitance_unit_.copy(units->capacitanceUnit()); + resistance_unit_.copy(units->resistanceUnit()); + voltage_unit_.copy(units->voltageUnit()); + current_unit_.copy(units->currentUnit()); + power_unit_.copy(units->powerUnit()); + distance_unit_.copy(units->distanceUnit()); + scalar_unit_.copy(units->scalarUnit()); +} + +} // namespace diff --git a/liberty/Units.hh b/liberty/Units.hh new file mode 100644 index 0000000..ae2855e --- /dev/null +++ b/liberty/Units.hh @@ -0,0 +1,95 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_UNITS_H +#define STA_UNITS_H + +#include "DisallowCopyAssign.hh" + +namespace sta { + +class Unit +{ +public: + Unit(); + ~Unit(); + Unit(float scale, + const char *suffix, + int digits); + void copy(const Unit *unit); + float scale() const { return scale_; } + void setScale(float scale); + const char *suffix() const { return suffix_; } + void setSuffix(const char *suffix); + int digits() const { return digits_; } + void setDigits(int digits); + int width() const; + const char *asString(float value) const; + const char *asString(double value) const; + const char *asString(float value, + int digits) const; + +private: + DISALLOW_COPY_AND_ASSIGN(Unit); + + float scale_; // multiplier from user units to internal units + const char *suffix_; // print suffix + int digits_; // print digits (after decimal pt) +}; + +// User interface units. +// Sta internal units are always seconds, farads, volts, amps. +class Units +{ +public: + Units() {} + Unit *find(const char *unit_name); + void copy(const Units *units); + Unit *timeUnit() { return &time_unit_; } + const Unit *timeUnit() const { return &time_unit_; } + Unit *capacitanceUnit() { return &capacitance_unit_; } + const Unit *capacitanceUnit() const { return &capacitance_unit_; } + Unit *voltageUnit() { return &voltage_unit_; } + const Unit *voltageUnit() const { return &voltage_unit_; } + Unit *resistanceUnit() { return &resistance_unit_; } + const Unit *resistanceUnit() const { return &resistance_unit_; } + Unit *pullingResistanceUnit() { return &pulling_resistance_unit_; } + const Unit *pullingResistanceUnit() const {return &pulling_resistance_unit_;} + Unit *currentUnit() { return ¤t_unit_; } + const Unit *currentUnit() const { return ¤t_unit_; } + Unit *powerUnit() { return &power_unit_; } + const Unit *powerUnit() const { return &power_unit_; } + Unit *distanceUnit() { return &distance_unit_; } + const Unit *distanceUnit() const { return &distance_unit_; } + Unit *scalarUnit() { return &scalar_unit_; } + const Unit *scalarUnit() const { return &scalar_unit_; } + +private: + DISALLOW_COPY_AND_ASSIGN(Units); + + Unit time_unit_; + Unit capacitance_unit_; + Unit voltage_unit_; + Unit resistance_unit_; + Unit pulling_resistance_unit_; + Unit current_unit_; + Unit power_unit_; + Unit distance_unit_; + Unit scalar_unit_; +}; + +} // namespace +#endif diff --git a/liberty/Wireload.cc b/liberty/Wireload.cc new file mode 100644 index 0000000..ebe1531 --- /dev/null +++ b/liberty/Wireload.cc @@ -0,0 +1,303 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "StringUtil.hh" +#include "Liberty.hh" +#include "Wireload.hh" + +namespace sta { + +Wireload::Wireload(const char *name, + LibertyLibrary *library) : + name_(stringCopy(name)), + library_(library), + area_(0.0F), + resistance_(0.0F), + capacitance_(0.0F), + slope_(0.0F) +{ +} + +Wireload::Wireload(const char *name, + LibertyLibrary *library, + float area, + float resistance, + float capacitance, + float slope) : + name_(stringCopy(name)), + library_(library), + area_(area), + resistance_(resistance), + capacitance_(capacitance), + slope_(slope) +{ +} + +Wireload::~Wireload() +{ + fanout_lengths_.deleteContents(); + stringDelete(name_); +} + +void +Wireload::setArea(float area) +{ + area_ = area; +} + +void +Wireload::setResistance(float res) +{ + resistance_ = res; +} + +void +Wireload::setCapacitance(float cap) +{ + capacitance_ = cap; +} + +void +Wireload::setSlope(float slope) +{ + slope_ = slope; +} + +struct FanoutLess +{ + bool operator()(FanoutLength *fanout1, + FanoutLength *fanout2) const + { + return fanout1->first < fanout2->first; + } +}; + +void +Wireload::addFanoutLength(float fanout, + float length) +{ + FanoutLength *fanout_length = new FanoutLength(fanout, length); + fanout_lengths_.push_back(fanout_length); + // Keep fanouts sorted for lookup. + if (fanout_lengths_.size() > 1 + && fanout < (fanout_lengths_[fanout_lengths_.size() - 2])->first) + sort(fanout_lengths_, FanoutLess()); +} + +void +Wireload::findWireload(float fanout, + const OperatingConditions *op_cond, + float &cap, + float &res) const +{ + size_t size = fanout_lengths_.size(); + float length; + if (size == 0) + length = 0; + else { + size_t max = size - 1; + float fanout0 = fanout_lengths_[0]->first; + float fanout_max = fanout_lengths_[max]->first; + if (fanout < fanout0) { + // Extrapolate from lowest fanout entry. + length = fanout_lengths_[0]->second - (fanout0 - fanout) * slope_; + if (length < 0) + length = 0; + } + else if (fanout == fanout0) + length = fanout_lengths_[0]->second; + else if (fanout >= fanout_max) + // Extrapolate from max fanout entry. + length = fanout_lengths_[max]->second + (fanout - fanout_max) * slope_; + else { + // Bisection search. + 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; + } + // Interpolate between lower and lower+1 entries. + float fanout1 = fanout_lengths_[lower]->first; + float fanout2 = fanout_lengths_[lower+1]->first; + float l1 = fanout_lengths_[lower]->second; + float l2 = fanout_lengths_[lower+1]->second; + length = l1 + (l2 - l1) * (fanout - fanout1) / (fanout2 - fanout1); + } + } + // Scale resistance and capacitance. + cap = length * capacitance_ + * library_->scaleFactor(ScaleFactorType::wire_cap, op_cond); + res = length * resistance_ + * library_->scaleFactor(ScaleFactorType::wire_res, op_cond); +} + +//////////////////////////////////////////////////////////////// + +class WireloadForArea +{ +public: + WireloadForArea(float min_area, + float max_area, + const Wireload *wireload); + float minArea() const { return min_area_; } + float maxArea() const { return max_area_; } + const Wireload *wireload() const { return wireload_; } + +private: + DISALLOW_COPY_AND_ASSIGN(WireloadForArea); + + float min_area_; + float max_area_; + const Wireload *wireload_; +}; + +WireloadForArea::WireloadForArea(float min_area, + 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() +{ + wireloads_.deleteContents(); + stringDelete(name_); +} + +struct WireloadForAreaMinLess +{ + bool operator()(WireloadForArea *wireload1, + WireloadForArea *wireload2) const + { + return wireload1->minArea() < wireload2->minArea(); + } +}; + +void +WireloadSelection::addWireloadFromArea(float min_area, + float max_area, + const Wireload *wireload) +{ + WireloadForArea *wireload_area = new WireloadForArea(min_area, max_area, + wireload); + wireloads_.push_back(wireload_area); + // Keep wireloads sorted by area for lookup. + if (wireloads_.size() > 1 + && min_area < (wireloads_[wireloads_.size() - 2])->minArea()) + sort(wireloads_, WireloadForAreaMinLess()); +} + +// Bisection search. +const Wireload * +WireloadSelection::findWireload(float area) const +{ + int max = static_cast(wireloads_.size()) - 1; + int lower = -1; + int upper = max + 1; + while (upper - lower > 1) { + int mid = (upper + lower) >> 1; + if (area >= wireloads_[mid]->minArea()) + lower = mid; + else + upper = mid; + } + float area0 = wireloads_[0]->minArea(); + float area_max = wireloads_[max]->minArea(); + if (area <= area0) + return wireloads_[0]->wireload(); + else if (area >= area_max) + return wireloads_[max]->wireload(); + else + return wireloads_[lower]->wireload(); +} + +//////////////////////////////////////////////////////////////// + +const char * +wireloadTreeString(WireloadTree tree) +{ + switch (tree) { + case WireloadTree::worst_case: + return "worst_case_tree"; + case WireloadTree::best_case: + return "best_case_tree"; + case WireloadTree::balanced: + return "balanced_tree"; + case WireloadTree::unknown: + return "unknown"; + } + // Prevent warnings from lame compilers. + return "?"; +} + +WireloadTree +stringWireloadTree(const char *wire_load_type) +{ + if (stringEq(wire_load_type, "worst_case_tree")) + return WireloadTree::worst_case; + else if (stringEq(wire_load_type, "best_case_tree")) + return WireloadTree::best_case; + else if (stringEq(wire_load_type, "balanced_tree")) + return WireloadTree::balanced; + else + return WireloadTree::unknown; +} + +const char * +wireloadModeString(WireloadMode wire_load_mode) +{ + switch (wire_load_mode) { + case WireloadMode::top: + return "top"; + case WireloadMode::enclosed: + return "enclosed"; + case WireloadMode::segmented: + return "segmented"; + case WireloadMode::unknown: + return "unknown"; + } + // Prevent warnings from lame compilers. + return "?"; +} + +WireloadMode +stringWireloadMode(const char *wire_load_mode) +{ + if (stringEq(wire_load_mode, "top")) + return WireloadMode::top; + else if (stringEq(wire_load_mode, "enclosed")) + return WireloadMode::enclosed; + else if (stringEq(wire_load_mode, "segmented")) + return WireloadMode::segmented; + else + return WireloadMode::unknown; +} + +} // namespace diff --git a/liberty/Wireload.hh b/liberty/Wireload.hh new file mode 100644 index 0000000..d30b61d --- /dev/null +++ b/liberty/Wireload.hh @@ -0,0 +1,100 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_WIRELOAD_H +#define STA_WIRELOAD_H + +#include "DisallowCopyAssign.hh" +#include "Vector.hh" +#include "LibertyClass.hh" + +namespace sta { + +class WireloadForArea; + +typedef std::pair FanoutLength; +typedef Vector FanoutLengthSeq; +typedef Vector WireloadForAreaSeq; + +const char * +wireloadTreeString(WireloadTree tree); +WireloadTree +stringWireloadTree(const char *tree); + +const char * +wireloadModeString(WireloadMode wire_load_mode); +WireloadMode +stringWireloadMode(const char *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); + virtual ~Wireload(); + const char *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); + // Find wireload resistance/capacitance for fanout. + virtual void findWireload(float fanout, + const OperatingConditions *op_cond, + float &cap, + float &res) const; + +protected: + const char *name_; + LibertyLibrary *library_; + float area_; + float resistance_; + float capacitance_; + // Fanout length extrapolation slope. + float slope_; + FanoutLengthSeq fanout_lengths_; + +private: + DISALLOW_COPY_AND_ASSIGN(Wireload); +}; + +class WireloadSelection +{ +public: + explicit WireloadSelection(const char *name); + ~WireloadSelection(); + const char *name() const { return name_; } + void addWireloadFromArea(float min_area, + float max_area, + const Wireload *wireload); + const Wireload *findWireload(float area) const; + +private: + DISALLOW_COPY_AND_ASSIGN(WireloadSelection); + + const char *name_; + WireloadForAreaSeq wireloads_; +}; + +} // namespace +#endif diff --git a/network/ConcreteLibrary.cc b/network/ConcreteLibrary.cc new file mode 100644 index 0000000..059e104 --- /dev/null +++ b/network/ConcreteLibrary.cc @@ -0,0 +1,635 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "PatternMatch.hh" +#include "PortDirection.hh" +#include "ParseBus.hh" +#include "ConcreteLibrary.hh" + +namespace sta { + +static constexpr char escape_ = '\\'; + +ConcreteLibrary::ConcreteLibrary(const char *name, + const char *filename, + bool is_liberty) : + name_(stringCopy(name)), + filename_(stringCopy(filename)), + is_liberty_(is_liberty), + bus_brkt_left_('['), + bus_brkt_right_(']') +{ +} + +ConcreteLibrary::~ConcreteLibrary() +{ + stringDelete(name_); + stringDelete(filename_); + cell_map_.deleteContents(); +} + +ConcreteCell * +ConcreteLibrary::makeCell(const char *name, + bool is_leaf, + const char *filename) +{ + ConcreteCell *cell = new ConcreteCell(this, name, is_leaf, filename); + addCell(cell); + return cell; +} + +void +ConcreteLibrary::addCell(ConcreteCell *cell) +{ + cell_map_[cell->name()] = cell; +} + +void +ConcreteLibrary::renameCell(ConcreteCell *cell, + const char *cell_name) +{ + cell_map_.erase(cell->name()); + cell_map_[cell_name] = cell; +} + +void +ConcreteLibrary::deleteCell(ConcreteCell *cell) +{ + cell_map_.erase(cell->name()); + delete cell; +} + +ConcreteLibraryCellIterator * +ConcreteLibrary::cellIterator() const +{ + return new ConcreteLibraryCellIterator(cell_map_); +} + +ConcreteCell * +ConcreteLibrary::findCell(const char *name) const +{ + return cell_map_.findKey(name); +} + +void +ConcreteLibrary::findCellsMatching(const PatternMatch *pattern, + CellSeq *cells) const +{ + ConcreteLibraryCellIterator cell_iter=ConcreteLibraryCellIterator(cell_map_); + while (cell_iter.hasNext()) { + ConcreteCell *cell = cell_iter.next(); + if (pattern->match(cell->name())) + cells->push_back(reinterpret_cast(cell)); + } +} + +void +ConcreteLibrary::setBusBrkts(char left, + char right) +{ + bus_brkt_left_ = left; + bus_brkt_right_ = right; +} + +//////////////////////////////////////////////////////////////// + +ConcreteCell::ConcreteCell(ConcreteLibrary *library, + const char *name, + bool is_leaf, + const char *filename): + library_(library), + name_(stringCopy(name)), + filename_(stringCopy(filename)), + liberty_cell_(nullptr), + port_bit_count_(0), + is_leaf_(is_leaf) +{ +} + +ConcreteCell::~ConcreteCell() +{ + stringDelete(name_); + if (filename_) + stringDelete(filename_); + ports_.deleteContents(); +} + +void +ConcreteCell::setName(const char *name) +{ + const char *name_cpy = stringCopy(name); + library_->renameCell(this, name_cpy); + stringDelete(name_); + name_ = name_cpy; +} + +void +ConcreteCell::setLibertyCell(LibertyCell *cell) +{ + liberty_cell_ = cell; +} + +ConcretePort * +ConcreteCell::makePort(const char *name) +{ + ConcretePort *port = new ConcretePort(this, name, false, -1, -1, false, nullptr); + addPort(port); + return port; +} + +ConcretePort * +ConcreteCell::makeBundlePort(const char *name, + ConcretePortSeq *members) +{ + ConcretePort *port = new ConcretePort(this, name, false, -1, -1, + true, members); + addPort(port); + return port; +} + +ConcretePort * +ConcreteCell::makeBusPort(const char *name, + int from_index, + int to_index) +{ + ConcretePort *port = new ConcretePort(this, name, true, from_index, to_index, + false, new ConcretePortSeq); + addPort(port); + makeBusPortBits(port, name, from_index, to_index); + return port; +} + +ConcretePort * +ConcreteCell::makeBusPort(const char *name, + int from_index, + int to_index, + ConcretePortSeq *members) +{ + ConcretePort *port = new ConcretePort(this, name, true, from_index, to_index, + false, members); + addPort(port); + return port; +} + +void +ConcreteCell::makeBusPortBits(ConcretePort *bus_port, + const char *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); + } + else { + for (int index = from_index; index >= to_index; index--) + makeBusPortBit(bus_port, 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); + bus_port->addPortBit(port); + addPortBit(port); +} + +ConcretePort * +ConcreteCell::makePort(const char *bit_name, + int bit_index) +{ + ConcretePort *port = new ConcretePort(this, bit_name, false, bit_index, + bit_index, false, nullptr); + addPortBit(port); + return port; +} + +void +ConcreteCell::addPortBit(ConcretePort *port) +{ + port_map_[port->name()] = port; + port->setPinIndex(port_bit_count_++); +} + +void +ConcreteCell::addPort(ConcretePort *port) +{ + port_map_[port->name()] = port; + ports_.push_back(port); + if (!port->hasMembers()) + port->setPinIndex(port_bit_count_++); +} + +void +ConcreteCell::setIsLeaf(bool is_leaf) +{ + is_leaf_ = is_leaf; +} + +ConcretePort * +ConcreteCell::findPort(const char *name) const +{ + return port_map_.findKey(name); +} + +size_t +ConcreteCell::portCount() const +{ + return ports_.size(); +} + +void +ConcreteCell::findPortsMatching(const PatternMatch *pattern, + PortSeq *ports) const +{ + char bus_brkt_right = library_->busBrktRight(); + const char *pattern1 = pattern->pattern(); + bool bus_pattern = (pattern1[strlen(pattern1) - 1] == bus_brkt_right); + ConcreteCellPortIterator *port_iter = portIterator(); + while (port_iter->hasNext()) { + ConcretePort *port = port_iter->next(); + if (port->isBus() && bus_pattern) { + ConcretePortMemberIterator *member_iter = port->memberIterator(); + while (member_iter->hasNext()) { + ConcretePort *port_bit = member_iter->next(); + if (pattern->match(port_bit->name())) + ports->push_back(reinterpret_cast(port_bit)); + } + delete member_iter; + } + else if (pattern->match(port->name())) + ports->push_back(reinterpret_cast(port)); + } + delete port_iter; +} + +ConcreteCellPortIterator * +ConcreteCell::portIterator() const +{ + return new ConcreteCellPortIterator(ports_); +} + +ConcreteCellPortBitIterator * +ConcreteCell::portBitIterator() const +{ + return new ConcreteCellPortBitIterator(this); +} + +//////////////////////////////////////////////////////////////// + +class BusPort +{ +public: + BusPort(const char *name, + int from, + PortDirection *direction); + ~BusPort(); + const char *name() const { return name_; } + void pushMember(ConcretePort *port); + void setTo(int to); + int from() const { return from_; } + int to() const { return to_; } + ConcretePortSeq *members() { return members_; } + PortDirection *direction() { return direction_; } + +private: + DISALLOW_COPY_AND_ASSIGN(BusPort); + + const char *name_; + int from_; + int to_; + PortDirection *direction_; + ConcretePortSeq *members_; +}; + +BusPort::BusPort(const char *name, + int from, + PortDirection *direction) : + name_(name), + from_(from), + to_(from), + direction_(direction), + members_(new ConcretePortSeq) +{ +} + +BusPort::~BusPort() +{ + // members_ ownership is transfered to bus port. + stringDelete(name_); +} + +void +BusPort::pushMember(ConcretePort *port) +{ + members_->push_back(port); +} + +void +BusPort::setTo(int to) +{ + to_ = to; +} + +typedef Map BusPortMap; + +void +ConcreteCell::groupBusPorts(const char bus_brkt_left, + const char bus_brkt_right) +{ + const char bus_brkts_left[2]{bus_brkt_left, '\0'}; + const char bus_brkts_right[2]{bus_brkt_right, '\0'}; + BusPortMap port_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 + // the ones we want to remove. + int port_index = 0; + ConcretePortSeq::Iterator port_iter(ports_); + while (port_iter.hasNext()) { + ConcretePort *port = port_iter.next(); + const char *port_name = port->name(); + char *bus_name; + int index; + parseBusName(port_name, bus_brkts_left, bus_brkts_right, escape_, + bus_name, index); + if (bus_name) { + if (!port->isBusBit()) { + BusPort *bus_port = port_map.findKey(bus_name); + if (bus_port) { + bus_port->setTo(index); + stringDelete(bus_name); + } + else { + bus_port = new BusPort(bus_name, index, port->direction()); + port_map[bus_name] = bus_port; + } + bus_port->pushMember(port); + } + else + ports_[port_index++] = port; + } + else + ports_[port_index++] = port; + } + // Resize to forget the ports that didn't make the cut. + ports_.resize(port_index); + + // Make the bus ports. + BusPortMap::Iterator bus_iter(port_map); + while (bus_iter.hasNext()) { + BusPort *bus_port = bus_iter.next(); + const char *bus_name = bus_port->name(); + ConcretePortSeq *members = bus_port->members(); + ConcretePort *port = makeBusPort(bus_name, bus_port->from(), + bus_port->to(), members); + port->setDirection(bus_port->direction()); + delete bus_port; + + ConcretePortSeq::Iterator member_iter(members); + while (member_iter.hasNext()) { + ConcretePort *port = member_iter.next(); + char *bus_name; + int index; + parseBusName(port->name(), bus_brkts_left, bus_brkts_right, escape_, + bus_name, index); + port->setBusBitIndex(index); + stringDelete(bus_name); + } + } +} + +//////////////////////////////////////////////////////////////// + +ConcretePort::ConcretePort(ConcreteCell *cell, + const char *name, + bool is_bus, + int from_index, + int to_index, + bool is_bundle, + ConcretePortSeq *member_ports) : + name_(stringCopy(name)), + cell_(cell), + direction_(PortDirection::unknown()), + liberty_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) +{ +} + +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(); + delete member_ports_; + stringDelete(name_); +} + +Cell * +ConcretePort::cell() const +{ + return reinterpret_cast(cell_); +} + +void +ConcretePort::setLibertyPort(LibertyPort *port) +{ + liberty_port_ = port; +} + +const char * +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()); + } + else + return name_; +} + +ConcretePort * +ConcretePort::findMember(int index) const +{ + return (*member_ports_)[index]; +} + +bool +ConcretePort::isBusBit() const +{ + return from_index_ != -1 + && from_index_ == to_index_; +} + +void +ConcretePort::setBusBitIndex(int index) +{ + from_index_ = to_index_ = index; +} + +int +ConcretePort::size() const +{ + if (is_bus_) + return abs(to_index_ - from_index_) + 1; + else if (is_bundle_) + return static_cast(member_ports_->size()); + else + return 1; +} + +void +ConcretePort::setPinIndex(int index) +{ + pin_index_ = index; +} + +void +ConcretePort::setDirection(PortDirection *dir) +{ + direction_ = dir; + if (hasMembers()) { + ConcretePortMemberIterator *member_iter = memberIterator(); + while (member_iter->hasNext()) { + ConcretePort *port_bit = member_iter->next(); + port_bit->setDirection(dir); + } + delete member_iter; + } +} + +void +ConcretePort::addPortBit(ConcretePort *port) +{ + member_ports_->push_back(port); +} + +ConcretePort * +ConcretePort::findBusBit(int index) const +{ + if (from_index_ < to_index_ + && index <= to_index_ + && index >= from_index_) + return (*member_ports_)[index - from_index_]; + else if (from_index_ >= to_index_ + && index >= to_index_ + && index <= from_index_) + return (*member_ports_)[from_index_ - index]; + else + return nullptr; +} + +bool +ConcretePort::busIndexInRange(int index) const +{ + return (from_index_ <= to_index_ + && index <= to_index_ + && index >= from_index_) + || (from_index_ > to_index_ + && index >= to_index_ + && index <= from_index_); +} + +bool +ConcretePort::hasMembers() const +{ + return is_bus_ || is_bundle_; +} + +ConcretePortMemberIterator * +ConcretePort::memberIterator() const +{ + return new ConcretePortMemberIterator(member_ports_); +} + +//////////////////////////////////////////////////////////////// + +ConcreteCellPortBitIterator::ConcreteCellPortBitIterator(const ConcreteCell* + cell) : + port_iter_(cell->ports_), + member_iter_(nullptr), + next_(nullptr) +{ + findNext(); +} + +bool +ConcreteCellPortBitIterator::hasNext() +{ + return next_ != nullptr; +} + +ConcretePort * +ConcreteCellPortBitIterator::next() +{ + ConcretePort *next = next_; + findNext(); + return next; +} + +void +ConcreteCellPortBitIterator::findNext() +{ + if (member_iter_) { + if (member_iter_->hasNext()) { + next_ = member_iter_->next(); + return; + } + else { + delete member_iter_; + member_iter_ = nullptr; + } + } + while (port_iter_.hasNext()) { + ConcretePort *next = port_iter_.next(); + if (next->isBus()) { + member_iter_ = next->memberIterator(); + next_ = member_iter_->next(); + return; + } + else if (!next->isBundle()) { + next_ = next; + return; + } + next_ = nullptr; + } + next_ = nullptr; +} + +} // namespace diff --git a/network/ConcreteLibrary.hh b/network/ConcreteLibrary.hh new file mode 100644 index 0000000..906b135 --- /dev/null +++ b/network/ConcreteLibrary.hh @@ -0,0 +1,255 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CONCRETE_LIBRARY_H +#define STA_CONCRETE_LIBRARY_H + +#include "DisallowCopyAssign.hh" +#include "Vector.hh" +#include "Map.hh" +#include "StringUtil.hh" +#include "NetworkClass.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 +// that implement the library portion of the network API. + +namespace sta { + +class ConcreteLibrary; +class ConcreteCell; +class ConcretePort; +class ConcreteCellPortBitIterator; +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; + +class ConcreteLibrary +{ +public: + explicit ConcreteLibrary(const char *name, + const char *filename, + bool is_liberty); + virtual ~ConcreteLibrary(); + const char *name() const { return name_; } + void setName(const char *name); + bool isLiberty() const { return is_liberty_; } + const char *filename() const { return filename_; } + void addCell(ConcreteCell *cell); + ConcreteCell *makeCell(const char *name, + bool is_leaf, + const char *filename); + void deleteCell(ConcreteCell *cell); + ConcreteLibraryCellIterator *cellIterator() const; + ConcreteCell *findCell(const char *name) const; + void findCellsMatching(const PatternMatch *pattern, + CellSeq *cells) const; + char busBrktLeft() { return bus_brkt_left_; } + char busBrktRight() { return bus_brkt_right_; } + void setBusBrkts(char left, + char right); + +protected: + void renameCell(ConcreteCell *cell, + const char *cell_name); + + const char *name_; + const char *filename_; + bool is_liberty_; + char bus_brkt_left_; + char bus_brkt_right_; + ConcreteCellMap cell_map_; + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteLibrary); + + friend class ConcreteCell; +}; + +class ConcreteCell +{ +public: + // Use ConcreteLibrary::deleteCell. + virtual ~ConcreteCell(); + ConcreteLibrary *library() const { return library_; } + const char *name() const { return name_; } + const char *filename() const { return filename_; } + LibertyCell *libertyCell() const { return liberty_cell_; } + void setLibertyCell(LibertyCell *cell); + int portBitCount() const { return port_bit_count_; } + ConcretePort *findPort(const char *name) const; + void findPortsMatching(const PatternMatch *pattern, + PortSeq *ports) const; + ConcreteCellPortIterator *portIterator() const; + ConcreteCellPortBitIterator *portBitIterator() const; + bool isLeaf() const { return is_leaf_; } + void setIsLeaf(bool is_leaf); + + // Cell acts as port factory. + ConcretePort *makePort(const char *name); + // Bus port. + ConcretePort *makeBusPort(const char *name, + int from_index, + int to_index); + // Bundle port. + ConcretePort *makeBundlePort(const char *name, + ConcretePortSeq *members); + // Group previously defined bus bit ports together. + void groupBusPorts(const char bus_brkt_left, + const char bus_brkt_right); + size_t portCount() const; + void setName(const char *name); + void addPort(ConcretePort *port); + void addPortBit(ConcretePort *port); + +protected: + ConcreteCell(ConcreteLibrary *library, + const char *name, + bool is_leaf, + const char *filename); + ConcretePort *makeBusPort(const char *name, + int from_index, + int to_index, + ConcretePortSeq *members); + void makeBusPortBits(ConcretePort *bus_port, + const char *name, + int from_index, + int to_index); + // Bus port bit (internal to makeBusPortBits). + ConcretePort *makePort(const char *bit_name, + int bit_index); + void makeBusPortBit(ConcretePort *bus_port, + const char *name, + int index); + + ConcreteLibrary *library_; + const char *name_; + // Filename is optional. + const char *filename_; + LibertyCell *liberty_cell_; + // 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_; + bool is_leaf_; + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteCell); + + friend class ConcreteLibrary; + friend class ConcreteCellPortBitIterator; +}; + +class ConcretePort +{ +public: + virtual ~ConcretePort(); + const char *name() const { return name_; } + const char *busName() const; + Cell *cell() const; + ConcreteLibrary *library() const { return cell_->library(); } + PortDirection *direction() const { return direction_; } + LibertyPort *libertyPort() const { return liberty_port_; } + void setLibertyPort(LibertyPort *port); + void setDirection(PortDirection *dir); + // Bundles are groups of related ports that do not use + // bus notation. + bool isBundle() const { return is_bundle_; } + bool isBus() const { return is_bus_; } + // Index of cell bit ports. + // Bus/bundle ports do not have an pin index. + int pinIndex() const { return pin_index_; } + void setPinIndex(int index); + // Size is the bus/bundle member count (1 for non-bus/bundle ports). + int size() const; + int fromIndex() const { return from_index_; } + int toIndex() const { return to_index_; } + // Bus member, bus[subscript]. + ConcretePort *findBusBit(int index) const; + // Predicate to determine if subscript is within bus range. + // (toIndex > fromIndex) && fromIndex <= subscript <= toIndex + // || (fromIndex > toIndex) && fromIndex >= subscript >= toIndex + bool busIndexInRange(int index) const; + // A port has members if it is a bundle or bus. + bool hasMembers() const; + ConcretePort *findMember(int index) const; + ConcretePortMemberIterator *memberIterator() const; + void setBusBitIndex(int index); + + // Bus bit port functions. + // Bus bit is one bit of a bus port. + bool isBusBit() const; + // Bit index within bus port. + // The bit index of A[3] is 3. + int busBitIndex() const { return to_index_; } + ConcretePortSeq *memberPorts() const { return member_ports_; } + void addPortBit(ConcretePort *port); + +protected: + // Constructors for factory in cell class. + ConcretePort(ConcreteCell *cell, + const char *name, + bool is_bus, + int from_index, + int to_index, + bool is_bundle, + ConcretePortSeq *member_ports); + + const char *name_; + ConcreteCell *cell_; + PortDirection *direction_; + LibertyPort *liberty_port_; + int pin_index_; + bool is_bundle_; + bool is_bus_; + int from_index_; + int to_index_; + // Expanded bus bit ports (ordered by from_index_ to to_index_) + // or bundle member ports. + ConcretePortSeq *member_ports_; + +private: + DISALLOW_COPY_AND_ASSIGN(ConcretePort); + + friend class ConcreteCell; +}; + +class ConcreteCellPortBitIterator : public Iterator +{ +public: + explicit ConcreteCellPortBitIterator(const ConcreteCell *cell); + virtual bool hasNext(); + virtual ConcretePort *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteCellPortBitIterator); + void findNext(); + + ConcretePortSeq::ConstIterator port_iter_; + ConcretePortMemberIterator *member_iter_; + ConcretePort *next_; +}; + +} // Namespace +#endif diff --git a/network/ConcreteNetwork.cc b/network/ConcreteNetwork.cc new file mode 100644 index 0000000..a681f96 --- /dev/null +++ b/network/ConcreteNetwork.cc @@ -0,0 +1,1979 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "PatternMatch.hh" +#include "Report.hh" +#include "PortDirection.hh" +#include "ConcreteLibrary.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "ConcreteNetwork.hh" + +namespace sta { + +static void +makeChildNetwork(Instance *proto, + 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); + +Network * +makeConcreteNetwork() +{ + return new ConcreteNetwork; +} + +class ConcreteInstanceChildIterator : public InstanceChildIterator +{ +public: + explicit ConcreteInstanceChildIterator(ConcreteInstanceChildMap *map); + bool hasNext(); + Instance *next(); + +private: + ConcreteInstanceChildMap::ConstIterator iter_; +}; + +ConcreteInstanceChildIterator:: +ConcreteInstanceChildIterator(ConcreteInstanceChildMap *map) : + iter_(map) +{ +} + +bool +ConcreteInstanceChildIterator::hasNext() +{ + return iter_.hasNext(); +} + +Instance * +ConcreteInstanceChildIterator::next() +{ + return reinterpret_cast(iter_.next()); +} + +class ConcreteInstanceNetIterator : public InstanceNetIterator +{ +public: + explicit ConcreteInstanceNetIterator(ConcreteInstanceNetMap *nets); + bool hasNext(); + Net *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteInstanceNetIterator); + void findNext(); + + ConcreteInstanceNetMap::Iterator iter_; + ConcreteNet *next_; +}; + +ConcreteInstanceNetIterator:: +ConcreteInstanceNetIterator(ConcreteInstanceNetMap *nets): + iter_(nets), + next_(nullptr) +{ + findNext(); +} + +bool +ConcreteInstanceNetIterator::hasNext() +{ + return next_ != nullptr; +} + +// Skip nets that have been merged. +void +ConcreteInstanceNetIterator::findNext() +{ + while (iter_.hasNext()) { + next_ = iter_.next(); + if (next_->mergedInto() == nullptr) + return; + } + next_ = nullptr; +} + +Net * +ConcreteInstanceNetIterator::next() +{ + ConcreteNet *next = next_; + findNext(); + return reinterpret_cast(next); +} + +//////////////////////////////////////////////////////////////// + +class ConcreteInstancePinIterator : public InstancePinIterator +{ +public: + ConcreteInstancePinIterator(const ConcreteInstance *inst, + int pin_count); + bool hasNext(); + Pin *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteInstancePinIterator); + void findNext(); + + ConcretePin **pins_; + int pin_count_; + int pin_index_; + ConcretePin *next_; +}; + +ConcreteInstancePinIterator:: +ConcreteInstancePinIterator(const ConcreteInstance *inst, + int pin_count) : + pins_(inst->pins_), + pin_count_(pin_count), + pin_index_(0) +{ + findNext(); +} + +bool +ConcreteInstancePinIterator::hasNext() +{ + return next_ != nullptr; +} + +Pin * +ConcreteInstancePinIterator::next() +{ + ConcretePin *next = next_; + findNext(); + return reinterpret_cast(next); +} + +// Skip over missing pins. +void +ConcreteInstancePinIterator::findNext() +{ + while (pin_index_ < pin_count_) { + next_ = pins_[pin_index_++]; + if (next_) + return; + } + next_ = nullptr; +} + +//////////////////////////////////////////////////////////////// + +class ConcreteNetPinIterator : public NetPinIterator +{ +public: + explicit ConcreteNetPinIterator(const ConcreteNet *net); + bool hasNext(); + Pin *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteNetPinIterator); + + ConcretePin *next_; +}; + +ConcreteNetPinIterator::ConcreteNetPinIterator(const ConcreteNet *net) : + next_(net->pins_) +{ +} + +bool +ConcreteNetPinIterator::hasNext() +{ + return next_ != nullptr; +} + +Pin * +ConcreteNetPinIterator::next() +{ + ConcretePin *next = next_; + next_ = next_->net_next_; + return reinterpret_cast(next); +} + +//////////////////////////////////////////////////////////////// + +class ConcreteNetTermIterator : public NetTermIterator +{ +public: + explicit ConcreteNetTermIterator(const ConcreteNet *net); + bool hasNext(); + Term *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteNetTermIterator); + + ConcreteTerm *next_; +}; + +ConcreteNetTermIterator::ConcreteNetTermIterator(const ConcreteNet *net) : + next_(net->terms_) +{ +} + +bool +ConcreteNetTermIterator::hasNext() +{ + return next_ != nullptr; +} + +Term * +ConcreteNetTermIterator::next() +{ + ConcreteTerm *next = next_; + next_ = next_->net_next_; + return reinterpret_cast(next); +} + +//////////////////////////////////////////////////////////////// + +ConcreteNetwork::ConcreteNetwork() : + NetworkReader(), + top_instance_(nullptr), + link_func_(nullptr) +{ +} + +ConcreteNetwork::~ConcreteNetwork() +{ + clear(); +} + +void +ConcreteNetwork::clear() +{ + deleteTopInstance(); + deleteCellNetworkViews(); + library_seq_.deleteContentsClear(); + library_map_.clear(); + Network::clear(); +} + +void +ConcreteNetwork::deleteTopInstance() +{ + if (top_instance_) { + deleteInstance(top_instance_); + top_instance_ = nullptr; + } +} + +void +ConcreteNetwork::deleteCellNetworkViews() +{ + CellNetworkViewMap::Iterator view_iter(cell_network_view_map_); + while (view_iter.hasNext()) { + Instance *view = view_iter.next(); + if (view) + deleteInstance(view); + } + cell_network_view_map_.clear(); +} + +Instance * +ConcreteNetwork::topInstance() const +{ + return top_instance_; +} + +//////////////////////////////////////////////////////////////// + +class ConcreteLibraryIterator1 : public Iterator +{ +public: + explicit ConcreteLibraryIterator1(const ConcreteLibrarySeq &lib_seq_); + virtual bool hasNext(); + virtual Library *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteLibraryIterator1); + + ConcreteLibraryIterator iter_; +}; + +ConcreteLibraryIterator1::ConcreteLibraryIterator1(const ConcreteLibrarySeq &lib_seq_): + iter_(lib_seq_) +{ +} + +bool +ConcreteLibraryIterator1::hasNext() +{ + return iter_.hasNext(); +} + +Library * +ConcreteLibraryIterator1::next() +{ + return reinterpret_cast(iter_.next()); +} + +LibraryIterator * +ConcreteNetwork::libraryIterator() const +{ + return new ConcreteLibraryIterator1(library_seq_); +} + +//////////////////////////////////////////////////////////////// + +class ConcreteLibertyLibraryIterator : public Iterator +{ +public: + explicit ConcreteLibertyLibraryIterator(const ConcreteNetwork *network); + virtual ~ConcreteLibertyLibraryIterator(); + virtual bool hasNext(); + virtual LibertyLibrary *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteLibertyLibraryIterator); + void findNext(); + + ConcreteLibrarySeq::ConstIterator iter_; + LibertyLibrary *next_; +}; + +ConcreteLibertyLibraryIterator:: +ConcreteLibertyLibraryIterator(const ConcreteNetwork *network): + iter_(network->library_seq_), + next_(nullptr) +{ + findNext(); +} + +ConcreteLibertyLibraryIterator::~ConcreteLibertyLibraryIterator() +{ +} + +bool +ConcreteLibertyLibraryIterator::hasNext() +{ + return next_ != nullptr; +} + +LibertyLibrary * +ConcreteLibertyLibraryIterator::next() +{ + LibertyLibrary *next = next_; + findNext(); + return next; +} + +void +ConcreteLibertyLibraryIterator::findNext() +{ + next_ = nullptr; + while (iter_.hasNext()) { + ConcreteLibrary *lib = iter_.next(); + if (lib->isLiberty()) { + LibertyLibrary *liberty = static_cast(lib); + if (liberty) { + next_ = liberty; + break; + } + } + } +} + +LibertyLibraryIterator * +ConcreteNetwork::libertyLibraryIterator() const +{ + return new ConcreteLibertyLibraryIterator(this); +} + +//////////////////////////////////////////////////////////////// + +Library * +ConcreteNetwork::makeLibrary(const char *name, + const char *filename) +{ + ConcreteLibrary *library = new ConcreteLibrary(name, filename, false); + addLibrary(library); + return reinterpret_cast(library); +} + +LibertyLibrary * +ConcreteNetwork::makeLibertyLibrary(const char *name, + const char *filename) +{ + LibertyLibrary *library = new LibertyLibrary(name, filename); + addLibrary(library); + return library; +} + +void +ConcreteNetwork::addLibrary(ConcreteLibrary *library) +{ + library_seq_.push_back(library); + library_map_[library->name()] = library; +} + +Library * +ConcreteNetwork::findLibrary(const char *name) +{ + return reinterpret_cast(library_map_.findKey(name)); +} + +void +ConcreteNetwork::deleteLibrary(ConcreteLibrary *library) +{ + library_map_.erase(library->name()); + library_seq_.eraseObject(library); + delete library; +} + +const char * +ConcreteNetwork::name(const Library *library) const +{ + const ConcreteLibrary *clib = + reinterpret_cast(library); + return clib->name(); +} + +LibertyLibrary * +ConcreteNetwork::findLiberty(const char *name) +{ + ConcreteLibrary *lib = library_map_.findKey(name); + return static_cast(lib); +} + +LibertyLibrary * +ConcreteNetwork::libertyLibrary(Library *library) const +{ + ConcreteLibrary *lib = reinterpret_cast(library); + return static_cast(lib); +} + +Cell * +ConcreteNetwork::makeCell(Library *library, + const char *name, + bool is_leaf, + const char *filename) +{ + ConcreteLibrary *clib = reinterpret_cast(library); + return reinterpret_cast(clib->makeCell(name, is_leaf, filename)); +} + +Cell * +ConcreteNetwork::findCell(const Library *library, + const char *name) const +{ + const ConcreteLibrary *clib = + reinterpret_cast(library); + return reinterpret_cast(clib->findCell(name)); +} + +Cell * +ConcreteNetwork::findAnyCell(const char *name) +{ + ConcreteLibrarySeq::Iterator lib_iter(library_seq_); + while (lib_iter.hasNext()) { + ConcreteLibrary *lib = lib_iter.next(); + ConcreteCell *cell = lib->findCell(name); + if (cell) + return reinterpret_cast(cell); + } + return nullptr; +} + +void +ConcreteNetwork::findCellsMatching(const Library *library, + const PatternMatch *pattern, + CellSeq *cells) const +{ + const ConcreteLibrary *clib = + reinterpret_cast(library); + clib->findCellsMatching(pattern, cells); +} + +void +ConcreteNetwork::deleteCell(Cell *cell) +{ + ConcreteCell *ccell = reinterpret_cast(cell); + ConcreteLibrary *clib = ccell->library(); + clib->deleteCell(ccell); +} + +//////////////////////////////////////////////////////////////// + +const char * +ConcreteNetwork::name(const Cell *cell) const +{ + const ConcreteCell *ccell = reinterpret_cast(cell); + return ccell->name(); +} + +void +ConcreteNetwork::setName(Cell *cell, + const char *name) +{ + ConcreteCell *ccell = reinterpret_cast(cell); + ccell->setName(name); +} + +void +ConcreteNetwork::setIsLeaf(Cell *cell, + bool is_leaf) +{ + ConcreteCell *ccell = reinterpret_cast(cell); + ccell->setIsLeaf(is_leaf); +} + +Library * +ConcreteNetwork::library(const Cell *cell) const +{ + const ConcreteCell *ccell = reinterpret_cast(cell); + return reinterpret_cast(ccell->library()); +} + +LibertyCell * +ConcreteNetwork::libertyCell(Cell *cell) const +{ + ConcreteCell *ccell = reinterpret_cast(cell); + return ccell->libertyCell(); +} + +const LibertyCell * +ConcreteNetwork::libertyCell(const Cell *cell) const +{ + const ConcreteCell *ccell = reinterpret_cast(cell); + return ccell->libertyCell(); +} + +Cell * +ConcreteNetwork::cell(LibertyCell *cell) const +{ + return reinterpret_cast(cell); +} + +const Cell * +ConcreteNetwork::cell(const LibertyCell *cell) const +{ + return reinterpret_cast(cell); +} + +const char * +ConcreteNetwork::filename(const Cell *cell) +{ + const ConcreteCell *ccell = reinterpret_cast(cell); + return ccell->filename(); +} + +Port * +ConcreteNetwork::findPort(const Cell *cell, + const char *name) const +{ + const ConcreteCell *ccell = reinterpret_cast(cell); + return reinterpret_cast(ccell->findPort(name)); +} + +void +ConcreteNetwork::findPortsMatching(const Cell *cell, + const PatternMatch *pattern, + PortSeq *ports) const +{ + const ConcreteCell *ccell = reinterpret_cast(cell); + ccell->findPortsMatching(pattern, ports); +} + +bool +ConcreteNetwork::isLeaf(const Cell *cell) const +{ + const ConcreteCell *ccell = reinterpret_cast(cell); + return ccell->isLeaf(); +} + +Port * +ConcreteNetwork::makePort(Cell *cell, + const char *name) +{ + ConcreteCell *ccell = reinterpret_cast(cell); + ConcretePort *port = ccell->makePort(name); + return reinterpret_cast(port); +} + +Port * +ConcreteNetwork::makeBusPort(Cell *cell, + const char *name, + int from_index, + int to_index) +{ + ConcreteCell *ccell = reinterpret_cast(cell); + ConcretePort *port = ccell->makeBusPort(name, from_index, to_index); + return reinterpret_cast(port); +} + +void +ConcreteNetwork::groupBusPorts(Cell *cell) +{ + Library *lib = library(cell); + ConcreteLibrary *clib = reinterpret_cast(lib); + ConcreteCell *ccell = reinterpret_cast(cell); + ccell->groupBusPorts(clib->busBrktLeft(), clib->busBrktRight()); +} + +Port * +ConcreteNetwork::makeBundlePort(Cell *cell, + const char *name, + PortSeq *members) +{ + ConcreteCell *ccell = reinterpret_cast(cell); + ConcretePortSeq *cmembers = reinterpret_cast(members); + ConcretePort *port = ccell->makeBundlePort(name, cmembers); + return reinterpret_cast(port); +} + +void +ConcreteNetwork::setDirection(Port *port, + PortDirection *dir) +{ + ConcretePort *cport = reinterpret_cast(port); + cport->setDirection(dir); +} + +//////////////////////////////////////////////////////////////// + +class ConcreteCellPortIterator1 : public CellPortIterator +{ +public: + explicit ConcreteCellPortIterator1(const ConcreteCell *cell); + ~ConcreteCellPortIterator1(); + virtual bool hasNext() { return iter_->hasNext(); } + virtual Port *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteCellPortIterator1); + + ConcreteCellPortIterator *iter_; +}; + +ConcreteCellPortIterator1::ConcreteCellPortIterator1(const ConcreteCell *cell): + iter_(cell->portIterator()) +{ +} + +ConcreteCellPortIterator1::~ConcreteCellPortIterator1() +{ + delete iter_; +} + +Port * +ConcreteCellPortIterator1::next() +{ + return reinterpret_cast(iter_->next()); +} + +CellPortIterator * +ConcreteNetwork::portIterator(const Cell *cell) const +{ + const ConcreteCell *ccell = reinterpret_cast(cell); + return new ConcreteCellPortIterator1(ccell); +} + +//////////////////////////////////////////////////////////////// + +class ConcreteCellPortBitIterator1 : public CellPortIterator +{ +public: + explicit ConcreteCellPortBitIterator1(const ConcreteCell *cell); + ~ConcreteCellPortBitIterator1(); + virtual bool hasNext() { return iter_->hasNext(); } + virtual Port *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteCellPortBitIterator1); + + ConcreteCellPortBitIterator *iter_; +}; + +ConcreteCellPortBitIterator1::ConcreteCellPortBitIterator1(const ConcreteCell *cell): + iter_(cell->portBitIterator()) +{ +} + +ConcreteCellPortBitIterator1::~ConcreteCellPortBitIterator1() +{ + delete iter_; +} + +Port * +ConcreteCellPortBitIterator1::next() +{ + return reinterpret_cast(iter_->next()); +} + +CellPortBitIterator * +ConcreteNetwork::portBitIterator(const Cell *cell) const +{ + const ConcreteCell *ccell = reinterpret_cast(cell); + return new ConcreteCellPortBitIterator1(ccell); +} + +int +ConcreteNetwork::portBitCount(const Cell *cell) const +{ + const ConcreteCell *ccell = reinterpret_cast(cell); + return ccell->portBitCount(); +} + +//////////////////////////////////////////////////////////////// + +const char * +ConcreteNetwork::name(const Port *port) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return cport->name(); +} + +Cell * +ConcreteNetwork::cell(const Port *port) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return cport->cell(); +} + +LibertyPort * +ConcreteNetwork::libertyPort(const Port *port) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return cport->libertyPort(); +} + +PortDirection * +ConcreteNetwork::direction(const Port *port) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return cport->direction(); +} + +bool +ConcreteNetwork::isBundle(const Port *port) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return cport->isBundle(); +} + +bool +ConcreteNetwork::isBus(const Port *port) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return cport->isBus(); +} + +const char * +ConcreteNetwork::busName(const Port *port) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return cport->busName(); +} + +int +ConcreteNetwork::size(const Port *port) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return cport->size(); +} + +int +ConcreteNetwork::fromIndex(const Port *port) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return cport->fromIndex(); +} + +int +ConcreteNetwork::toIndex(const Port *port) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return cport->toIndex(); +} + +Port * +ConcreteNetwork::findBusBit(const Port *port, + int index) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return reinterpret_cast(cport->findBusBit(index)); +} + +Port * +ConcreteNetwork::findMember(const Port *port, + int index) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return reinterpret_cast(cport->findMember(index)); +} + +bool +ConcreteNetwork::hasMembers(const Port *port) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return cport->hasMembers(); +} + +//////////////////////////////////////////////////////////////// + +class ConcretePortMemberIterator1 : public PortMemberIterator +{ +public: + explicit ConcretePortMemberIterator1(const ConcretePort *port); + ~ConcretePortMemberIterator1(); + virtual bool hasNext() { return iter_->hasNext(); } + virtual Port *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(ConcretePortMemberIterator1); + + ConcretePortMemberIterator *iter_; +}; + +ConcretePortMemberIterator1::ConcretePortMemberIterator1(const ConcretePort * + port) : + iter_(port->memberIterator()) +{ +} + +ConcretePortMemberIterator1::~ConcretePortMemberIterator1() +{ + delete iter_; +} + +Port * +ConcretePortMemberIterator1::next() +{ + return reinterpret_cast(iter_->next()); +} + +PortMemberIterator * +ConcreteNetwork::memberIterator(const Port *port) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return new ConcretePortMemberIterator1(cport); +} + +//////////////////////////////////////////////////////////////// + +const char * +ConcreteNetwork::name(const Instance *instance) const +{ + const ConcreteInstance *inst = + reinterpret_cast(instance); + return inst->name(); +} + +Cell * +ConcreteNetwork::cell(const Instance *instance) const +{ + const ConcreteInstance *inst = + reinterpret_cast(instance); + return inst->cell(); +} + +Instance * +ConcreteNetwork::parent(const Instance *instance) const +{ + const ConcreteInstance *inst = + reinterpret_cast(instance); + return reinterpret_cast(inst->parent()); +} + +bool +ConcreteNetwork::isLeaf(const Instance *instance) const +{ + const ConcreteInstance *inst = + reinterpret_cast(instance); + ConcreteCell *ccell = reinterpret_cast(inst->cell()); + return ccell->isLeaf(); +} + +Instance * +ConcreteNetwork::findChild(const Instance *parent, + const char *name) const +{ + const ConcreteInstance *inst = + reinterpret_cast(parent); + return inst->findChild(name); +} + +Pin * +ConcreteNetwork::findPin(const Instance *instance, + const char *port_name) const +{ + const ConcreteInstance *inst = + reinterpret_cast(instance); + return reinterpret_cast(inst->findPin(port_name)); +} + +Pin * +ConcreteNetwork::findPin(const Instance *instance, + const Port *port) const +{ + const ConcreteInstance *inst = + reinterpret_cast(instance); + return reinterpret_cast(inst->findPin(port)); +} + +Net * +ConcreteNetwork::findNet(const Instance *instance, + const char *net_name) const +{ + const ConcreteInstance *inst = + reinterpret_cast(instance); + return reinterpret_cast(inst->findNet(net_name)); +} + +void +ConcreteNetwork::findInstNetsMatching(const Instance *instance, + const PatternMatch *pattern, + NetSeq *nets) const +{ + const ConcreteInstance *inst = + reinterpret_cast(instance); + inst->findNetsMatching(pattern, nets); +} + +//////////////////////////////////////////////////////////////// + +InstanceChildIterator * +ConcreteNetwork::childIterator(const Instance *instance) const +{ + const ConcreteInstance *inst = + reinterpret_cast(instance); + return inst->childIterator(); +} + +InstancePinIterator * +ConcreteNetwork::pinIterator(const Instance *instance) const +{ + const ConcreteInstance *inst = + reinterpret_cast(instance); + ConcreteCell *cell = reinterpret_cast(inst->cell()); + int pin_count = cell->portBitCount(); + return new ConcreteInstancePinIterator(inst, pin_count); +} + +InstanceNetIterator * +ConcreteNetwork::netIterator(const Instance *instance) const +{ + const ConcreteInstance *inst = + reinterpret_cast(instance); + return inst->netIterator(); +} + +//////////////////////////////////////////////////////////////// + +Instance * +ConcreteNetwork::instance(const Pin *pin) const +{ + const ConcretePin *cpin = reinterpret_cast(pin); + return reinterpret_cast(cpin->instance()); +} + +Net * +ConcreteNetwork::net(const Pin *pin) const +{ + const ConcretePin *cpin = reinterpret_cast(pin); + return reinterpret_cast(cpin->net()); +} + +Term * +ConcreteNetwork::term(const Pin *pin) const +{ + const ConcretePin *cpin = reinterpret_cast(pin); + return reinterpret_cast(cpin->term()); +} + +Port * +ConcreteNetwork::port(const Pin *pin) const +{ + const ConcretePin *cpin = reinterpret_cast(pin); + return reinterpret_cast(cpin->port()); +} + +PortDirection * +ConcreteNetwork::direction(const Pin *pin) const +{ + const ConcretePin *cpin = reinterpret_cast(pin); + const ConcretePort *cport = cpin->port(); + return cport->direction(); +} + +VertexIndex +ConcreteNetwork::vertexIndex(const Pin *pin) const +{ + const ConcretePin *cpin = reinterpret_cast(pin); + return cpin->vertexIndex(); +} + +void +ConcreteNetwork::setVertexIndex(Pin *pin, + VertexIndex index) +{ + ConcretePin *cpin = reinterpret_cast(pin); + cpin->setVertexIndex(index); +} + +//////////////////////////////////////////////////////////////// + +Net * +ConcreteNetwork::net(const Term *term) const +{ + const ConcreteTerm *cterm = reinterpret_cast(term); + return reinterpret_cast(cterm->net()); +} + +Pin * +ConcreteNetwork::pin(const Term *term) const +{ + const ConcreteTerm *cterm = reinterpret_cast(term); + return reinterpret_cast(cterm->pin()); +} + +//////////////////////////////////////////////////////////////// + +const char * +ConcreteNetwork::name(const Net *net) const +{ + const ConcreteNet *cnet = reinterpret_cast(net); + return cnet->name(); +} + +Instance * +ConcreteNetwork::instance(const Net *net) const +{ + const ConcreteNet *cnet = reinterpret_cast(net); + return reinterpret_cast(cnet->instance()); +} + +bool +ConcreteNetwork::isPower(const Net *net) const +{ + return constant_nets_[int(LogicValue::one)].hasKey(const_cast(net)); +} + +bool +ConcreteNetwork::isGround(const Net *net) const +{ + return constant_nets_[int(LogicValue::zero)].hasKey(const_cast(net)); +} + +NetPinIterator * +ConcreteNetwork::pinIterator(const Net *net) const +{ + const ConcreteNet *cnet = reinterpret_cast(net); + return new ConcreteNetPinIterator(cnet); +} + +NetTermIterator * +ConcreteNetwork::termIterator(const Net *net) const +{ + const ConcreteNet *cnet = reinterpret_cast(net); + return new ConcreteNetTermIterator(cnet); +} + +void +ConcreteNetwork::mergeInto(Net *net, + Net *into_net) +{ + ConcreteNet *cnet = reinterpret_cast(net); + ConcreteNet *cinto_net = reinterpret_cast(into_net); + cnet->mergeInto(cinto_net); + clearNetDrvPinrMap(); +} + +Net * +ConcreteNetwork::mergedInto(Net *net) +{ + ConcreteNet *cnet = reinterpret_cast(net); + return reinterpret_cast(cnet->mergedInto()); +} + +//////////////////////////////////////////////////////////////// + +Cell * +ConcreteInstance::cell() const +{ + return reinterpret_cast(cell_); +} + +Instance * +ConcreteNetwork::makeInstance(Cell *cell, + const char *name, + Instance *parent) +{ + ConcreteCell *ccell = reinterpret_cast(cell); + return makeConcreteInstance(ccell, name, parent); +} + +Instance * +ConcreteNetwork::makeInstance(LibertyCell *cell, + const char *name, + Instance *parent) +{ + return makeConcreteInstance(cell, name, parent); +} + +Instance * +ConcreteNetwork::makeConcreteInstance(ConcreteCell *cell, + const char *name, + Instance *parent) +{ + ConcreteInstance *cparent = + reinterpret_cast(parent); + ConcreteInstance *inst = new ConcreteInstance(cell, name, cparent); + if (parent) + cparent->addChild(inst); + return reinterpret_cast(inst); +} + +void +ConcreteNetwork::makePins(Instance *inst) +{ + CellPortBitIterator *port_iterator = portBitIterator(cell(inst)); + while (port_iterator->hasNext()) { + Port *port = port_iterator->next(); + makePin(inst, port, nullptr); + } + delete port_iterator; +} + +void +ConcreteNetwork::replaceCell(Instance *inst, + Cell *cell) +{ + ConcreteCell *ccell = reinterpret_cast(cell); + int port_count = ccell->portBitCount(); + ConcreteInstance *cinst = reinterpret_cast(inst); + ConcretePin **pins = cinst->pins_; + ConcretePin **rpins = new ConcretePin*[port_count]; + for (int i = 0; i < port_count; i++) + rpins[i] = nullptr; + for (int i = 0; i < port_count; i++) { + ConcretePin *cpin = pins[i]; + if (cpin) { + ConcretePort *pin_cport = reinterpret_cast(cpin->port()); + ConcretePort *cport = ccell->findPort(pin_cport->name()); + rpins[cport->pinIndex()] = cpin; + cpin->port_ = cport; + } + } + delete [] pins; + cinst->pins_ = rpins; + cinst->setCell(ccell); +} + +void +ConcreteNetwork::deleteInstance(Instance *inst) +{ + ConcreteInstance *cinst = reinterpret_cast(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; + } + delete term_iter; + deleteNet(net); + } + + // Delete children. + InstanceChildIterator *child_iter = childIterator(inst); + while (child_iter->hasNext()) { + Instance *child = child_iter->next(); + deleteInstance(child); + } + delete child_iter; + + InstancePinIterator *pin_iter = pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + deletePin(pin); + } + delete pin_iter; + + Instance *parent_inst = parent(inst); + if (parent_inst) { + ConcreteInstance *cparent = + reinterpret_cast(parent_inst); + cparent->deleteChild(cinst); + } + delete cinst; +} + +Pin * +ConcreteNetwork::makePin(Instance *inst, + Port *port, + Net *net) +{ + ConcreteInstance *cinst = reinterpret_cast(inst); + ConcretePort *cport = reinterpret_cast(port); + ConcreteNet *cnet = reinterpret_cast(net); + ConcretePin *cpin = new ConcretePin(cinst, cport, cnet); + cinst->addPin(cpin); + if (cnet) + connectNetPin(cnet, cpin); + return reinterpret_cast(cpin); +} + +Term * +ConcreteNetwork::makeTerm(Pin *pin, + Net *net) +{ + ConcretePin *cpin = reinterpret_cast(pin); + ConcreteNet *cnet = reinterpret_cast(net); + ConcreteTerm *cterm = new ConcreteTerm(cpin, cnet); + if (cnet) + cnet->addTerm(cterm); + cpin->term_ = cterm; + return reinterpret_cast(cterm); +} + +Pin * +ConcreteNetwork::connect(Instance *inst, + LibertyPort *port, + Net *net) +{ + return connect(inst, reinterpret_cast(port), net); +} + +Pin * +ConcreteNetwork::connect(Instance *inst, + Port *port, + Net *net) +{ + ConcreteNet *cnet = reinterpret_cast(net); + ConcreteInstance *cinst = reinterpret_cast(inst); + ConcretePort *cport = reinterpret_cast(port); + ConcretePin *cpin = cinst->findPin(port); + if (cpin) { + ConcreteNet *prev_net = cpin->net_; + if (prev_net) + disconnectNetPin(prev_net, cpin); + } + else { + cpin = new ConcretePin(cinst, cport, cnet); + cinst->addPin(cpin); + } + if (inst == top_instance_) { + // makeTerm + ConcreteTerm *cterm = new ConcreteTerm(cpin, cnet); + cnet->addTerm(cterm); + cpin->term_ = cterm; + cpin->net_ = nullptr; + } + else { + cpin->net_ = cnet; + connectNetPin(cnet, cpin); + } + return reinterpret_cast(cpin); +} + +void +ConcreteNetwork::connectNetPin(ConcreteNet *cnet, + ConcretePin *cpin) +{ + cnet->addPin(cpin); + + // If there are no terminals the net does not span hierarchy levels + // and it is safe to incrementally update the drivers. + Pin *pin = reinterpret_cast(cpin); + if (isDriver(pin)) { + if (cnet->terms_ == nullptr) { + Net *net = reinterpret_cast(cnet); + PinSet *drvrs = net_drvr_pin_map_.findKey(net); + if (drvrs) + drvrs->insert(pin); + } + else + clearNetDrvPinrMap(); + } +} + +void +ConcreteNetwork::disconnectPin(Pin *pin) +{ + ConcretePin *cpin = reinterpret_cast(pin); + if (reinterpret_cast(cpin->instance()) == top_instance_) { + ConcreteTerm *cterm = cpin->term_; + if (cterm) { + ConcreteNet *cnet = cterm->net_; + if (cnet) { + cnet->deleteTerm(cterm); + clearNetDrvPinrMap(); + } + cpin->term_ = nullptr; + delete cterm; + } + } + else { + ConcreteNet *cnet = cpin->net(); + if (cnet) + disconnectNetPin(cnet, cpin); + cpin->net_ = nullptr; + } +} + +void +ConcreteNetwork::disconnectNetPin(ConcreteNet *cnet, + ConcretePin *cpin) +{ + cnet->deletePin(cpin); + + Pin *pin = reinterpret_cast(cpin); + if (isDriver(pin)) { + ConcreteNet *cnet = cpin->net(); + // If there are no terminals the net does not span hierarchy levels + // 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); + if (drvrs) + drvrs->erase(pin); + } + else + clearNetDrvPinrMap(); + } +} + +void +ConcreteNetwork::deletePin(Pin *pin) +{ + ConcretePin *cpin = reinterpret_cast(pin); + ConcreteNet *cnet = cpin->net(); + if (cnet) + disconnectNetPin(cnet, cpin); + ConcreteInstance *cinst = + reinterpret_cast(cpin->instance()); + if (cinst) + cinst->deletePin(cpin); + delete cpin; +} + +Net * +ConcreteNetwork::makeNet(const char *name, + Instance *parent) +{ + ConcreteInstance *cparent = reinterpret_cast(parent); + ConcreteNet *net = new ConcreteNet(name, cparent); + cparent->addNet(net); + return reinterpret_cast(net); +} + +void +ConcreteNetwork::deleteNet(Net *net) +{ + ConcreteNet *cnet = reinterpret_cast(net); + ConcreteNetPinIterator pin_iter(cnet); + while (pin_iter.hasNext()) { + ConcretePin *pin = reinterpret_cast(pin_iter.next()); + // Do NOT use net->disconnectPin because it would be N^2 + // to delete all of the pins from the 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); + if (drvrs) { + delete drvrs; + net_drvr_pin_map_.erase(net); + } + + ConcreteInstance *cinst = + reinterpret_cast(cnet->instance()); + cinst->deleteNet(cnet); + delete cnet; +} + +void +ConcreteNetwork::clearConstantNets() +{ + constant_nets_[int(LogicValue::zero)].clear(); + constant_nets_[int(LogicValue::one)].clear(); +} + +void +ConcreteNetwork::addConstantNet(Net *net, + LogicValue value) +{ + constant_nets_[int(value)].insert(net); +} + +ConstantPinIterator * +ConcreteNetwork::constantPinIterator() +{ + return new NetworkConstantPinIterator(this, + constant_nets_[int(LogicValue::zero)], + constant_nets_[int(LogicValue::one)]); +} + +//////////////////////////////////////////////////////////////// + +// Optimized version of Network::visitConnectedPins. +void +ConcreteNetwork::visitConnectedPins(const Net *net, + PinVisitor &visitor, + ConstNetSet &visited_nets) const +{ + if (!visited_nets.hasKey(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)); + } + } + + // Search down from net pins. + for (ConcretePin *pin = cnet->pins_; pin; pin = pin->net_next_) { + 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); + } + } + } +} + +//////////////////////////////////////////////////////////////// + +ConcreteInstance::ConcreteInstance(ConcreteCell *cell, + const char *name, + ConcreteInstance *parent) : + cell_(cell), + name_(stringCopy(name)), + parent_(parent), + children_(nullptr), + nets_(nullptr) +{ + initPins(); +} + +void +ConcreteInstance::initPins() +{ + int pin_count = reinterpret_cast(cell_)->portBitCount(); + if (pin_count) { + pins_ = new ConcretePin*[pin_count]; + for (int i = 0; i < pin_count; i++) + pins_[i] = nullptr; + } + else + pins_ = nullptr; +} + +ConcreteInstance::~ConcreteInstance() +{ + stringDelete(name_); + delete [] pins_; + delete children_; + delete nets_; +} + +Instance * +ConcreteInstance::findChild(const char *name) const +{ + if (children_) + return reinterpret_cast(children_->findKey(name)); + else + return nullptr; +} + +ConcretePin * +ConcreteInstance::findPin(const char *port_name) const +{ + ConcreteCell *ccell = reinterpret_cast(cell_); + const ConcretePort *cport = + reinterpret_cast(ccell->findPort(port_name)); + if (cport + && !cport->isBus()) + return pins_[cport->pinIndex()]; + else + return nullptr; +} + +ConcretePin * +ConcreteInstance::findPin(const Port *port) const +{ + const ConcretePort *cport = reinterpret_cast(port); + return pins_[cport->pinIndex()]; +} + +ConcreteNet * +ConcreteInstance::findNet(const char *net_name) const +{ + ConcreteNet *net = nullptr; + if (nets_) { + net = nets_->findKey(net_name); + // Follow merge pointer to surviving net. + if (net) { + while (net->mergedInto()) + net = net->mergedInto(); + } + } + return net; +} + +void +ConcreteInstance::findNetsMatching(const PatternMatch *pattern, + NetSeq *nets) 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)) + nets->push_back(reinterpret_cast(cnet)); + } + } + else { + ConcreteNet *cnet = findNet(pattern->pattern()); + if (cnet) + nets->push_back(reinterpret_cast(cnet)); + } +} + +InstanceNetIterator * +ConcreteInstance::netIterator() const +{ + return reinterpret_cast + (new ConcreteInstanceNetIterator(nets_)); +} + +InstanceChildIterator * +ConcreteInstance::childIterator() const +{ + return new ConcreteInstanceChildIterator(children_); +} + +void +ConcreteInstance::addChild(ConcreteInstance *child) +{ + if (children_ == nullptr) + children_ = new ConcreteInstanceChildMap; + (*children_)[child->name()] = child; +} + +void +ConcreteInstance::deleteChild(ConcreteInstance *child) +{ + children_->erase(child->name()); +} + +void +ConcreteInstance::addPin(ConcretePin *pin) +{ + ConcretePort *cport = reinterpret_cast(pin->port()); + pins_[cport->pinIndex()] = pin; +} + +void +ConcreteInstance::deletePin(ConcretePin *pin) +{ + ConcretePort *cport = reinterpret_cast(pin->port()); + pins_[cport->pinIndex()] = nullptr; +} + +void +ConcreteInstance::addNet(ConcreteNet *net) +{ + if (nets_ == nullptr) + nets_ = new ConcreteInstanceNetMap; + (*nets_)[net->name()] = net; +} + +void +ConcreteInstance::addNet(const char *name, + ConcreteNet *net) +{ + if (nets_ == nullptr) + nets_ = new ConcreteInstanceNetMap; + (*nets_)[name] = net; +} + +void +ConcreteInstance::deleteNet(ConcreteNet *net) +{ + nets_->erase(net->name()); +} + +void +ConcreteInstance::setCell(ConcreteCell *cell) +{ + cell_ = cell; +} + +//////////////////////////////////////////////////////////////// + +ConcretePin::ConcretePin(ConcreteInstance *instance, + ConcretePort *port, + ConcreteNet *net) : + instance_(instance), + port_(port), + net_(net), + term_(nullptr), + net_next_(nullptr), + net_prev_(nullptr), + vertex_index_(0) +{ +} + +const char * +ConcretePin::name() const +{ + return port_->name(); +} + +void +ConcretePin::setVertexIndex(VertexIndex index) +{ + vertex_index_ = index; +} + +//////////////////////////////////////////////////////////////// + +const char * +ConcreteTerm::name() const +{ + ConcretePin *cpin = reinterpret_cast(pin_); + const ConcretePort *cport = + reinterpret_cast(cpin->port()); + return cport->name(); +} + +ConcreteTerm::ConcreteTerm(ConcretePin *pin, + ConcreteNet *net) : + pin_(pin), + net_(net), + net_next_(nullptr) +{ +} + +//////////////////////////////////////////////////////////////// + +ConcreteNet::ConcreteNet(const char *name, + ConcreteInstance *instance) : + name_(stringCopy(name)), + instance_(instance), + pins_(nullptr), + terms_(nullptr), + merged_into_(nullptr) +{ +} + +ConcreteNet::~ConcreteNet() +{ + stringDelete(name_); +} + +// Merged nets are kept around to serve as name aliases. +// Only Instance::findNet and InstanceNetIterator need to know +// the net has been merged. +void +ConcreteNet::mergeInto(ConcreteNet *net) +{ + ConcreteNetPinIterator pin_iter(this); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + ConcretePin *cpin = reinterpret_cast(pin); + net->addPin(cpin); + cpin->net_ = net; + } + pins_ = nullptr; + ConcreteNetTermIterator term_iter(this); + while (term_iter.hasNext()) { + Term *term = term_iter.next(); + ConcreteTerm *cterm = reinterpret_cast(term); + net->addTerm(cterm); + cterm->net_ = net; + } + terms_ = nullptr; + // Leave name map pointing to merged net because otherwise a top + // level merged net has no pointer to it and it is leaked. + merged_into_ = net; +} + +void +ConcreteNet::addPin(ConcretePin *pin) +{ + if (pins_) + pins_->net_prev_ = pin; + pin->net_next_ = pins_; + pin->net_prev_ = nullptr; + pins_ = pin; +} + +void +ConcreteNet::deletePin(ConcretePin *pin) +{ + ConcretePin *prev = pin->net_prev_; + ConcretePin *next = pin->net_next_; + if (prev) + prev->net_next_ = next; + if (next) + next->net_prev_ = prev; + if (pins_ == pin) + pins_ = next; +} + +void +ConcreteNet::addTerm(ConcreteTerm *term) +{ + ConcreteTerm *next = terms_; + terms_ = term; + term->net_next_ = next; +} + +void +ConcreteNet::deleteTerm(ConcreteTerm *term) +{ + ConcreteTerm *net_prev_term = nullptr; + 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_; + else + terms_ = term->net_next_; + break; + } + net_prev_term = net_term; + } +} + +//////////////////////////////////////////////////////////////// + +typedef Map BindingMap; + +// Binding table used for linking/expanding network. +class ConcreteBindingTbl +{ +public: + explicit ConcreteBindingTbl(NetworkEdit *network); + Net *ensureBinding(Net *proto_net, + Instance *parent); + Net *find(Net *net); + void bind(Net *proto_net, + Net *clone_net); + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteBindingTbl); + + BindingMap map_; + NetworkEdit *network_; +}; + +void +ConcreteNetwork::setCellNetworkView(Cell *cell, + Instance *inst) +{ + cell_network_view_map_[cell] = inst; +} + +Instance * +ConcreteNetwork::cellNetworkView(Cell *cell) +{ + return cell_network_view_map_.findKey(cell); +} + +void +ConcreteNetwork::readNetlistBefore() +{ + clearConstantNets(); + deleteTopInstance(); + clearNetDrvPinrMap(); +} + +void +ConcreteNetwork::setTopInstance(Instance *top_inst) +{ + if (top_instance_) { + deleteInstance(top_instance_); + clearConstantNets(); + clearNetDrvPinrMap(); + } + top_instance_ = top_inst; +} + +void +ConcreteNetwork::setLinkFunc(LinkNetworkFunc *link) +{ + link_func_ = link; +} + +bool +ConcreteNetwork::linkNetwork(const char *top_cell_name, + bool make_black_boxes, + Report *report) +{ + if (link_func_) { + clearConstantNets(); + deleteTopInstance(); + top_instance_ = link_func_(top_cell_name, make_black_boxes, report, this); + return top_instance_ != nullptr; + } + else { + report->error("cell type %s can not be linked.\n", top_cell_name); + return false; + } +} + +Instance * +linkReaderNetwork(Cell *top_cell, + bool, Report *, + NetworkReader *network) +{ + Instance *view = network->cellNetworkView(top_cell); + if (view) { + // Seed the recursion for expansion with the top level instance. + Instance *top_instance = network->makeInstance(top_cell, "", nullptr); + ConcreteBindingTbl bindings(network); + makeClonePins(view, top_instance, view, &bindings, nullptr, nullptr, network); + InstanceChildIterator *child_iter = network->childIterator(view); + while (child_iter->hasNext()) { + Instance *child = child_iter->next(); + makeChildNetwork(child, top_instance, &bindings, network); + } + delete child_iter; + network->deleteCellNetworkViews(); + return top_instance; + } + return nullptr; +} + +static void +makeChildNetwork(Instance *proto, + Instance *parent, + ConcreteBindingTbl *parent_bindings, + NetworkReader *network) +{ + Cell *proto_cell = network->cell(proto); + Instance *clone = network->makeInstance(proto_cell, network->name(proto), + parent); + if (network->isLeaf(proto_cell)) + makeClonePins(proto, clone, nullptr, nullptr, parent, parent_bindings, network); + else { + // Recurse if this isn't a leaf cell. + ConcreteBindingTbl bindings(network); + Instance *clone_view = network->cellNetworkView(proto_cell); + makeClonePins(proto, clone, clone_view, &bindings, parent, + 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); + } + delete child_iter; + } + } +} + +static void +makeClonePins(Instance *proto, + 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()) { + Pin *proto_pin = proto_pin_iter->next(); + Net *proto_net = network->net(proto_pin); + Port *proto_port = network->port(proto_pin); + Net *clone_net = nullptr; + if (proto_net && parent_bindings) + clone_net = parent_bindings->ensureBinding(proto_net, parent); + Pin *clone_pin = network->connect(clone, proto_port, clone_net); + if (clone_view) { + Pin *clone_proto_pin = network->findPin(clone_view, proto_port); + 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); + network->makeTerm(clone_pin, clone_child_net); + } + } + delete proto_pin_iter; +} + +//////////////////////////////////////////////////////////////// + +ConcreteBindingTbl::ConcreteBindingTbl(NetworkEdit *network) : + network_(network) +{ +} + +// Follow the merged_into pointers rather than update the +// binding tables up the call tree when nodes are merged +// because the name changes up the hierarchy. +Net * +ConcreteBindingTbl::find(Net *proto_net) +{ + ConcreteNet *net = reinterpret_cast(map_.findKey(proto_net)); + while (net && net->mergedInto()) + net = net->mergedInto(); + return reinterpret_cast(net); +} + +void +ConcreteBindingTbl::bind(Net *proto_net, + Net *net) +{ + map_[proto_net] = net; +} + +Net * +ConcreteBindingTbl::ensureBinding(Net *proto_net, + Instance *parent) +{ + Net *net = find(proto_net); + if (net == nullptr) { + net = network_->makeNet(network_->name(proto_net), parent); + map_[proto_net] = net; + } + return net; +} + +} // namespace diff --git a/network/ConcreteNetwork.hh b/network/ConcreteNetwork.hh new file mode 100644 index 0000000..87f68f9 --- /dev/null +++ b/network/ConcreteNetwork.hh @@ -0,0 +1,405 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CONCRETE_NETWORK_H +#define STA_CONCRETE_NETWORK_H + +#include "DisallowCopyAssign.hh" +#include "Map.hh" +#include "Set.hh" +#include "StringUtil.hh" +#include "Network.hh" +#include "LibertyClass.hh" + +namespace sta { + +class Report; +class ConcreteLibrary; +class ConcreteCell; +class ConcretePin; +class ConcreteInstance; +class ConcreteNet; +class ConcreteTerm; +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 Map CellNetworkViewMap; +typedef Set ConcreteNetSet; + +// This adapter implements the network api for the concrete network. +// A superset of the Network api methods are implemented in the interface. +class ConcreteNetwork : public NetworkReader +{ +public: + ConcreteNetwork(); + virtual ~ConcreteNetwork(); + virtual void clear(); + virtual bool linkNetwork(const char *top_cell_name, + bool make_black_boxes, + Report *report); + virtual Instance *topInstance() const; + + virtual LibraryIterator *libraryIterator() const; + virtual LibertyLibraryIterator *libertyLibraryIterator() const ; + virtual Library *findLibrary(const char *name); + virtual const char *name(const Library *library) const; + virtual LibertyLibrary *findLiberty(const char *name); + virtual LibertyLibrary *libertyLibrary(Library *library) const; + virtual Cell *findCell(const Library *library, + const char *name) const; + virtual Cell *findAnyCell(const char *name); + virtual void findCellsMatching(const Library *library, + const PatternMatch *pattern, + CellSeq *cells) const; + + virtual const char *name(const Cell *cell) const; + virtual Library *library(const Cell *cell) const; + virtual LibertyCell *libertyCell(Cell *cell) const; + virtual const LibertyCell *libertyCell(const Cell *cell) const; + virtual Cell *cell(LibertyCell *cell) const; + virtual const Cell *cell(const LibertyCell *cell) const; + virtual const char *filename(const Cell *cell); + virtual Port *findPort(const Cell *cell, + const char *name) const; + virtual void findPortsMatching(const Cell *cell, + const PatternMatch *pattern, + PortSeq *ports) const; + virtual bool isLeaf(const Cell *cell) const; + virtual CellPortIterator *portIterator(const Cell *cell) const; + virtual CellPortBitIterator *portBitIterator(const Cell *cell) const; + virtual int portBitCount(const Cell *cell) const; + + virtual const char *name(const Port *port) const; + virtual Cell *cell(const Port *port) const; + virtual LibertyPort *libertyPort(const Port *port) const; + virtual PortDirection *direction(const Port *port) const; + virtual bool isBundle(const Port *port) const; + virtual bool hasMembers(const Port *port) const; + + virtual bool isBus(const Port *port) const; + virtual int size(const Port *port) const; + virtual const char *busName(const Port *port) const; + virtual Port *findBusBit(const Port *port, + int index) const; + virtual int fromIndex(const Port *port) const; + virtual int toIndex(const Port *port) const; + virtual Port *findMember(const Port *port, + int index) const; + virtual PortMemberIterator *memberIterator(const Port *port) const; + + virtual const char *name(const Instance *instance) const; + virtual Cell *cell(const Instance *instance) const; + virtual Instance *parent(const Instance *instance) const; + virtual bool isLeaf(const Instance *instance) const; + virtual Instance *findChild(const Instance *parent, + const char *name) const; + virtual Pin *findPin(const Instance *instance, + const char *port_name) const; + virtual Pin *findPin(const Instance *instance, + const Port *port) const; + + virtual InstanceChildIterator * + childIterator(const Instance *instance) const; + virtual InstancePinIterator * + pinIterator(const Instance *instance) const; + virtual InstanceNetIterator * + netIterator(const Instance *instance) const; + + virtual Instance *instance(const Pin *pin) const; + virtual Net *net(const Pin *pin) const; + virtual Term *term(const Pin *pin) const; + virtual Port *port(const Pin *pin) const; + virtual PortDirection *direction(const Pin *pin) const; + virtual VertexIndex vertexIndex(const Pin *pin) const; + virtual void setVertexIndex(Pin *pin, + VertexIndex index); + + virtual Net *net(const Term *term) const; + virtual Pin *pin(const Term *term) const; + + virtual Net *findNet(const Instance *instance, + const char *net_name) const; + virtual void findInstNetsMatching(const Instance *instance, + const PatternMatch *pattern, + NetSeq *nets) const; + virtual const char *name(const Net *net) const; + virtual Instance *instance(const Net *net) const; + virtual bool isPower(const Net *net) const; + virtual bool isGround(const Net *net) const; + virtual NetPinIterator *pinIterator(const Net *net) const; + virtual NetTermIterator *termIterator(const Net *net) const; + virtual void mergeInto(Net *net, + Net *into_net); + virtual Net *mergedInto(Net *net); + + virtual ConstantPinIterator *constantPinIterator(); + void addConstantNet(Net *net, + LogicValue value); + + // Edit methods. + virtual Library *makeLibrary(const char *name, + const char *filename); + virtual LibertyLibrary *makeLibertyLibrary(const char *name, + const char *filename); + virtual Cell *makeCell(Library *library, + const char *name, + bool is_leaf, + const char *filename); + virtual void deleteCell(Cell *cell); + virtual void setName(Cell *cell, + const char *name); + virtual void setIsLeaf(Cell *cell, + bool is_leaf); + virtual Port *makePort(Cell *cell, + const char *name); + virtual Port *makeBusPort(Cell *cell, + const char *name, + int from_index, + int to_index); + virtual void groupBusPorts(Cell *cell); + virtual Port *makeBundlePort(Cell *cell, + const char *name, + PortSeq *members); + virtual void setDirection(Port *port, + PortDirection *dir); + // For NetworkEdit. + virtual Instance *makeInstance(LibertyCell *cell, + const char *name, + Instance *parent); + void makePins(Instance *inst); + // For linking. + virtual Instance *makeInstance(Cell *cell, + const char *name, + Instance *parent); + virtual void replaceCell(Instance *inst, + Cell *cell); + virtual void deleteInstance(Instance *inst); + virtual Pin *connect(Instance *inst, + Port *port, + Net *net); + virtual Pin *connect(Instance *inst, + LibertyPort *port, + Net *net); + virtual void disconnectPin(Pin *pin); + virtual void deletePin(Pin *pin); + virtual Net *makeNet(const char *name, + Instance *parent); + virtual void deleteNet(Net *net); + + // For NetworkReader API. + virtual Term *makeTerm(Pin *pin, + Net *net); + virtual Pin *makePin(Instance *inst, + Port *port, + Net *net); + + // Instance is the network view for cell. + virtual void setCellNetworkView(Cell *cell, + Instance *inst); + virtual Instance *cellNetworkView(Cell *cell); + virtual void deleteCellNetworkViews(); + void deleteTopInstance(); + + virtual void readNetlistBefore(); + virtual void setLinkFunc(LinkNetworkFunc *link); + void setTopInstance(Instance *top_inst); + + using Network::netIterator; + using Network::findPin; + using Network::findNet; + using Network::findNetsMatching; + using Network::libertyLibrary; + using Network::libertyCell; + using Network::libertyPort; + using Network::isLeaf; + +protected: + void addLibrary(ConcreteLibrary *library); + void deleteLibrary(ConcreteLibrary *library); + void setName(const char *name); + void clearConstantNets(); + virtual void visitConnectedPins(const Net *net, + PinVisitor &visitor, + ConstNetSet &visited_nets) const; + Instance *makeConcreteInstance(ConcreteCell *cell, + const char *name, + Instance *parent); + void disconnectNetPin(ConcreteNet *cnet, + ConcretePin *cpin); + void connectNetPin(ConcreteNet *cnet, + ConcretePin *cpin); + + // Cell lookup search order sequence. + ConcreteLibrarySeq library_seq_; + ConcreteLibraryMap library_map_; + Instance *top_instance_; + NetSet constant_nets_[2]; // LogicValue::zero/one + LinkNetworkFunc *link_func_; + CellNetworkViewMap cell_network_view_map_; + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteNetwork); + + friend class ConcreteLibertyLibraryIterator; +}; + +class ConcreteInstance +{ +public: + const char *name() const { return name_; } + Cell *cell() const; + ConcreteInstance *parent() const { return parent_; } + ConcretePin *findPin(const char *port_name) const; + ConcretePin *findPin(const Port *port) const; + ConcreteNet *findNet(const char *net_name) const; + void findNetsMatching(const PatternMatch *pattern, + NetSeq *nets) const; + InstanceNetIterator *netIterator() const; + Instance *findChild(const char *name) const; + InstanceChildIterator *childIterator() const; + 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 deleteNet(ConcreteNet *net); + void setCell(ConcreteCell *cell); + void initPins(); + +protected: + ConcreteInstance(ConcreteCell *cell, + const char *name, + ConcreteInstance *parent); + ~ConcreteInstance(); + + ConcreteCell *cell_; + const char *name_; + ConcreteInstance *parent_; + // Array of pins indexed by pin->port->index(). + ConcretePin **pins_; + ConcreteInstanceChildMap *children_; + ConcreteInstanceNetMap *nets_; + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteInstance); + + friend class ConcreteNetwork; + friend class ConcreteInstancePinIterator; +}; + +class ConcretePin +{ +public: + const char *name() const; + ConcreteInstance *instance() const { return instance_; } + ConcreteNet *net() const { return net_; } + ConcretePort *port() const { return port_; } + ConcreteTerm *term() const { return term_; } + VertexIndex vertexIndex() const { return vertex_index_; } + void setVertexIndex(VertexIndex index); + +protected: + ~ConcretePin() {} + ConcretePin(ConcreteInstance *instance, + ConcretePort *port, + ConcreteNet *net); + + ConcreteInstance *instance_; + ConcretePort *port_; + ConcreteNet *net_; + ConcreteTerm *term_; + // Doubly linked list of net pins. + ConcretePin *net_next_; + ConcretePin *net_prev_; + VertexIndex vertex_index_; + +private: + DISALLOW_COPY_AND_ASSIGN(ConcretePin); + + friend class ConcreteNetwork; + friend class ConcreteNet; + friend class ConcreteNetPinIterator; +}; + +class ConcreteTerm +{ +public: + const char *name() const; + ConcreteNet *net() const { return net_; } + ConcretePin *pin() const { return pin_; } + +protected: + ~ConcreteTerm() {} + ConcreteTerm(ConcretePin *pin, + ConcreteNet *net); + + ConcretePin *pin_; + ConcreteNet *net_; + // Linked list of net terms. + ConcreteTerm *net_next_; + +private: + DISALLOW_COPY_AND_ASSIGN(ConcreteTerm); + + friend class ConcreteNetwork; + friend class ConcreteNet; + friend class ConcreteNetTermIterator; +}; + +class ConcreteNet +{ +public: + const char *name() const { return name_; } + ConcreteInstance *instance() const { return instance_; } + void addPin(ConcretePin *pin); + void deletePin(ConcretePin *pin); + void addTerm(ConcreteTerm *term); + void deleteTerm(ConcreteTerm *term); + void mergeInto(ConcreteNet *net); + ConcreteNet *mergedInto() { return merged_into_; } + +protected: + DISALLOW_COPY_AND_ASSIGN(ConcreteNet); + ConcreteNet(const char *name, + ConcreteInstance *instance); + ~ConcreteNet(); + const char *name_; + ConcreteInstance *instance_; + // Pointer to head of linked list of pins. + ConcretePin *pins_; + // 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_; + + friend class ConcreteNetwork; + friend class ConcreteNetTermIterator; + friend class ConcreteNetPinIterator; +}; + +} // namespace +#endif diff --git a/network/HpinDrvrLoad.cc b/network/HpinDrvrLoad.cc new file mode 100644 index 0000000..6d7cd63 --- /dev/null +++ b/network/HpinDrvrLoad.cc @@ -0,0 +1,303 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "Network.hh" +#include "HpinDrvrLoad.hh" + +namespace sta { + +HpinDrvrLoad::HpinDrvrLoad(Pin *drvr, + 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), + hpins_to_load_(hpins_to_load ? new PinSet(*hpins_to_load) : nullptr) +{ +} + +HpinDrvrLoad::HpinDrvrLoad(Pin *drvr, + Pin *load) : + drvr_(drvr), + load_(load) +{ +} + +HpinDrvrLoad::~HpinDrvrLoad() +{ + delete hpins_from_drvr_; + delete hpins_to_load_; +} + +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()) { + Pin *pin = pin_iter.next(); + printf("%s ", network->pathName(pin)); + } + printf("* "); + PinSet::Iterator pin_iter2(hpins_to_load_); + while (pin_iter2.hasNext()) { + Pin *pin = pin_iter2.next(); + printf("%s ", network->pathName(pin)); + } + printf("\n"); +} + +void +HpinDrvrLoad::setDrvr(Pin *drvr) +{ + drvr_ = drvr; +} + +bool +HpinDrvrLoadLess::operator()(const HpinDrvrLoad *drvr_load1, + const HpinDrvrLoad *drvr_load2) const +{ + Pin *load1 = drvr_load1->load(); + Pin *load2 = drvr_load2->load(); + if (load1 == load2) { + Pin *drvr1 = drvr_load1->drvr(); + Pin *drvr2 = drvr_load2->drvr(); + return drvr1 < drvr2; + } + else + return load1 < load2; +} + +//////////////////////////////////////////////////////////////// + +static void +visitPinsAboveNet2(const Pin *hpin, + 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. + NetPinIterator *pin_iter = network->pinIterator(above_net); + while (pin_iter->hasNext()) { + 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); + } + if (network->isLoad(above_pin)) { + 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); + } + } + } + } + delete pin_iter; + + // Search up from net terminals. + NetTermIterator *term_iter = network->termIterator(above_net); + while (term_iter->hasNext()) { + Term *term = term_iter->next(); + Pin *above_pin = network->pin(term); + if (above_pin + && 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 (network->isDriver(above_pin)) { + 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); + } + } + } + delete term_iter; +} + +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) +{ + visited.insert(below_net); + // Visit below net pins. + NetPinIterator *pin_iter = network->pinIterator(below_net); + while (pin_iter->hasNext()) { + 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 (network->isDriver(below_pin)) { + 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); + } + 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); + } + } + } + } + } + delete pin_iter; + + // Search up from net terminals. + NetTermIterator *term_iter = network->termIterator(below_net); + while (term_iter->hasNext()) { + Term *term = term_iter->next(); + Pin *above_pin = network->pin(term); + if (above_pin + && 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); + } + } + } + delete term_iter; +} + +static void +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(); + HpinDrvrLoad clone(drvr->drvr(), + load->load(), + drvr->hpinsFromDrvr(), + load->hpinsToLoad()); + visitor->visit(&clone); + } + } +} + +void +visitHpinDrvrLoads(const Pin *pin, + const Network *network, + HpinDrvrLoadVisitor *visitor) +{ + NetSet visited; + HpinDrvrLoads above_drvrs; + HpinDrvrLoads above_loads; + PinSet hpin_path; + Net *above_net = network->net(pin); + if (above_net) { + visitPinsAboveNet2(pin, above_net, visited, + above_drvrs, above_loads, + &hpin_path, network); + } + + // Search down from hpin terminal. + HpinDrvrLoads below_drvrs; + HpinDrvrLoads below_loads; + Term *term = network->term(pin); + if (term) { + Net *below_net = network->net(term); + if (below_net) + visitPinsBelowNet2(pin, above_net, below_net, visited, + below_drvrs, below_loads, + &hpin_path, network); + } + if (network->isHierarchical(pin)) { + visitHpinDrvrLoads(above_drvrs, below_loads, visitor); + visitHpinDrvrLoads(below_drvrs, above_loads, visitor); + } + else { + if (network->isDriver(pin)) { + HpinDrvrLoad drvr(const_cast(pin), nullptr, &hpin_path, nullptr); + HpinDrvrLoads drvrs; + drvrs.insert(&drvr); + visitHpinDrvrLoads(drvrs, below_loads, visitor); + visitHpinDrvrLoads(drvrs, above_loads, visitor); + } + // Bidirects are drivers and loads. + if (network->isLoad(pin)) { + HpinDrvrLoad load(nullptr, const_cast(pin), nullptr, &hpin_path); + HpinDrvrLoads loads; + loads.insert(&load); + visitHpinDrvrLoads(below_drvrs, loads, visitor); + visitHpinDrvrLoads(above_drvrs, loads, visitor); + } + } + above_drvrs.deleteContents(); + above_loads.deleteContents(); + below_drvrs.deleteContents(); + below_loads.deleteContents(); +} + +} // namespace diff --git a/network/HpinDrvrLoad.hh b/network/HpinDrvrLoad.hh new file mode 100644 index 0000000..e0ad331 --- /dev/null +++ b/network/HpinDrvrLoad.hh @@ -0,0 +1,79 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_HPIN_DRVR_LOAD_H +#define STA_HPIN_DRVR_LOAD_H + +#include "Set.hh" +#include "NetworkClass.hh" + +namespace sta { + +class HpinDrvrLoad; + +class HpinDrvrLoadLess +{ +public: + bool operator()(const HpinDrvrLoad *drvr_load1, + const HpinDrvrLoad *drvr_load2) const; +}; + +typedef Set HpinDrvrLoads; + +// Abstract base class for visitDrvrLoadsThruHierPin visitor. +class HpinDrvrLoadVisitor +{ +public: + HpinDrvrLoadVisitor() {} + virtual ~HpinDrvrLoadVisitor() {} + virtual void visit(HpinDrvrLoad *drvr_load) = 0; + +private: + DISALLOW_COPY_AND_ASSIGN(HpinDrvrLoadVisitor); +}; + +class HpinDrvrLoad +{ +public: + HpinDrvrLoad(Pin *drvr, + Pin *load, + PinSet *hpins_from_drvr, + PinSet *hpins_to_load); + ~HpinDrvrLoad(); + void report(const Network *network); + HpinDrvrLoad(Pin *drvr, + Pin *load); + Pin *drvr() const { return drvr_; } + Pin *load() const { return load_; } + PinSet *hpinsFromDrvr() { return hpins_from_drvr_; } + PinSet *hpinsToLoad() { return hpins_to_load_; } + void setDrvr(Pin *drvr); + +private: + Pin *drvr_; + Pin *load_; + PinSet *hpins_from_drvr_; + PinSet *hpins_to_load_; +}; + +void +visitHpinDrvrLoads(const Pin *pin, + const Network *network, + HpinDrvrLoadVisitor *visitor); + +} // namespace +#endif + diff --git a/network/MakeConcreteNetwork.hh b/network/MakeConcreteNetwork.hh new file mode 100644 index 0000000..eb97017 --- /dev/null +++ b/network/MakeConcreteNetwork.hh @@ -0,0 +1,28 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_MAKE_CONCRETE_NETWORK_H +#define STA_MAKE_CONCRETE_NETWORK_H + +namespace sta { + +class Network; + +Network * +makeConcreteNetwork(); + +} // namespace +#endif diff --git a/network/Makefile.am b/network/Makefile.am new file mode 100644 index 0000000..0e3fa7a --- /dev/null +++ b/network/Makefile.am @@ -0,0 +1,46 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +lib_LTLIBRARIES = libnetwork.la + +include_HEADERS = \ + ConcreteLibrary.hh \ + ConcreteNetwork.hh \ + HpinDrvrLoad.hh \ + MakeConcreteNetwork.hh \ + Network.hh \ + NetworkClass.hh \ + NetworkCmp.hh \ + ParseBus.hh \ + PortDirection.hh \ + SdcNetwork.hh \ + VerilogNamespace.hh + +libnetwork_la_SOURCES = \ + ConcreteLibrary.cc \ + ConcreteNetwork.cc \ + HpinDrvrLoad.cc \ + Network.cc \ + NetworkCmp.cc \ + ParseBus.cc \ + PortDirection.cc \ + SdcNetwork.cc \ + VerilogNamespace.cc + +libs: $(lib_LTLIBRARIES) + +xtags: $(SOURCES) $(HEADERS) + etags -a -o ../TAGS $(SOURCES) $(HEADERS) diff --git a/network/Network.cc b/network/Network.cc new file mode 100644 index 0000000..f577034 --- /dev/null +++ b/network/Network.cc @@ -0,0 +1,1840 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "StringUtil.hh" +#include "PatternMatch.hh" +#include "PortDirection.hh" +#include "Liberty.hh" +#include "Network.hh" + +namespace sta { + +Network::Network() : + default_liberty_(nullptr), + divider_('/'), + escape_('\\') +{ +} + +Network::~Network() +{ + net_drvr_pin_map_.deleteContents(); +} + +void +Network::clear() +{ + default_liberty_ = nullptr; + clearNetDrvPinrMap(); +} + +bool +Network::isLinked() const +{ + return topInstance() != nullptr; +} + +LibertyLibrary * +Network::libertyLibrary(const Cell *cell) const +{ + return libertyCell(cell)->libertyLibrary(); +} + +LibertyLibrary * +Network::libertyLibrary(const Instance *instance) const +{ + return libertyCell(instance)->libertyLibrary(); +} + +void +Network::readLibertyAfter(LibertyLibrary *) +{ +} + +LibertyCell * +Network::findLibertyCell(const char *name) const +{ + LibertyLibraryIterator *iter = libertyLibraryIterator(); + while (iter->hasNext()) { + LibertyLibrary *lib = iter->next(); + LibertyCell *cell = lib->findLibertyCell(name); + if (cell) { + delete iter; + return cell; + } + } + delete iter; + return nullptr; +} + +LibertyLibrary * +Network::defaultLibertyLibrary() const +{ + return default_liberty_; +} + +void +Network::setDefaultLibertyLibrary(LibertyLibrary *library) +{ + default_liberty_ = library; +} + +// Only used by Sta::setMinLibrary so linear search is acceptable. +LibertyLibrary * +Network::findLibertyFilename(const char *filename) +{ + LibertyLibraryIterator *lib_iter = libertyLibraryIterator(); + while (lib_iter->hasNext()) { + LibertyLibrary *lib = lib_iter->next(); + if (stringEq(lib->filename(), filename)) { + delete lib_iter; + return lib; + } + } + delete lib_iter; + return nullptr; +} + +LibertyCell * +Network::libertyCell(const Instance *instance) const +{ + return libertyCell(cell(instance)); +} + +LibertyPort * +Network::libertyPort(const Pin *pin) const +{ + return libertyPort(port(pin)); +} + +bool +Network::busIndexInRange(const Port *port, + int index) +{ + int from_index = fromIndex(port); + int to_index = toIndex(port); + return (from_index <= to_index + && index <= to_index + && index >= from_index) + || (from_index > to_index + && index >= to_index + && index <= from_index); +} + +bool +Network::hasMembers(const Port *port) const +{ + return isBus(port) || isBundle(port); +} + +const char * +Network::pathName(const Instance *instance) const +{ + ConstInstanceSeq inst_path; + path(instance, inst_path); + size_t name_length = 0; + ConstInstanceSeq::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); + char *path_ptr = path_name; + // Top instance has null string name, so terminate the string here. + *path_name = '\0'; + while (inst_path.size()) { + const Instance *inst = inst_path.back(); + const char *inst_name = name(inst); + strcpy(path_ptr, inst_name); + path_ptr += strlen(inst_name); + inst_path.pop_back(); + if (inst_path.size()) + *path_ptr++ = pathDivider(); + *path_ptr = '\0'; + } + return path_name; +} + +bool +Network::pathNameLess(const Instance *inst1, + const Instance *inst2) const +{ + return pathNameCmp(inst1, inst2) < 0; +} + +int +Network::pathNameCmp(const Instance *inst1, + const Instance *inst2) const +{ + if (inst1 == nullptr && inst2) + return -1; + else if (inst1 && inst2 == nullptr) + return 1; + else if (inst1 == inst2) + return 0; + else { + ConstInstanceSeq path1; + ConstInstanceSeq path2; + path(inst1, path1); + path(inst2, path2); + while (!path1.empty() && !path2.empty()) { + const Instance *inst1 = path1.back(); + const Instance *inst2 = path2.back(); + int cmp = strcmp(name(inst1), name(inst2)); + if (cmp != 0) + return cmp; + path1.pop_back(); + path2.pop_back(); + } + if (path1.empty() && !path2.empty()) + return -1; + else if (path1.empty() && path2.empty()) + return 0; + else + return 1; + } +} + +void +Network::path(const Instance *inst, + // Return value. + ConstInstanceSeq &path) const +{ + while (!isTopInstance(inst)) { + path.push_back(inst); + inst = parent(inst); + } +} + +bool +Network::isTopInstance(const Instance *inst) const +{ + return inst == topInstance(); +} + +bool +Network::isInside(const Instance *inst, + const Instance *hier_inst) const +{ + while (inst) { + if (inst == hier_inst) + return true; + inst = parent(inst); + } + return false; +} + +bool +Network::isHierarchical(const Instance *instance) const +{ + return !isLeaf(instance); +} + +//////////////////////////////////////////////////////////////// + +const char * +Network::name(const Pin *pin) const +{ + return pathName(pin); +} + +const char * +Network::portName(const Pin *pin) const +{ + return name(port(pin)); +} + +const char * +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); + return path_name; + } + else + return portName(pin); +} + +bool +Network::pathNameLess(const Pin *pin1, + const Pin *pin2) const +{ + return pathNameCmp(pin1, pin2) < 0; +} + +int +Network::pathNameCmp(const Pin *pin1, + const Pin *pin2) const +{ + int inst_cmp = pathNameCmp(instance(pin1), instance(pin2)); + if (inst_cmp == 0) + return strcmp(portName(pin1), portName(pin2)); + else + return inst_cmp; +} + +bool +Network::isInside(const Net *net, + const Instance *hier_inst) const +{ + return isInside(instance(net), hier_inst); +} + +bool +Network::isLeaf(const Pin *pin) const +{ + return isLeaf(instance(pin)); +} + +bool +Network::isHierarchical(const Pin *pin) const +{ + return (!isLeaf(pin) && !isTopLevelPort(pin)); +} + +bool +Network::isTopLevelPort(const Pin *pin) const +{ + return (parent(instance(pin)) == nullptr); +} + +bool +Network::isInside(const Pin *pin, + const Pin *hier_pin) const +{ + return isInside(pin, instance(hier_pin)); +} + +bool +Network::isInside(const Pin *pin, + const Instance *hier_inst) const +{ + return isInside(instance(pin), hier_inst); +} + +bool +Network::pinLess(const Pin *pin1, + const Pin *pin2) const +{ + return pathNameLess(pin1, pin2); +} + +//////////////////////////////////////////////////////////////// + +const char * +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); + return path_name; + } + else + return name(net); +} + +bool +Network::pathNameLess(const Net *net1, + const Net *net2) const +{ + return pathNameCmp(net1, net2) < 0; +} + +int +Network::pathNameCmp(const Net *net1, + const Net *net2) const +{ + int inst_cmp = pathNameCmp(instance(net1), instance(net2)); + if (inst_cmp == 0) + return strcmp(name(net1), name(net2)); + else + return inst_cmp; +} + +Net * +Network::highestNetAbove(Net *net) const +{ + Net *highest_net = net; + // Search up from net terminals. + NetTermIterator *term_iter = termIterator(net); + while (term_iter->hasNext()) { + Term *term = term_iter->next(); + Pin *above_pin = pin(term); + if (above_pin) { + Net *above_net = this->net(above_pin); + if (above_net) { + highest_net = highestNetAbove(above_net); + break; + } + } + } + delete term_iter; + return highest_net; +} + +Net * +Network::highestConnectedNet(Net *net) const +{ + NetSet nets; + connectedNets(net, &nets); + Net *highest_net = net; + int highest_level = hierarchyLevel(net); + NetSet::Iterator net_iter(nets); + while (net_iter.hasNext()) { + Net *net1 = net_iter.next(); + int level = hierarchyLevel(net1); + if (level < highest_level + || (level == highest_level + && stringLess(pathName(net1), pathName(highest_net)))) { + highest_net = net1; + highest_level = level; + } + } + return highest_net; +} + +void +Network::connectedNets(Net *net, + NetSet *nets) const +{ + if (!nets->hasKey(net)) { + nets->insert(net); + // Search up from net terminals. + NetTermIterator *term_iter = termIterator(net); + while (term_iter->hasNext()) { + 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); + } + } + delete term_iter; + + // Search down from net pins. + NetPinIterator *pin_iter = pinIterator(net); + while (pin_iter->hasNext()) { + 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); + } + } + delete pin_iter; + } +} + +void +Network::connectedNets(const Pin *pin, + NetSet *nets) const +{ + Net *net = this->net(pin); + if (net) + connectedNets(net, nets); + else { + Term *term = this->term(pin); + if (term) { + Net *below_net = this->net(term); + if (below_net) + connectedNets(below_net, nets); + } + } +} + +int +Network::hierarchyLevel(const Net *net) const +{ + NetTermIterator *term_iter = network_->termIterator(net); + while (term_iter->hasNext()) { + Term *term = term_iter->next(); + Pin *pin = network_->pin(term); + if (pin) { + Net *above_net = network_->net(pin); + if (above_net) { + delete term_iter; + return hierarchyLevel(above_net) + 1; + } + } + } + delete term_iter; + return 0; +} + +bool +Network::isDriver(const Pin *pin) const +{ + PortDirection *dir = direction(pin); + const Instance *inst = instance(pin); + return (isLeaf(inst) && dir->isAnyOutput()) + // isTopLevelPort(pin) + || (isTopInstance(inst) && dir->isAnyInput()); +} + +bool +Network::isLoad(const Pin *pin) const +{ + PortDirection *dir = direction(pin); + const Instance *inst = instance(pin); + return (isLeaf(inst) && dir->isAnyInput()) + // isTopLevelPort(pin) + || (isTopInstance(inst) && dir->isAnyOutput()); +} + +bool +Network::isRegClkPin(const Pin *pin) const +{ + const LibertyPort *port = libertyPort(pin); + return port && port->isRegClk(); +} + +bool +Network::isCheckClk(const Pin *pin) const +{ + const LibertyPort *port = libertyPort(pin); + return port && port->isCheckClk(); +} + +bool +Network::isLatchData(const Pin *pin) const +{ + LibertyPort *port = libertyPort(pin); + if (port) + return port->libertyCell()->isLatchData(port); + else + return false; +} + +//////////////////////////////////////////////////////////////// + +const char * +Network::name(const Term *term) const +{ + return name(pin(term)); +} + +const char * +Network::pathName(const Term *term) const +{ + return pathName(pin(term)); +} + +const char * +Network::portName(const Term *term) const +{ + return portName(pin(term)); +} + +//////////////////////////////////////////////////////////////// + +const char * +Network::cellName(const Instance *inst) const +{ + return name(cell(inst)); +} + +Instance * +Network::findInstance(const char *path_name) const +{ + return findInstanceRelative(topInstance(), path_name); +} + +Instance * +Network::findInstanceRelative(const Instance *inst, + const char *path_name) const +{ + char *first, *tail; + pathNameFirst(path_name, first, tail); + if (first) { + Instance *inst1 = findChild(inst, first); + stringDelete(first); + while (inst1 && tail) { + char *next_tail; + pathNameFirst(tail, first, next_tail); + if (first) { + inst1 = findChild(inst1, first); + stringDelete(first); + } + else + inst1 = findChild(inst1, tail); + stringDelete(tail); + tail = next_tail; + } + stringDelete(tail); + return inst1; + } + else + return findChild(inst, path_name); +} + +void +Network::findInstancesMatching(const Instance *context, + const PatternMatch *pattern, + InstanceSeq *insts) const +{ + 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; + findInstancesMatching1(context, context_name_length, pattern, insts); + } + else { + Instance *inst = findInstanceRelative(context, pattern->pattern()); + if (inst) + insts->push_back(inst); + } +} + +void +Network::findInstancesMatching1(const Instance *context, + size_t context_name_length, + const PatternMatch *pattern, + InstanceSeq *insts) const +{ + InstanceChildIterator *child_iter = childIterator(context); + while (child_iter->hasNext()) { + Instance *child = child_iter->next(); + const char *child_name = pathName(child); + // Remove context prefix from the name. + const char *child_context_name = &child_name[context_name_length]; + if (pattern->match(child_context_name)) + insts->push_back(child); + if (!isLeaf(child)) + findInstancesMatching1(child, context_name_length, pattern, insts); + } + delete child_iter; +} + +void +Network::findInstancesHierMatching(const Instance *instance, + const PatternMatch *pattern, + // Return value. + InstanceSeq *insts) const +{ + InstanceChildIterator *child_iter = childIterator(instance); + while (child_iter->hasNext()) { + Instance *child = child_iter->next(); + if (pattern->match(name(child))) + insts->push_back(child); + if (!isLeaf(child)) + findInstancesHierMatching(child, pattern, insts); + } + delete child_iter; +} + +void +Network::findChildrenMatching(const Instance *parent, + const PatternMatch *pattern, + InstanceSeq *insts) const +{ + if (pattern->hasWildcards()) { + InstanceChildIterator *child_iter = childIterator(parent); + while (child_iter->hasNext()) { + Instance *child = child_iter->next(); + if (pattern->match(name(child))) + insts->push_back(child); + } + delete child_iter; + } + else { + Instance *child = findChild(parent, pattern->pattern()); + if (child) + insts->push_back(child); + } +} + +Pin * +Network::findPin(const char *path_name) const +{ + return findPinRelative(topInstance(), path_name); +} + +Pin * +Network::findPinRelative(const Instance *inst, + const char *path_name) const +{ + char *inst_path, *port_name; + pathNameLast(path_name, inst_path, port_name); + if (inst_path) { + 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); + return nullptr; + } + else + // Top level pin. + return findPin(inst, path_name); +} + +Pin * +Network::findPinLinear(const Instance *instance, + const char *port_name) const +{ + InstancePinIterator *pin_iter = pinIterator(instance); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (stringEq(port_name, portName(pin))) { + delete pin_iter; + return pin; + } + } + delete pin_iter; + return nullptr; +} + +Pin * +Network::findPin(const Instance *instance, + const Port *port) const +{ + return findPin(instance, name(port)); +} + +Pin * +Network::findPin(const Instance *instance, + const LibertyPort *port) const +{ + return findPin(instance, port->name()); +} + +Net * +Network::findNet(const char *path_name) const +{ + return findNetRelative(topInstance(), path_name); +} + +Net * +Network::findNetRelative(const Instance *inst, + const char *path_name) const +{ + char *inst_path, *net_name; + pathNameLast(path_name, inst_path, net_name); + if (inst_path) { + 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); + return nullptr; + } + else + // Top level net. + return findNet(inst, path_name); +} + +Net * +Network::findNetLinear(const Instance *instance, + const char *net_name) const +{ + InstanceNetIterator *net_iter = netIterator(instance); + while (net_iter->hasNext()) { + Net *net = net_iter->next(); + if (stringEq(name(net), net_name)) { + delete net_iter; + return net; + } + } + delete net_iter; + return nullptr; +} + +void +Network::findNetsMatching(const Instance *context, + const PatternMatch *pattern, + NetSeq *nets) const +{ + if (pattern->hasWildcards()) { + char *inst_path, *net_name; + pathNameLast(pattern->pattern(), inst_path, net_name); + if (inst_path) { + PatternMatch inst_pattern(inst_path, pattern); + PatternMatch net_pattern(net_name, pattern); + InstanceSeq insts; + findInstancesMatching(context, &inst_pattern, &insts); + InstanceSeq::Iterator inst_iter(insts); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + findNetsMatching(inst, &net_pattern, nets); + } + stringDelete(inst_path); + stringDelete(net_name); + } + else + // Top level net. + findInstNetsMatching(context, pattern, nets); + } + else { + Net *net = findNet(pattern->pattern()); + if (net) + nets->push_back(net); + } +} + +void +Network::findNetsHierMatching(const Instance *instance, + const PatternMatch *pattern, + // Return value. + NetSeq *nets) const +{ + findInstNetsMatching(instance, pattern, nets); + InstanceChildIterator *child_iter = childIterator(instance); + while (child_iter->hasNext()) { + Instance *child = child_iter->next(); + findNetsHierMatching(child, pattern, nets); + } + delete child_iter; +} + +void +Network::findNetsMatchingLinear(const Instance *instance, + const PatternMatch *pattern, + NetSeq *nets) const +{ + InstanceNetIterator *net_iter = netIterator(instance); + while (net_iter->hasNext()) { + Net *net = net_iter->next(); + if (pattern->match(name(net))) + nets->push_back(net); + } + delete net_iter; +} + +void +Network::findPinsMatching(const Instance *instance, + const PatternMatch *pattern, + PinSeq *pins) const +{ + if (pattern->hasWildcards()) { + char *inst_path, *port_name; + pathNameLast(pattern->pattern(), inst_path, port_name); + if (inst_path) { + PatternMatch inst_pattern(inst_path, pattern); + PatternMatch port_pattern(port_name, pattern); + InstanceSeq insts; + findInstancesMatching(instance, &inst_pattern, &insts); + InstanceSeq::Iterator inst_iter(insts); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + findInstPinsMatching(inst, &port_pattern, pins); + } + stringDelete(inst_path); + stringDelete(port_name); + } + else + // Top level pin. + findInstPinsMatching(instance, pattern, pins); + } + else { + Pin *pin = findPin(pattern->pattern()); + if (pin) + pins->push_back(pin); + } +} + +void +Network::findPinsHierMatching(const Instance *instance, + const PatternMatch *pattern, + // Return value. + PinSeq *pins) const +{ + InstanceChildIterator *child_iter = childIterator(instance); + while (child_iter->hasNext()) { + Instance *child = child_iter->next(); + findInstPinsHierMatching(child, pattern, pins); + findPinsHierMatching(child, pattern, pins); + } + delete child_iter; +} + +void +Network::findInstPinsHierMatching(const Instance *instance, + const PatternMatch *pattern, + // Return value. + PinSeq *pins) const +{ + const char *inst_name = name(instance); + InstancePinIterator *pin_iter = pinIterator(instance); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + const char *port_name = name(port(pin)); + string pin_name; + stringPrint(pin_name, "%s%c%s", inst_name,divider_, port_name); + if (pattern->match(pin_name.c_str())) + pins->push_back(pin); + } + delete pin_iter; +} + +void +Network::findInstPinsMatching(const Instance *instance, + const PatternMatch *pattern, + PinSeq *pins) const +{ + if (pattern->hasWildcards()) { + InstancePinIterator *pin_iter = pinIterator(instance); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (pattern->match(name(pin))) + pins->push_back(pin); + } + delete pin_iter; + } + else { + Pin *pin = findPin(instance, pattern->pattern()); + if (pin) + pins->push_back(pin); + } +} + +int +Network::instanceCount(Instance *inst) +{ + int count = 1; + InstanceChildIterator *child_iter = childIterator(inst); + while (child_iter->hasNext()) { + Instance *child = child_iter->next(); + count += instanceCount(child); + } + delete child_iter; + return count; +} + +int +Network::instanceCount() +{ + return instanceCount(topInstance()); +} + +int +Network::pinCount(Instance *inst) +{ + int count = 0; + InstancePinIterator *pin_iter = pinIterator(inst); + while (pin_iter->hasNext()) { + pin_iter->next(); + count++; + } + delete pin_iter; + + InstanceChildIterator *child_iter = childIterator(inst); + while (child_iter->hasNext()) { + Instance *child = child_iter->next(); + count += pinCount(child); + } + delete child_iter; + return count; +} + +int +Network::pinCount() +{ + return pinCount(topInstance()); +} + +int +Network::netCount(Instance *inst) +{ + int count = 0; + InstanceNetIterator *net_iter = netIterator(inst); + while (net_iter->hasNext()) { + net_iter->next(); + count++; + } + delete net_iter; + + InstanceChildIterator *child_iter = childIterator(inst); + while (child_iter->hasNext()) { + Instance *child = child_iter->next(); + count += netCount(child); + } + delete child_iter; + return count; +} + +int +Network::netCount() +{ + return netCount(topInstance()); +} + +int +Network::leafInstanceCount() +{ + int count = 0; + LeafInstanceIterator *leaf_iter = leafInstanceIterator(); + while (leaf_iter->hasNext()) { + leaf_iter->next(); + count++; + } + delete leaf_iter; + return count; +} + +int +Network::leafPinCount() +{ + int count = 0; + LeafInstanceIterator *leaf_iter = leafInstanceIterator(); + while (leaf_iter->hasNext()) { + Instance *leaf = leaf_iter->next(); + InstancePinIterator *pin_iter = pinIterator(leaf); + while (pin_iter->hasNext()) { + pin_iter->next(); + count++; + } + delete pin_iter; + } + delete leaf_iter; + return count; +} + +void +Network::setPathDivider(char divider) +{ + divider_ = divider; +} + +void +Network::setPathEscape(char escape) +{ + escape_ = escape; +} + +//////////////////////////////////////////////////////////////// + +typedef Vector InstanceChildIteratorSeq; + +class LeafInstanceIterator1 : public LeafInstanceIterator +{ +public: + LeafInstanceIterator1(const Instance *inst, + const Network *network); + bool hasNext() { return next_; } + Instance *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(LeafInstanceIterator1); + void nextInst(); + + const Network *network_; + InstanceChildIteratorSeq pending_child_iters_; + InstanceChildIterator *child_iter_; + Instance *next_; + +}; + +LeafInstanceIterator1::LeafInstanceIterator1(const Instance *inst, + const Network *network) : + network_(network), + child_iter_(network->childIterator(inst)), + next_(nullptr) +{ + pending_child_iters_.reserve(512); + nextInst(); +} + +Instance * +LeafInstanceIterator1::next() +{ + Instance *next = next_; + nextInst(); + return next; +} + +void +LeafInstanceIterator1::nextInst() +{ + next_ = nullptr; + while (child_iter_) { + while (child_iter_->hasNext()) { + next_ = child_iter_->next(); + if (network_->isLeaf(next_)) + return; + else { + pending_child_iters_.push_back(child_iter_); + child_iter_ = network_->childIterator(next_); + next_ = nullptr; + } + } + delete child_iter_; + + if (pending_child_iters_.empty()) + child_iter_ = nullptr; + else { + child_iter_ = pending_child_iters_.back(); + pending_child_iters_.pop_back(); + } + } +} + +LeafInstanceIterator * +Network::leafInstanceIterator() const +{ + return new LeafInstanceIterator1(topInstance(), this); +} + +LeafInstanceIterator * +Network::leafInstanceIterator(const Instance *hier_inst) const +{ + return new LeafInstanceIterator1(hier_inst, this); +} + +//////////////////////////////////////////////////////////////// + +void +Network::visitConnectedPins(Pin *pin, + PinVisitor &visitor) const +{ + ConstNetSet visited_nets; + Net *pin_net = net(pin); + Term *pin_term = term(pin); + if (pin_net) + visitConnectedPins(pin_net, visitor, visited_nets); + else if (pin_term == nullptr) + // Unconnected or internal pin. + visitor(pin); + + // Search down from pin terminal. + if (pin_term) { + Net *term_net = net(pin_term); + if (term_net) + visitConnectedPins(term_net, visitor, visited_nets); + } +} + +void +Network::visitConnectedPins(const Net *net, + PinVisitor &visitor) const +{ + ConstNetSet visited_nets; + visitConnectedPins(net, visitor, visited_nets); +} + +void +Network::visitConnectedPins(const Net *net, + PinVisitor &visitor, + ConstNetSet &visited_nets) const +{ + if (!visited_nets.hasKey(net)) { + visited_nets.insert(net); + // Search up from net terminals. + NetTermIterator *term_iter = termIterator(net); + while (term_iter->hasNext()) { + 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); + } + } + delete term_iter; + + // Search down from net pins. + NetPinIterator *pin_iter = pinIterator(net); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + 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); + } + } + delete pin_iter; + } +} + +//////////////////////////////////////////////////////////////// + +class ConnectedPinIterator1 : public ConnectedPinIterator +{ +public: + explicit ConnectedPinIterator1(PinSet *pins); + virtual ~ConnectedPinIterator1(); + virtual bool hasNext(); + virtual Pin *next(); + +protected: + PinSet::Iterator pin_iter_; + +private: + DISALLOW_COPY_AND_ASSIGN(ConnectedPinIterator1); +}; + +ConnectedPinIterator1::ConnectedPinIterator1(PinSet *pins) : + pin_iter_(pins) +{ +} + +ConnectedPinIterator1::~ConnectedPinIterator1() +{ + delete pin_iter_.container(); +} + +bool +ConnectedPinIterator1::hasNext() +{ + return pin_iter_.hasNext(); +} + +Pin * +ConnectedPinIterator1::next() +{ + return pin_iter_.next(); +} + +class FindConnectedPins : public PinVisitor +{ +public: + explicit FindConnectedPins(PinSet *pins); + virtual void operator()(Pin *pin); + +protected: + PinSet *pins_; + +private: + DISALLOW_COPY_AND_ASSIGN(FindConnectedPins); +}; + +FindConnectedPins::FindConnectedPins(PinSet *pins) : + PinVisitor(), + pins_(pins) +{ +} + +void +FindConnectedPins::operator()(Pin *pin) +{ + pins_->insert(pin); +} + +NetConnectedPinIterator * +Network::connectedPinIterator(const Net *net) const +{ + PinSet *pins = new PinSet; + FindConnectedPins visitor(pins); + visitConnectedPins(net, visitor); + return new ConnectedPinIterator1(pins); +} + +PinConnectedPinIterator * +Network::connectedPinIterator(const Pin *pin) const +{ + PinSet *pins = new PinSet; + pins->insert(const_cast(pin)); + + FindConnectedPins visitor(pins); + Net *pin_net = net(pin); + if (pin_net) + visitConnectedPins(pin_net, visitor); + + // Search down from pin terminal. + Term *pin_term = term(pin); + if (pin_term) { + Net *term_net = net(pin_term); + if (term_net) + visitConnectedPins(term_net, visitor); + } + return new ConnectedPinIterator1(pins); +} + +//////////////////////////////////////////////////////////////// + +bool +Network::isConnected(const Net *net, + const Pin *pin) const +{ + if (this->net(pin) == net) + return true; + else { + ConstNetSet nets; + return isConnected(net, pin, nets); + } +} + +bool +Network::isConnected(const Net *net, + const Pin *pin, + ConstNetSet &nets) const +{ + if (!nets.hasKey(net)) { + nets.insert(net); + // Search up from net terminals. + NetTermIterator *term_iter = termIterator(net); + while (term_iter->hasNext()) { + 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; + } + } + } + } + delete term_iter; + + // Search down from net pins. + NetPinIterator *pin_iter = pinIterator(net); + while (pin_iter->hasNext()) { + Pin *pin1 = pin_iter->next(); + if (pin1 == pin) { + 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; + } + } + } + } + delete pin_iter; + } + return false; +} + +bool +Network::isConnected(const Net *net1, + const Net *net2) const +{ + ConstNetSet nets; + return isConnected(net1, net2, nets); +} + +bool +Network::isConnected(const Net *net1, + const Net *net2, + ConstNetSet &nets) const +{ + if (net1 == net2) + return true; + else if (!nets.hasKey(net1)) { + nets.insert(net1); + // Search up from net terminals. + NetTermIterator *term_iter = termIterator(net1); + while (term_iter->hasNext()) { + 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; + } + } + } + delete term_iter; + + // Search down from net pins. + NetPinIterator *pin_iter = pinIterator(net1); + while (pin_iter->hasNext()) { + 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; + } + } + } + delete pin_iter; + } + return false; +} + +//////////////////////////////////////////////////////////////// + +class FindDrvrPins : public PinVisitor +{ +public: + explicit FindDrvrPins(PinSet *pins, + const Network *network); + virtual void operator()(Pin *pin); + +protected: + PinSet *pins_; + const Network *network_; + +private: + DISALLOW_COPY_AND_ASSIGN(FindDrvrPins); +}; + +FindDrvrPins::FindDrvrPins(PinSet *pins, + const Network *network) : + PinVisitor(), + pins_(pins), + network_(network) +{ +} + +void +FindDrvrPins::operator()(Pin *pin) +{ + if (network_->isDriver(pin)) + pins_->insert(pin); +} + +PinSet * +Network::drivers(const Pin *pin) +{ + const Net *net = this->net(pin); + if (net) + return drivers(net); + else + return nullptr; +} + +void +Network::clearNetDrvPinrMap() +{ + net_drvr_pin_map_.deleteContentsClear(); +} + +PinSet * +Network::drivers(const Net *net) +{ + PinSet *drvrs = net_drvr_pin_map_.findKey(net); + if (drvrs == nullptr) { + drvrs = new PinSet; + FindDrvrPins visitor(drvrs, this); + visitConnectedPins(net, visitor); + net_drvr_pin_map_[net] = drvrs; + } + return drvrs; +} + +//////////////////////////////////////////////////////////////// + +void +Network::pathNameFirst(const char *path_name, + char *&first, + char *&tail) const +{ + 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; + } +} + +void +Network::pathNameLast(const char *path_name, + char *&head, + char *&last) const +{ + 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); +} + +//////////////////////////////////////////////////////////////// + +NetworkConstantPinIterator:: +NetworkConstantPinIterator(const Network *network, + NetSet &zero_nets, + NetSet &one_nets) : + ConstantPinIterator(), + 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_; +} + +void +NetworkConstantPinIterator::findConstantPins(NetSet &nets, + PinSet &pins) +{ + NetSet::Iterator net_iter(nets); + while (net_iter.hasNext()) { + const Net *net = net_iter.next(); + NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + pins.insert(pin); + } + delete pin_iter; + } +} + +bool +NetworkConstantPinIterator::hasNext() +{ + if (pin_iter_->hasNext()) + 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(); + } + else + return false; +} + +void +NetworkConstantPinIterator::next(Pin *&pin, + LogicValue &value) +{ + pin = pin_iter_->next(); + value = value_; +} + +//////////////////////////////////////////////////////////////// + +FindNetDrvrLoads::FindNetDrvrLoads(Pin *drvr_pin, + PinSet &visited_drvrs, + PinSeq &loads, + PinSeq &drvrs, + const Network *network) : + drvr_pin_(drvr_pin), + visited_drvrs_(visited_drvrs), + loads_(loads), + drvrs_(drvrs), + network_(network) +{ +} + +void +FindNetDrvrLoads::operator()(Pin *pin) +{ + if (network_->isLoad(pin)) + loads_.push_back(pin); + if (network_->isDriver(pin)) { + drvrs_.push_back(pin); + if (pin != drvr_pin_) + visited_drvrs_.insert(pin); + } +} + +//////////////////////////////////////////////////////////////// + +static void +visitPinsAboveNet1(const Pin *hpin, + Net *above_net, + NetSet &visited, + PinSet &above_drvrs, + PinSet &above_loads, + const Network *network) +{ + visited.insert(above_net); + // Visit above net pins. + NetPinIterator *pin_iter = network->pinIterator(above_net); + while (pin_iter->hasNext()) { + Pin *above_pin = pin_iter->next(); + if (above_pin != hpin) { + if (network->isDriver(above_pin)) + above_drvrs.insert(above_pin); + if (network->isLoad(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); + } + } + } + delete pin_iter; + + // Search up from net terminals. + NetTermIterator *term_iter = network->termIterator(above_net); + while (term_iter->hasNext()) { + Term *term = term_iter->next(); + Pin *above_pin = network->pin(term); + if (above_pin + && 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 (network->isDriver(above_pin)) + above_drvrs.insert(above_pin); + if (network->isLoad(above_pin)) + above_loads.insert(above_pin); + } + } + delete term_iter; +} + +static void +visitPinsBelowNet1(const Pin *hpin, + Net *below_net, + NetSet &visited, + PinSet &below_drvrs, + PinSet &below_loads, + const Network *network) +{ + visited.insert(below_net); + // Visit below net pins. + NetPinIterator *pin_iter = network->pinIterator(below_net); + while (pin_iter->hasNext()) { + Pin *below_pin = pin_iter->next(); + if (below_pin != hpin) { + NetSet visited_above; + if (network->isDriver(below_pin)) + below_drvrs.insert(below_pin); + if (network->isLoad(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); + } + } + } + } + delete pin_iter; +} + +static void +visitDrvrLoads(PinSet drvrs, + PinSet loads, + HierPinThruVisitor *visitor) +{ + PinSet::Iterator drvr_iter(drvrs); + while (drvr_iter.hasNext()) { + Pin *drvr = drvr_iter.next(); + PinSet::Iterator load_iter(loads); + while (load_iter.hasNext()) { + Pin *load = load_iter.next(); + visitor->visit(drvr, load); + } + } +} + +void +visitDrvrLoadsThruHierPin(const Pin *hpin, + const Network *network, + HierPinThruVisitor *visitor) +{ + Net *above_net = network->net(hpin); + if (above_net) { + // Search down from hpin terminal. + Term *term = network->term(hpin); + if (term) { + Net *below_net = network->net(term); + if (below_net) { + NetSet visited; + PinSet above_drvrs; + PinSet above_loads; + visitPinsAboveNet1(hpin, above_net, visited, + above_drvrs, above_loads, network); + PinSet below_drvrs; + PinSet below_loads; + visitPinsBelowNet1(hpin, below_net, visited, + below_drvrs, below_loads, network); + visitDrvrLoads(above_drvrs, below_loads, visitor); + visitDrvrLoads(below_drvrs, above_loads, visitor); + } + } + } +} + +void +visitDrvrLoadsThruNet(Net *net, + const Network *network, + HierPinThruVisitor *visitor) +{ + NetSet visited; + PinSet above_drvrs; + PinSet above_loads; + PinSet below_drvrs; + PinSet below_loads; + PinSet net_drvrs; + PinSet net_loads; + NetPinIterator *pin_iter = network->pinIterator(net); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (network->isHierarchical(pin)) { + // 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); + } + } + } + else { + if (network->isDriver(pin)) + net_drvrs.insert(pin); + if (network->isLoad(pin)) + net_loads.insert(pin); + } + } + delete pin_iter; + + NetTermIterator *term_iter = network->termIterator(net); + while (term_iter->hasNext()) { + Term *term = term_iter->next(); + Pin *above_pin = network->pin(term); + if (above_pin) { + if (network->isDriver(above_pin)) + above_drvrs.insert(above_pin); + if (network->isLoad(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); + } + } + delete term_iter; + visitDrvrLoads(above_drvrs, below_loads, visitor); + visitDrvrLoads(above_drvrs, net_loads, visitor); + visitDrvrLoads(below_drvrs, above_loads, visitor); + visitDrvrLoads(below_drvrs, net_loads, visitor); + visitDrvrLoads(net_drvrs, above_loads, visitor); + visitDrvrLoads(net_drvrs, below_loads, visitor); + visitDrvrLoads(net_drvrs, net_loads, visitor); +} + +//////////////////////////////////////////////////////////////// + +char +logicValueString(LogicValue value) +{ + static char str[] = "01X^v"; + return str[int(value)]; +} + +bool +PortPairLess::operator()(const PortPair *pair1, + const PortPair *pair2) const +{ + return pair1->first < pair2->first + || (pair1->first == pair2->first + && pair1->second < pair2->second); +} + +} // namespace diff --git a/network/Network.hh b/network/Network.hh new file mode 100644 index 0000000..825e8db --- /dev/null +++ b/network/Network.hh @@ -0,0 +1,670 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_NETWORK_H +#define STA_NETWORK_H + +#include "DisallowCopyAssign.hh" +#include "Map.hh" +#include "StringUtil.hh" +#include "LibertyClass.hh" +#include "GraphClass.hh" +#include "NetworkClass.hh" +#include "StaState.hh" + +namespace sta { + +class Report; +class PatternMatch; +class PinVisitor; + +typedef Set ConstNetSet; +typedef Map LibertyLibraryMap; +// Link network function returns top level instance. +// Return nullptr if link fails. +typedef Instance *(LinkNetworkFunc)(const char *top_cell_name, + bool make_black_boxes, + Report *report, + NetworkReader *network); +typedef Map NetDrvrPinsMap; + +// The Network class defines the network API used by sta. +// The interface to a network implementation is constructed by +// deriving a class from it. This interface class is known as a +// ADAPTER/DELEGATE. An instance of the adapter is used to +// translate calls in the sta to a target network implementation. + +// Network data types: +// Libraries are collections of cells. +// Cells are masters (ala verilog module or liberty cell). +// Ports define the connections to a cell. +// There are four different sub-classes of ports: +// Simple non-bus ports such as "enable". +// Bundles of simple or single bit ports, such as D bundling D0, D1. +// Buses such as D[3:0] +// Bus bit ports, which are one bit of a bus port such as D[0]. +// Bus bit ports are always members of a bus. +// Instances are calls of cells in the design hierarchy +// Hierarchcial and leaf instances are in the network. +// At the top of the hierarchy is a top level instance that +// has instances for the top level netlist. +// Pins are a connection between an instance and a net corresponding +// to a port. Ports on the top level instance also have pins in +// the network. +// Terminals are a connection between a net and a pin on the parent +// instance. +// Nets connect pins at a level of hierarchy. Both hierarchical +// instance pins and leaf instance pins are connected by net. +// +// The network API only contains pointers/handles to these data types. +// The adapter casts these handles to its native data types and calls +// the appropriate code to implement the member functions of the Network +// class. +// +// Pattern arguments used by lookup find*Matching functions +// use a simple unix shell or tcl "string match" style pattern matching: +// '*' matches zero or more characters +// '?' matches any character +// +// Note that some of the network member functions are not virtual because +// they can be implemented in terms of the other functions. +// Only the pure virtual functions MUST be implemented by a derived class. + +class Network : public StaState +{ +public: + Network(); + virtual ~Network(); + virtual void clear(); + + // Linking the hierarchy creates the instance/pin/net network hierarchy. + // Most network functions are only useful after the hierarchy + // 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 isLinked() const; + virtual bool isEditable() const { return false; } + + //////////////////////////////////////////////////////////////// + // Library functions. + virtual LibraryIterator *libraryIterator() const = 0; + virtual LibertyLibraryIterator *libertyLibraryIterator() const = 0; + virtual Library *findLibrary(const char *name) = 0; + // Find liberty library by filename. + virtual LibertyLibrary *findLiberty(const char *name) = 0; + virtual LibertyLibrary *findLibertyFilename(const char *filename); + virtual const char *name(const Library *library) const = 0; + virtual Cell *findCell(const Library *library, + const char *name) const = 0; + // Search the design (non-liberty) libraries for cells matching pattern. + virtual void findCellsMatching(const Library *library, + const PatternMatch *pattern, + // Return value. + CellSeq *cells) 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; + // Hook for network after reading liberty library. + virtual void readLibertyAfter(LibertyLibrary *library); + // First liberty library read is used to look up defaults. + // This corresponds to a link_path of '*'. + LibertyLibrary *defaultLibertyLibrary() const; + void setDefaultLibertyLibrary(LibertyLibrary *library); + + //////////////////////////////////////////////////////////////// + // Cell functions. + virtual const char *name(const Cell *cell) const = 0; + virtual Library *library(const Cell *cell) const = 0; + virtual LibertyLibrary *libertyLibrary(const Cell *cell) const; + // Find the corresponding liberty cell. + virtual const LibertyCell *libertyCell(const Cell *cell) const = 0; + virtual LibertyCell *libertyCell(Cell *cell) const = 0; + 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; + // Name can be a simple, bundle, bus, or bus bit name. + virtual Port *findPort(const Cell *cell, + const char *name) const = 0; + virtual void findPortsMatching(const Cell *cell, + const PatternMatch *pattern, + // Return value. + PortSeq *ports) const = 0; + virtual bool isLeaf(const Cell *cell) const = 0; + virtual CellPortIterator *portIterator(const Cell *cell) const = 0; + // Iterate over port bits (expanded buses). + virtual CellPortBitIterator *portBitIterator(const Cell *cell) const = 0; + // Port bit count (expanded buses). + virtual int portBitCount(const Cell *cell) const = 0; + + //////////////////////////////////////////////////////////////// + // Port functions + virtual const char *name(const Port *port) const = 0; + virtual Cell *cell(const Port *port) const = 0; + virtual LibertyPort *libertyPort(const Port *port) const = 0; + virtual PortDirection *direction(const Port *port) const = 0; + // Bus port functions. + virtual bool isBus(const Port *port) const = 0; + virtual bool isBundle(const Port *port) const = 0; + // 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; + // Bus member, bus[subscript]. + virtual Port *findBusBit(const Port *port, + int index) const = 0; + virtual int fromIndex(const Port *port) const = 0; + virtual int toIndex(const Port *port) const = 0; + // Predicate to determine if subscript is within bus range. + // (toIndex > fromIndex) && fromIndex <= subscript <= toIndex + // || (fromIndex > toIndex) && fromIndex >= subscript >= toIndex + bool busIndexInRange(const Port *port, + int index); + // Find Bundle/bus member by index. + virtual Port *findMember(const Port *port, + 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; + // A port has members if it is a bundle or bus. + virtual bool hasMembers(const Port *port) const; + + //////////////////////////////////////////////////////////////// + // Instance functions + // 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; + // Find instance relative to hierarchical instance. + Instance *findInstanceRelative(const Instance *inst, + const char *path_name) const; + // Default implementation uses linear search. + virtual void findInstancesMatching(const Instance *context, + const PatternMatch *pattern, + // Return value. + InstanceSeq *insts) const; + virtual void findInstancesHierMatching(const Instance *instance, + const PatternMatch *pattern, + // Return value. + InstanceSeq *insts) const; + // Name local to containing cell/instance. + virtual const char *name(const Instance *instance) const = 0; + // Hierarchical path name. + virtual const char *pathName(const Instance *instance) const; + bool pathNameLess(const Instance *inst1, + const Instance *inst2) const; + int pathNameCmp(const Instance *inst1, + const Instance *inst2) const; + // Path from instance up to top level (last in the sequence). + void path(const Instance *inst, + // Return value. + ConstInstanceSeq &path) const; + virtual Cell *cell(const Instance *instance) const = 0; + virtual const char *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; + virtual void findChildrenMatching(const Instance *parent, + const PatternMatch *pattern, + // Return value. + InstanceSeq *insts) const; + // Is inst inside of hier_inst? + bool isInside(const Instance *inst, + 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 + // the other primitives. + LeafInstanceIterator *leafInstanceIterator() const; + LeafInstanceIterator *leafInstanceIterator(const Instance *hier_inst) const; + // Iterate over the children of an instance. + virtual InstanceChildIterator * + childIterator(const Instance *instance) const = 0; + // Iterate over the pins on an instance. + virtual InstancePinIterator * + pinIterator(const Instance *instance) const = 0; + // Iterate over the nets in an instance. + // This should include nets that are connected to the + // instance parent thru pins. + virtual InstanceNetIterator * + netIterator(const Instance *instance) const = 0; + int instanceCount(); + int instanceCount(Instance *inst); + int leafInstanceCount(); + + //////////////////////////////////////////////////////////////// + // Pin functions + virtual Pin *findPin(const char *path_name) const; + virtual Pin *findPin(const Instance *instance, + const char *port_name) const = 0; + virtual Pin *findPin(const Instance *instance, + const Port *port) const; + virtual Pin *findPin(const Instance *instance, + const LibertyPort *port) const; + // Find pin relative to hierarchical instance. + Pin *findPinRelative(const Instance *inst, + const char *path_name) const; + // Default implementation uses linear search. + virtual void findPinsMatching(const Instance *instance, + const PatternMatch *pattern, + // Return value. + PinSeq *pins) const; + // Traverse the hierarchy from instance down and find pins matching + // pattern of the form instance_name/port_name. + virtual void findPinsHierMatching(const Instance *instance, + const PatternMatch *pattern, + // Return value. + PinSeq *pins) const; + // Name is instance_name/port_name (the same as path name). + virtual const char *name(const Pin *pin) const; + virtual const char *portName(const Pin *pin) const; + // Path name is instance_name/port_name. + virtual const char *pathName(const Pin *pin) const; + bool pathNameLess(const Pin *pin1, + const Pin *pin2) const; + int pathNameCmp(const Pin *pin1, + 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; + virtual Net *net(const Pin *pin) const = 0; + 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; + // Is pin inside the instance hier_pin is attached to? + 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; + // Has register/latch rise/fall edges from pin. + bool isRegClkPin(const Pin *pin) const; + // Pin clocks a timing check. + bool isCheckClk(const Pin *pin) const; + 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(Pin *pin, + 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; + // Return the index of the pin graph vertex. + virtual VertexIndex vertexIndex(const Pin *pin) const = 0; + virtual void setVertexIndex(Pin *pin, + VertexIndex index) = 0; + int pinCount(); + int pinCount(Instance *inst); + int leafPinCount(); + + //////////////////////////////////////////////////////////////// + // Terminal functions + // Name is instance_name/port_name (the same as path name). + virtual const char *name(const Term *term) const; + virtual const char *portName(const Term *term) const; + // Path name is instance_name/port_name (pin name). + virtual const char *pathName(const Term *term) const; + virtual Net *net(const Term *term) const = 0; + virtual Pin *pin(const Term *term) const = 0; + + //////////////////////////////////////////////////////////////// + // Net functions + virtual Net *findNet(const char *path_name) const; + // Find net relative to hierarchical instance. + Net *findNetRelative(const Instance *inst, + const char *path_name) const; + // Default implementation uses linear search. + virtual void findNetsMatching(const Instance *context, + const PatternMatch *pattern, + // Return value. + NetSeq *nets) const; + virtual Net *findNet(const Instance *instance, + const char *net_name) const = 0; + // Traverse the hierarchy from instance down and find nets matching + // pattern of the form instance_name/net_name. + virtual void findNetsHierMatching(const Instance *instance, + const PatternMatch *pattern, + // Return value. + NetSeq *nets) const; + virtual void findInstNetsMatching(const Instance *instance, + const PatternMatch *pattern, + // Return value. + NetSeq *nets) const = 0; + virtual const char *name(const Net *net) const = 0; // no hierarchy prefix + virtual const char *pathName(const Net *net) const; + bool pathNameLess(const Net *net1, + const Net *net2) const; + int pathNameCmp(const Net *net1, + const Net *net2) const; + virtual Instance *instance(const Net *net) const = 0; + // Is net inside of hier_inst? + bool isInside(const Net *net, + const Instance *hier_inst) const; + // Is pin connected to net anywhere in the hierarchy? + virtual bool isConnected(const Net *net, + const Pin *pin) const; + // Is net1 connected to net2 anywhere in the hierarchy? + virtual bool isConnected(const Net *net1, + const Net *net2) const; + Net *highestNetAbove(Net *net) const; + Net *highestConnectedNet(Net *net) const; + virtual void connectedNets(Net *net, + NetSet *nets) const; + virtual void connectedNets(const Pin *pin, + NetSet *nets) const; + virtual bool isPower(const Net *net) const = 0; + virtual bool isGround(const Net *net) const = 0; + + // Iterate over the pins connected to a net (port, leaf and hierarchical). + virtual NetPinIterator *pinIterator(const Net *net) const = 0; + // Iterate over the terminals connected to a net. + virtual NetTermIterator *termIterator(const Net *net) const = 0; + // Iterate over all of the pins connected to a net and the parent + // and child nets it is hierarchically connected to (port, leaf and + // hierarchical pins). + virtual NetConnectedPinIterator *connectedPinIterator(const Net *net) const; + virtual void visitConnectedPins(const Net *net, + PinVisitor &visitor) const; + // Find driver pins for net. + // Return value is owned by the network. + virtual PinSet *drivers(const Net *net); + int netCount(); + int netCount(Instance *inst); + + // Iterate over pins connected to nets tied off to logic zero and one. + virtual ConstantPinIterator *constantPinIterator() = 0; + + //////////////////////////////////////////////////////////////// + + // 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; + // 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; + + // Divider between instance names in a hierarchical path name. + virtual char pathDivider() const { return divider_; } + virtual void setPathDivider(char divider); + // Escape prefix for path dividers in path names. + virtual char pathEscape() const { return escape_; } + virtual void setPathEscape(char escape); + +protected: + Pin *findPinLinear(const Instance *instance, + const char *port_name) const; + void findInstancesMatching1(const Instance *context, + size_t context_name_length, + const PatternMatch *pattern, + // Return value. + InstanceSeq *insts) const; + bool isConnected(const Net *net, + const Pin *pin, + ConstNetSet &nets) const; + bool isConnected(const Net *net1, + const Net *net2, + ConstNetSet &nets) const; + int hierarchyLevel(const Net *net) const; + virtual void visitConnectedPins(const Net *net, + PinVisitor &visitor, + ConstNetSet &visited_nets) const; + // Default implementation uses linear search. + virtual void findInstPinsMatching(const Instance *instance, + const PatternMatch *pattern, + // Return value. + PinSeq *pins) const; + void findInstPinsHierMatching(const Instance *parent, + const PatternMatch *pattern, + // Return value. + PinSeq *pins) const; + // findNet using linear search. + Net *findNetLinear(const Instance *instance, + const char *net_name) const; + // findNetsMatching using linear search. + void findNetsMatchingLinear(const Instance *instance, + const PatternMatch *pattern, + // Return value. + NetSeq *nets) const; + // Connect/disconnect net/pins should clear the net->drvrs map. + // Incrementally maintaining the map is expensive because + // nets may be connected across hierarchy levels. + void clearNetDrvPinrMap(); + + LibertyLibrary *default_liberty_; + char divider_; + char escape_; + NetDrvrPinsMap net_drvr_pin_map_; + +private: + DISALLOW_COPY_AND_ASSIGN(Network); +}; + +// Network API to support network edits. +class NetworkEdit : public Network +{ +public: + NetworkEdit(); + virtual bool isEditable() const { return true; } + virtual Instance *makeInstance(LibertyCell *cell, + const char *name, + Instance *parent) = 0; + virtual void makePins(Instance *inst) = 0; + virtual void replaceCell(Instance *inst, + 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; + virtual Pin *connect(Instance *inst, + LibertyPort *port, + Net *net) = 0; + // makePin/connectPin replaced by connect. + virtual void connectPin(Pin *pin, + Net *net) __attribute__ ((deprecated)); + // 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; + // Deleting net disconnects (but does not delete) net pins. + virtual void deleteNet(Net *net) = 0; + virtual void mergeInto(Net *net, + Net *into_net) = 0; + virtual Net *mergedInto(Net *net) = 0; + +private: + DISALLOW_COPY_AND_ASSIGN(NetworkEdit); +}; + +// Network API to support the Parallax readers. +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; + // Search the libraries in read order for a cell by name. + virtual Cell *findAnyCell(const char *name) = 0; + virtual Cell *makeCell(Library *library, + const char *name, + bool is_leaf, + const char *filename) = 0; + virtual void deleteCell(Cell *cell) = 0; + virtual void setName(Cell *cell, + const char *name) = 0; + virtual void setIsLeaf(Cell *cell, + bool is_leaf) = 0; + virtual Port *makePort(Cell *cell, + const char *name) = 0; + virtual Port *makeBusPort(Cell *cell, + const char *name, + int from_index, + int to_index) = 0; + virtual void groupBusPorts(Cell *cell) = 0; + virtual Port *makeBundlePort(Cell *cell, + const char *name, + PortSeq *members) = 0; + virtual Instance *makeInstance(Cell *cell, + const char *name, + Instance *parent) = 0; + virtual Pin *makePin(Instance *inst, + Port *port, + Net *net) = 0; + virtual Term *makeTerm(Pin *pin, + Net *net) = 0; + virtual void setDirection(Port *port, + PortDirection *dir) = 0; + // Instance is the network view for cell. + virtual void setCellNetworkView(Cell *cell, + Instance *inst) = 0; + virtual Instance *cellNetworkView(Cell *cell) = 0; + virtual void deleteCellNetworkViews() = 0; + virtual void addConstantNet(Net *net, + LogicValue const_value) = 0; + + using NetworkEdit::makeInstance; + +private: + DISALLOW_COPY_AND_ASSIGN(NetworkReader); +}; + +Instance * +linkReaderNetwork(Cell *top_cell, + bool make_black_boxes, + Report *report, + NetworkReader *network); + +// Abstract class for Network::constantPinIterator(). +class ConstantPinIterator +{ +public: + ConstantPinIterator() {} + virtual ~ConstantPinIterator() {} + virtual bool hasNext() = 0; + virtual void next(Pin *&pin, + LogicValue &value) = 0; + +private: + DISALLOW_COPY_AND_ASSIGN(ConstantPinIterator); +}; + +// Implementation class for Network::constantPinIterator(). +class NetworkConstantPinIterator : public ConstantPinIterator +{ +public: + NetworkConstantPinIterator(const Network *network, + NetSet &zero_nets, + NetSet &one_nets); + ~NetworkConstantPinIterator(); + virtual bool hasNext(); + virtual void next(Pin *&pin, LogicValue &value); + +private: + DISALLOW_COPY_AND_ASSIGN(NetworkConstantPinIterator); + void findConstantPins(NetSet &nets, + PinSet &pins); + + const Network *network_; + PinSet constant_pins_[2]; + LogicValue value_; + PinSet::Iterator *pin_iter_; +}; + +// Abstract base class for visitDrvrLoadsThruHierPin visitor. +class HierPinThruVisitor +{ +public: + HierPinThruVisitor() {} + virtual ~HierPinThruVisitor() {} + virtual void visit(Pin *drvr, + Pin *load) = 0; + +private: + DISALLOW_COPY_AND_ASSIGN(HierPinThruVisitor); +}; + +class PinVisitor +{ +public: + virtual ~PinVisitor() {} + virtual void operator()(Pin *pin) = 0; +}; + +class FindNetDrvrLoads : public PinVisitor +{ +public: + FindNetDrvrLoads(Pin *drvr_pin, + PinSet &visited_drvrs, + PinSeq &loads, + PinSeq &drvrs, + const Network *network); + virtual void operator()(Pin *pin); + +protected: + Pin *drvr_pin_; + PinSet &visited_drvrs_; + PinSeq &loads_; + PinSeq &drvrs_; + const Network *network_; + +private: + DISALLOW_COPY_AND_ASSIGN(FindNetDrvrLoads); +}; + +// Visit driver/loads pins through a hierarcial pin. +void +visitDrvrLoadsThruHierPin(const Pin *hpin, + const Network *network, + HierPinThruVisitor *visitor); +void +visitDrvrLoadsThruNet(Net *net, + const Network *network, + HierPinThruVisitor *visitor); + +char +logicValueString(LogicValue value); + +} // namespace +#endif diff --git a/network/NetworkClass.hh b/network/NetworkClass.hh new file mode 100644 index 0000000..a2ed164 --- /dev/null +++ b/network/NetworkClass.hh @@ -0,0 +1,81 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_NETWORK_CLASS_H +#define STA_NETWORK_CLASS_H + +#include "Set.hh" +#include "Vector.hh" +#include "Iterator.hh" + +namespace sta { + +class Network; +class NetworkEdit; +class NetworkReader; +class Library; +class Cell; +class Port; +class PortDirection; +class Instance; +class Pin; +class Term; +class Net; +class ConstantPinIterator; +class ViewType; +class LibertyLibrary; + +typedef Iterator LibraryIterator; +typedef Iterator LibertyLibraryIterator; +typedef Vector CellSeq; +typedef Set CellSet; +typedef Vector PortSeq; +typedef Set PortSet; +typedef Iterator CellPortIterator; +typedef Iterator CellPortBitIterator; +typedef Iterator PortMemberIterator; +typedef std::pair PortPair; + +typedef Vector PinSeq; +typedef Vector ConstPinSeq; +typedef Vector InstanceSeq; +typedef Vector ConstInstanceSeq; +typedef Vector NetSeq; +typedef Set PinSet; +typedef Set InstanceSet; +typedef Set NetSet; +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; + +class PortPairLess +{ +public: + bool operator()(const PortPair *pair1, + const PortPair *pair2) const; +}; + +enum class LogicValue : unsigned { zero, one, unknown, rise, fall }; + +} // namespace +#endif diff --git a/network/NetworkCmp.cc b/network/NetworkCmp.cc new file mode 100644 index 0000000..3703dd7 --- /dev/null +++ b/network/NetworkCmp.cc @@ -0,0 +1,122 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "StringUtil.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "NetworkCmp.hh" + +namespace sta { + +PortNameLess::PortNameLess(const Network *network) : + network_(network) +{ +} + +bool +PortNameLess::operator()(const Port *port1, + const Port *port2) const +{ + return stringLess(network_->name(port1), network_->name(port2)); +} + +PinPathNameLess::PinPathNameLess(const Network *network) : + network_(network) +{ +} + +bool +PinPathNameLess::operator()(const Pin *pin1, + const Pin *pin2) const +{ + return network_->pathNameLess(pin1, pin2); +} + +NetPathNameLess::NetPathNameLess(const Network *network) : + network_(network) +{ +} + +bool +NetPathNameLess::operator()(const Net *net1, + const Net *net2) const +{ + return network_->pathNameLess(net1, net2); +} + +InstancePathNameLess::InstancePathNameLess(const Network *network) : + network_(network) +{ +} + +bool +InstancePathNameLess::operator()(const Instance *inst1, + const Instance *inst2) const +{ + return network_->pathNameLess(inst1, inst2); +} + +//////////////////////////////////////////////////////////////// + +void +sortPinSet(PinSet *set, + const Network *network, + PinSeq &pins) +{ + PinSet::Iterator pin_iter(set); + while (pin_iter.hasNext()) + pins.push_back(pin_iter.next()); + sort(pins, PinPathNameLess(network)); +} + +void +sortPortSet(PortSet *set, + const Network *network, + PortSeq &ports) +{ + PortSet::Iterator port_iter(set); + while (port_iter.hasNext()) + ports.push_back(port_iter.next()); + sort(ports, PortNameLess(network)); +} + +void +sortInstanceSet(InstanceSet *set, + const Network *network, + InstanceSeq &insts) +{ + InstanceSet::Iterator inst_iter(set); + while (inst_iter.hasNext()) + insts.push_back(inst_iter.next()); + // Sort ports so regression results are portable. + sort(insts, InstancePathNameLess(network)); +} + +void +sortNetSet(NetSet *set, + const Network *network, + NetSeq &nets) +{ + NetSet::Iterator net_iter(set); + while (net_iter.hasNext()) + nets.push_back(net_iter.next()); + // Sort nets so regression results are netable. + sort(nets, NetPathNameLess(network)); +} + +} // namespace diff --git a/network/NetworkCmp.hh b/network/NetworkCmp.hh new file mode 100644 index 0000000..30d2cae --- /dev/null +++ b/network/NetworkCmp.hh @@ -0,0 +1,89 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_NETWORK_CMP_H +#define STA_NETWORK_CMP_H + +#include "LibertyClass.hh" +#include "NetworkClass.hh" + +namespace sta { + +// Comparison operators for sorting network objects. + +class PortNameLess +{ +public: + explicit PortNameLess(const Network *network); + bool operator()(const Port *port1, + const Port *port2) const; + +private: + const Network *network_; +}; + +class PinPathNameLess +{ +public: + explicit PinPathNameLess(const Network *network); + bool operator()(const Pin *pin1, + const Pin *pin2) const; + +private: + const Network *network_; +}; + +class InstancePathNameLess +{ +public: + explicit InstancePathNameLess(const Network *network); + bool operator()(const Instance *inst1, + const Instance *inst2) const; + +private: + const Network *network_; +}; + +class NetPathNameLess +{ +public: + explicit NetPathNameLess(const Network *network); + bool operator()(const Net *net1, + const Net *net2) const; + +private: + const Network *network_; +}; + +void +sortPinSet(PinSet *set, + const Network *network, + PinSeq &pins); +void +sortPortSet(PortSet *set, + const Network *network, + PortSeq &ports); +void +sortInstanceSet(InstanceSet *set, + const Network *network, + InstanceSeq &insts); +void +sortNetSet(NetSet *set, + const Network *network, + NetSeq &nets); + +} // namespace +#endif diff --git a/network/ParseBus.cc b/network/ParseBus.cc new file mode 100644 index 0000000..fc0306f --- /dev/null +++ b/network/ParseBus.cc @@ -0,0 +1,200 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include +#include "Machine.hh" +#include "StringUtil.hh" +#include "ParseBus.hh" + +namespace sta { + +using std::string; + +bool +isBusName(const char *name, + const char brkt_left, + const char brkt_right, + char escape) +{ + size_t len = strlen(name); + // 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; + } + else + return false; +} + +void +parseBusName(const char *name, + const char brkt_left, + const char brkt_right, + char escape, + // Return values. + char *&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, bus_name, index); +} + +void +parseBusName(const char *name, + const char *brkts_left, + const char *brkts_right, + char escape, + // Return values. + char *&bus_name, + int &index) +{ + bus_name = nullptr; + size_t len = strlen(name); + // 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 bus_name_len = left - name; + bus_name = new char[bus_name_len + 1]; + strncpy(bus_name, name, bus_name_len); + bus_name[bus_name_len] = '\0'; + // Simple bus subscript. + index = atoi(left + 1); + } + } + } +} + +void +parseBusRange(const char *name, + const char brkt_left, + const char brkt_right, + char escape, + // Return values. + char *&bus_name, + int &from, + int &to) +{ + const char brkts_left[2] = {brkt_left, '\0'}; + const char brkts_right[2] = {brkt_right, '\0'}; + parseBusRange(name, brkts_left, brkts_right, escape, bus_name, from, to); +} + +void +parseBusRange(const char *name, + const char *brkts_left, + const char *brkts_right, + char escape, + // Return values. + char *&bus_name, + int &from, + int &to) +{ + bus_name = nullptr; + size_t len = strlen(name); + // Shortest bus range is a[1:0]. + if (len >= 6 + // 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) { + // Check for bus range. + const char range_sep = ':'; + const char *range = strchr(name, range_sep); + if (range) { + size_t bus_name_len = left - name; + bus_name = new char[bus_name_len + 1]; + strncpy(bus_name, name, bus_name_len); + bus_name[bus_name_len] = '\0'; + // No need to terminate bus subscript because atoi stops + // scanning at first non-digit character. + from = atoi(left + 1); + to = atoi(range + 1); + } + } + } + } +} + +const char * +escapeChars(const char *token, + char ch1, + char ch2, + char escape) +{ + int result_length = 0; + bool need_escapes = false; + for (const char *s = token; *s; s++) { + char ch = *s; + 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') + result_length++; + } + else if (ch == ch1 || ch == ch2) { + result_length++; + need_escapes = true; + } + result_length++; + } + if (need_escapes) { + char *result = makeTmpString(result_length + 1); + char *r = result; + for (const char *s = token; *s; s++) { + char ch = *s; + 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') { + *r++ = ch; + *r++ = next_ch; + s++; + } + } + else if (ch == ch1 || ch == ch2) { + *r++ = escape; + *r++ = ch; + } + else + *r++ = ch; + } + *r++ = '\0'; + return result; + } + else + return token; +} + +} // namespace diff --git a/network/ParseBus.hh b/network/ParseBus.hh new file mode 100644 index 0000000..88eeb3d --- /dev/null +++ b/network/ParseBus.hh @@ -0,0 +1,86 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PARSE_BUS_H +#define STA_PARSE_BUS_H + +namespace sta { + +// Return true if name is a bus. +bool +isBusName(const char *name, + const char brkt_left, + const char brkt_right, + char escape); + +// Parse name as a bus. +// signal +// bus_name = nullptr +// bus[bit] +// bus_name = "bus" +// 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. + char *&bus_name, + 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. + char *&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 +parseBusRange(const char *name, + const char brkt_left, + const char brkt_right, + char escape, + // Return values. + char *&bus_name, + int &from, + int &to); +// brkt_lefts and brkt_rights are corresponding strings of legal +// bus brackets such as "[(<" and "])>". +void +parseBusRange(const char *name, + const char *brkts_left, + const char *brkts_right, + char escape, + // Return values. + char *&bus_name, + int &from, + int &to); + +// Insert escapes before ch1 and ch2 in token. +const char * +escapeChars(const char *token, + char ch1, + char ch2, + char escape); + +} // namespace +#endif diff --git a/network/PortDirection.cc b/network/PortDirection.cc new file mode 100644 index 0000000..1e6b4a7 --- /dev/null +++ b/network/PortDirection.cc @@ -0,0 +1,101 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "PortDirection.hh" + +namespace sta { + +PortDirection *PortDirection::input_; +PortDirection *PortDirection::output_; +PortDirection *PortDirection::tristate_; +PortDirection *PortDirection::bidirect_; +PortDirection *PortDirection::internal_; +PortDirection *PortDirection::ground_; +PortDirection *PortDirection::power_; +PortDirection *PortDirection::unknown_; + +void +PortDirection::init() +{ + input_ = new PortDirection("input", 0); + output_ = new PortDirection("output", 1); + tristate_ = new PortDirection("tristate", 2); + bidirect_ = new PortDirection("bidirect", 3); + internal_ = new PortDirection("internal", 4); + ground_ = new PortDirection("ground", 5); + power_ = new PortDirection("power", 6); + unknown_ = new PortDirection("unknown", 7); +} + +void +PortDirection::destroy() +{ + delete input_; + input_ = nullptr; + delete output_; + output_ = nullptr; + delete tristate_; + tristate_ = nullptr; + delete bidirect_; + bidirect_ = nullptr; + delete internal_; + internal_ = nullptr; + delete ground_; + ground_ = nullptr; + delete power_; + power_ = nullptr; + delete unknown_; + unknown_ = nullptr; +} + +PortDirection::PortDirection(const char *name, + int index) : + name_(name), + index_(index) +{ +} + +bool +PortDirection::isAnyInput() const +{ + return this == input_ + || this == bidirect_; +} + +bool +PortDirection::isAnyOutput() const +{ + return this == output_ + || this == tristate_ + || this == bidirect_; +} + +bool +PortDirection::isAnyTristate() const +{ + return this == tristate_ + || this == bidirect_; +} + +bool +PortDirection::isPowerGround() const +{ + return this == ground_ + || this == power_; +} + +} // namespace diff --git a/network/PortDirection.hh b/network/PortDirection.hh new file mode 100644 index 0000000..bac5b40 --- /dev/null +++ b/network/PortDirection.hh @@ -0,0 +1,77 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PORT_DIRECTION_H +#define STA_PORT_DIRECTION_H + +#include "DisallowCopyAssign.hh" +#include "NetworkClass.hh" + +namespace sta { + +class PortDirection +{ +public: + static void init(); + static void destroy(); + // Singleton accessors. + static PortDirection *input() { return input_; } + static PortDirection *output() { return output_; } + static PortDirection *tristate() { return tristate_; } + static PortDirection *bidirect() { return bidirect_; } + static PortDirection *internal() { return internal_; } + static PortDirection *ground() { return ground_; } + static PortDirection *power() { return power_; } + static PortDirection *unknown() { return unknown_; } + const char *name() const { return name_; } + int index() const { return index_; } + bool isInput() const { return this == input_; } + // Input or bidirect. + bool isAnyInput() const; + bool isOutput() const { return this == output_; } + // Output, tristate or bidirect. + bool isAnyOutput() const; + bool isTristate() const { return this == tristate_; } + bool isBidirect() const { return this == bidirect_; } + // Bidirect or tristate. + bool isAnyTristate() const; + bool isGround() const { return this == ground_; } + bool isPower() const { return this == power_; } + // Ground or power. + bool isPowerGround() const; + bool isInternal() const { return this == internal_; } + bool isUnknown() const { return this == unknown_; } + +private: + DISALLOW_COPY_AND_ASSIGN(PortDirection); + PortDirection(const char *name, + int index); + + const char *name_; + int index_; + + static PortDirection *input_; + static PortDirection *output_; + static PortDirection *tristate_; + static PortDirection *bidirect_; + static PortDirection *internal_; + static PortDirection *ground_; + static PortDirection *power_; + static PortDirection *unknown_; +}; + +} // namespace +#endif diff --git a/network/SdcNetwork.cc b/network/SdcNetwork.cc new file mode 100644 index 0000000..98732c0 --- /dev/null +++ b/network/SdcNetwork.cc @@ -0,0 +1,1123 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "StringUtil.hh" +#include "PatternMatch.hh" +#include "ParseBus.hh" +#include "SdcNetwork.hh" + +namespace sta { + +static const char * +escapeDividers(const char *token, + const Network *network); +static const char * +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) +{ + return network_->linkNetwork(top_cell_name, make_black_boxes, report); +} + +Instance * +NetworkNameAdapter::topInstance() const +{ + return network_->topInstance(); +} + +LibraryIterator * +NetworkNameAdapter::libraryIterator() const +{ + return network_->libraryIterator(); +} + +LibertyLibraryIterator * +NetworkNameAdapter::libertyLibraryIterator() const +{ + return network_->libertyLibraryIterator(); +} + +Library * +NetworkNameAdapter::findLibrary(const char *name) +{ + return network_->findLibrary(name); +} + +LibertyLibrary * +NetworkNameAdapter::findLiberty(const char *name) +{ + return network_->findLiberty(name); +} + +LibertyLibrary * +NetworkNameAdapter::findLibertyFilename(const char *filename) +{ + return network_->findLibertyFilename(filename); +} + +const char * +NetworkNameAdapter::name(const Library *library) const +{ + return network_->name(library); +} + +Cell * +NetworkNameAdapter::findCell(const Library *library, + const char *name) const +{ + return network_->findCell(library, name); +} + +void +NetworkNameAdapter::findCellsMatching(const Library *library, + const PatternMatch *pattern, + CellSeq *cells) const +{ + network_->findCellsMatching(library, pattern, cells); +} + +//////////////////////////////////////////////////////////////// + +const char * +NetworkNameAdapter::name(const Cell *cell) const +{ + return network_->name(cell); +} + +Library * +NetworkNameAdapter::library(const Cell *cell) const +{ + return network_->library(cell); +} + +const char * +NetworkNameAdapter::filename(const Cell *cell) +{ + return network_->filename(cell); +} + +LibertyCell * +NetworkNameAdapter::libertyCell(Cell *cell) const +{ + return network_->libertyCell(cell); +} + +const LibertyCell * +NetworkNameAdapter::libertyCell(const Cell *cell) const +{ + return network_->libertyCell(cell); +} + +Cell * +NetworkNameAdapter::cell(LibertyCell *cell) const +{ + return network_->cell(cell); +} + +const Cell * +NetworkNameAdapter::cell(const LibertyCell *cell) const +{ + return network_->cell(cell); +} + +Port * +NetworkNameAdapter::findPort(const Cell *cell, + const char *name) const +{ + return network_->findPort(cell, name); +} + +void +NetworkNameAdapter::findPortsMatching(const Cell *cell, + const PatternMatch *pattern, + PortSeq *ports) const +{ + network_->findPortsMatching(cell, pattern, ports); +} + +bool +NetworkNameAdapter::isLeaf(const Cell *cell) const +{ + return network_->isLeaf(cell); +} + +CellPortIterator * +NetworkNameAdapter::portIterator(const Cell *cell) const +{ + return network_->portIterator(cell); +} + +CellPortBitIterator * +NetworkNameAdapter::portBitIterator(const Cell *cell) const +{ + return network_->portBitIterator(cell); +} + +int +NetworkNameAdapter::portBitCount(const Cell *cell) const +{ + return network_->portBitCount(cell); +} + +//////////////////////////////////////////////////////////////// + +const char * +NetworkNameAdapter::name(const Port *port) const +{ + return network_->name(port); +} + +Cell * +NetworkNameAdapter::cell(const Port *port) const +{ + return network_->cell(port); +} + +LibertyPort * +NetworkNameAdapter::libertyPort(const Port *port) const +{ + return network_->libertyPort(port); +} + +PortDirection * +NetworkNameAdapter::direction(const Port *port) const +{ + return network_->direction(port); +} + +VertexIndex +NetworkNameAdapter::vertexIndex(const Pin *pin) const +{ + return network_->vertexIndex(pin); +} + +void +NetworkNameAdapter::setVertexIndex(Pin *pin, + VertexIndex index) +{ + network_->setVertexIndex(pin, index); +} + +bool +NetworkNameAdapter::isBundle(const Port *port) const +{ + return network_->isBundle(port); +} + +bool +NetworkNameAdapter::isBus(const Port *port) const +{ + return network_->isBus(port); +} + +const char * +NetworkNameAdapter::busName(const Port *port) const +{ + return network_->busName(port); +} + +Port * +NetworkNameAdapter::findBusBit(const Port *port, + int index) const +{ + return network_->findMember(port, index); +} + +int +NetworkNameAdapter::size(const Port *port) const +{ + return network_->size(port); +} + +int +NetworkNameAdapter::fromIndex(const Port *port) const +{ + return network_->fromIndex(port); +} + +int +NetworkNameAdapter::toIndex(const Port *port) const +{ + return network_->toIndex(port); +} + +bool +NetworkNameAdapter::hasMembers(const Port *port) const +{ + return network_->hasMembers(port); +} + +Port * +NetworkNameAdapter::findMember(const Port *port, + int index) const +{ + return network_->findMember(port, index); +} + +PortMemberIterator * +NetworkNameAdapter::memberIterator(const Port *port) const +{ + return network_->memberIterator(port); +} + +//////////////////////////////////////////////////////////////// + +Cell * +NetworkNameAdapter::cell(const Instance *instance) const +{ + return network_->cell(instance); +} + +Instance * +NetworkNameAdapter::parent(const Instance *instance) const +{ + return network_->parent(instance); +} + +bool +NetworkNameAdapter::isLeaf(const Instance *instance) const +{ + return network_->isLeaf(instance); +} + +Pin * +NetworkNameAdapter::findPin(const Instance *instance, + const Port *port) const +{ + return network_->findPin(instance, port); +} + +Pin * +NetworkNameAdapter::findPin(const Instance *instance, + const LibertyPort *port) const +{ + return network_->findPin(instance, port); +} + +InstanceChildIterator * +NetworkNameAdapter::childIterator(const Instance *instance) const +{ + return network_->childIterator(instance); +} + +InstancePinIterator * +NetworkNameAdapter::pinIterator(const Instance *instance) const +{ + return network_->pinIterator(instance); +} + +InstanceNetIterator * +NetworkNameAdapter::netIterator(const Instance *instance) const +{ + return network_->netIterator(instance); +} + +Port * +NetworkNameAdapter::port(const Pin *pin) const +{ + return network_->port(pin); +} + +Instance * +NetworkNameAdapter::instance(const Pin *pin) const +{ + return network_->instance(pin); +} + +Net * +NetworkNameAdapter::net(const Pin *pin) const +{ + return network_->net(pin); +} + +Term * +NetworkNameAdapter::term(const Pin *pin) const +{ + return network_->term(pin); +} + +PortDirection * +NetworkNameAdapter::direction(const Pin *pin) const +{ + return network_->direction(pin); +} + +Net * +NetworkNameAdapter::net(const Term *term) const +{ + return network_->net(term); +} + +Pin * +NetworkNameAdapter::pin(const Term *term) const +{ + return network_->pin(term); +} + +Instance * +NetworkNameAdapter::instance(const Net *net) const +{ + return network_->instance(net); +} + +NetPinIterator * +NetworkNameAdapter::pinIterator(const Net *net) const +{ + return network_->pinIterator(net); +} + +NetTermIterator * +NetworkNameAdapter::termIterator(const Net *net) const +{ + return network_->termIterator(net); +} + +bool +NetworkNameAdapter::isPower(const Net *net) const +{ + return network_->isPower(net); +} + +bool +NetworkNameAdapter::isGround(const Net *net) const +{ + return network_->isGround(net); +} + +ConstantPinIterator * +NetworkNameAdapter::constantPinIterator() +{ + return network_->constantPinIterator(); +} + +char +NetworkNameAdapter::pathDivider() const +{ + return network_->pathDivider(); +} + +void +NetworkNameAdapter::setPathDivider(char divider) +{ + network_->setPathDivider(divider); +} + +char +NetworkNameAdapter::pathEscape() const +{ + return network_->pathEscape(); +} + +void +NetworkNameAdapter::setPathEscape(char escape) +{ + network_->setPathEscape(escape); +} + +bool +NetworkNameAdapter::isEditable() const +{ + return network_->isEditable(); +} + + +LibertyLibrary * +NetworkNameAdapter::makeLibertyLibrary(const char *name, + const char *filename) +{ + return network_edit_->makeLibertyLibrary(name, filename); +} + +Instance * +NetworkNameAdapter::makeInstance(LibertyCell *cell, + const char *name, + Instance *parent) +{ + return network_edit_->makeInstance(cell, name, parent); +} + +void +NetworkNameAdapter::makePins(Instance *inst) +{ + network_edit_->makePins(inst); +} + +void +NetworkNameAdapter::mergeInto(Net *net, + Net *into_net) +{ + network_edit_->mergeInto(net, into_net); +} + +Net * +NetworkNameAdapter::mergedInto(Net *net) +{ + return network_edit_->mergedInto(net); +} + +Net * +NetworkNameAdapter::makeNet(const char *name, + Instance *parent) +{ + return network_edit_->makeNet(name, parent); +} + +void +NetworkNameAdapter::replaceCell(Instance *inst, + Cell *to_cell) +{ + network_edit_->replaceCell(inst, to_cell); +} + +Pin * +NetworkNameAdapter::connect(Instance *inst, + Port *port, + Net *net) +{ + return network_edit_->connect(inst, port, net); +} + +Pin * +NetworkNameAdapter::connect(Instance *inst, + LibertyPort *port, + Net *net) +{ + return network_edit_->connect(inst, port, net); +} + +void +NetworkNameAdapter::disconnectPin(Pin *pin) +{ + network_edit_->disconnectPin(pin); +} + +void +NetworkNameAdapter::deleteNet(Net *net) +{ + network_edit_->deleteNet(net); +} + +void +NetworkNameAdapter::deletePin(Pin *pin) +{ + network_edit_->deletePin(pin); +} + +void +NetworkNameAdapter::deleteInstance(Instance *inst) +{ + network_edit_->deleteInstance(inst); +} + +//////////////////////////////////////////////////////////////// + +Network * +makeSdcNetwork(Network *network) +{ + return new SdcNetwork(network); +} + +SdcNetwork::SdcNetwork(Network *network) : + NetworkNameAdapter(network) +{ +} + +// Translate sta namespace to sdc namespace. +// Remove all escapes. +const char * +SdcNetwork::staToSdc(const char *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]; + if (ch == escape) { + char next_ch = s[1]; + // Escaped escape. + if (next_ch == escape) { + *d++ = ch; + *d++ = next_ch; + s++; + } + } + else + // Non escape. + *d++ = ch; + } + *d++ = '\0'; + return sdc_name; +} + +Port * +SdcNetwork::findPort(const Cell *cell, + const char *name) const +{ + Port *port = network_->findPort(cell, name); + if (port == nullptr) { + // Look for matches after escaping brackets. + char *bus_name; + int index; + parseBusName(name, '[', ']', pathEscape(), bus_name, index); + if (bus_name) { + const char *escaped1 = escapeBrackets(name, this); + port = network_->findPort(cell, escaped1); + if (port == nullptr + && bus_name[strlen(bus_name) - 1] == ']') { + // Try escaping base foo\[0\][1] + const char *escaped2 = stringPrintTmp("%s[%d]", + escapeBrackets(bus_name, this), + index); + port = network_->findPort(cell, escaped2); + } + stringDelete(bus_name); + } + } + return port; +} + +void +SdcNetwork::findPortsMatching(const Cell *cell, + const PatternMatch *pattern, + PortSeq *ports) const +{ + network_->findPortsMatching(cell, pattern, ports); + if (ports->empty()) { + // Look for matches after escaping brackets. + char *bus_name; + int index; + parseBusName(pattern->pattern(), '[', ']', pathEscape(), bus_name, index); + if (bus_name) { + const char *escaped1 = escapeBrackets(pattern->pattern(), this); + PatternMatch escaped_pattern1(escaped1, pattern); + network_->findPortsMatching(cell, &escaped_pattern1, ports); + if (ports->empty() + && bus_name[strlen(bus_name) - 1] == ']') { + // Try escaping base foo\[0\][1] + const char *escaped2 = stringPrintTmp("%s[%d]", + escapeBrackets(bus_name, this), + index); + PatternMatch escaped_pattern2(escaped2, pattern); + network_->findPortsMatching(cell, &escaped_pattern2, ports); + } + stringDelete(bus_name); + } + } +} + +const char * +SdcNetwork::name(const Port *port) const +{ + return staToSdc(network_->name(port)); +} + +const char * +SdcNetwork::busName(const Port *port) const +{ + return staToSdc(network_->busName(port)); +} + +const char * +SdcNetwork::name(const Instance *instance) const +{ + return staToSdc(network_->name(instance)); +} + +const char * +SdcNetwork::pathName(const Instance *instance) const +{ + return staToSdc(network_->pathName(instance)); +} + +const char * +SdcNetwork::pathName(const Pin *pin) const +{ + return staToSdc(network_->pathName(pin)); +} + +const char * +SdcNetwork::portName(const Pin *pin) const +{ + return staToSdc(network_->portName(pin)); +} + +const char * +SdcNetwork::name(const Net *net) const +{ + return staToSdc(network_->name(net)); +} + +const char * +SdcNetwork::pathName(const Net *net) const +{ + return staToSdc(network_->pathName(net)); +} + +//////////////////////////////////////////////////////////////// + +Instance * +SdcNetwork::findInstance(const char *path_name) const +{ + const char *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) + child = findChild(parent, escapeDividers(child_name, this)); + return child; +} + +void +SdcNetwork::findInstancesMatching(const Instance *context, + const PatternMatch *pattern, + InstanceSeq *insts) const +{ + visitMatches(context, pattern, + [&](const Instance *instance, + const PatternMatch *tail) + { + size_t match_count = insts->size(); + network_->findChildrenMatching(instance, tail, insts); + return insts->size() != match_count; + }); +} + +Instance * +SdcNetwork::findChild(const Instance *parent, + const char *name) const +{ + Instance *child = network_->findChild(parent, name); + if (child == nullptr) { + const char *escaped = escapeBrackets(name, this); + child = network_->findChild(parent, escaped); + } + return child; +} + +//////////////////////////////////////////////////////////////// + +Net * +SdcNetwork::findNet(const char *path_name) const +{ + const char *net_name; + Instance *inst; + parsePath(path_name, inst, net_name); + if (inst == nullptr) + inst = network_->topInstance(); + return findNet(inst, net_name); +} + +Net * +SdcNetwork::findNet(const Instance *instance, + const char *net_name) const +{ + Net *net = network_->findNet(instance, net_name); + if (net == nullptr) { + const char *net_name_ = escapeBrackets(net_name, this); + net = network_->findNet(instance, net_name_); + } + return net; +} + +void +SdcNetwork::findNetsMatching(const Instance *parent, + const PatternMatch *pattern, + NetSeq *nets) const +{ + visitMatches(parent, pattern, + [&](const Instance *instance, + const PatternMatch *tail) + { + size_t match_count = nets->size(); + network_->findInstNetsMatching(instance, tail, nets); + return nets->size() != match_count; + }); +} + +void +SdcNetwork::findInstNetsMatching(const Instance *instance, + const PatternMatch *pattern, + NetSeq *nets) const +{ + network_->findInstNetsMatching(instance, pattern, nets); + if (nets->empty()) { + // Look for matches after escaping path dividers. + const PatternMatch escaped_dividers(escapeDividers(pattern->pattern(), + this), + pattern); + network_->findInstNetsMatching(instance, &escaped_dividers, nets); + if (nets->empty()) { + // Look for matches after escaping brackets. + const PatternMatch escaped_brkts(escapeBrackets(pattern->pattern(),this), + pattern); + network_->findInstNetsMatching(instance, &escaped_brkts, nets); + } + } +} + +//////////////////////////////////////////////////////////////// + +Pin * +SdcNetwork::findPin(const char *path_name) const +{ + const char *port_name; + Instance *inst; + parsePath(path_name, inst, port_name); + if (inst == nullptr) + inst = network_->topInstance(); + return findPin(inst, port_name); +} + +Pin * +SdcNetwork::findPin(const Instance *instance, + const char *port_name) const +{ + Pin *pin = network_->findPin(instance, port_name); + if (pin == nullptr) { + // Look for match after escaping brackets. + char *bus_name; + int index; + parseBusName(port_name, '[', ']', pathEscape(), bus_name, index); + if (bus_name) { + const char *escaped1 = escapeBrackets(port_name, this); + pin = network_->findPin(instance, escaped1); + if (pin == nullptr + && bus_name[strlen(bus_name) - 1] == ']') { + // Try escaping base foo\[0\][1] + const char *escaped2 = stringPrintTmp("%s[%d]", + escapeBrackets(bus_name, this), + index); + pin = network_->findPin(instance, escaped2); + } + stringDelete(bus_name); + } + } + return pin; +} + +// Top level ports are not considered pins by get_pins. +void +SdcNetwork::findPinsMatching(const Instance *instance, + const PatternMatch *pattern, + PinSeq *pins) const +{ + if (stringEq(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()) { + Pin *pin = pin_iter->next(); + pins->push_back(pin); + } + delete pin_iter; + } + delete child_iter; + } + else + visitMatches(instance, pattern, + [&](const Instance *instance, + const PatternMatch *tail) + { + return visitPinTail(instance, tail, pins); + }); +} + +bool +SdcNetwork::visitPinTail(const Instance *instance, + const PatternMatch *tail, + PinSeq *pins) const +{ + bool found_match = false; + if (instance != network_->topInstance()) { + Cell *cell = network_->cell(instance); + CellPortIterator *port_iter = network_->portIterator(cell); + while (port_iter->hasNext()) { + Port *port = port_iter->next(); + const char *port_name = network_->name(port); + if (network_->hasMembers(port)) { + bool bus_matches = tail->match(port_name) + || tail->match(escapeDividers(port_name, network_)); + PortMemberIterator *member_iter = network_->memberIterator(port); + while (member_iter->hasNext()) { + Port *member_port = member_iter->next(); + Pin *pin = network_->findPin(instance, member_port); + if (pin) { + if (bus_matches) { + pins->push_back(pin); + found_match = true; + } + else { + const char *member_name = network_->name(member_port); + if (tail->match(member_name) + || tail->match(escapeDividers(member_name, network_))) { + pins->push_back(pin); + found_match = true; + } + } + } + } + delete member_iter; + } + else if (tail->match(port_name) + || tail->match(escapeDividers(port_name, network_))) { + Pin *pin = network_->findPin(instance, port); + if (pin) { + pins->push_back(pin); + found_match = true; + } + } + } + delete port_iter; + } + return found_match; +} + +Instance * +SdcNetwork::makeInstance(LibertyCell *cell, + const char *name, + Instance *parent) +{ + const char *escaped_name = escapeDividers(name, this); + return network_edit_->makeInstance(cell, escaped_name, parent); +} + +Net * +SdcNetwork::makeNet(const char *name, + Instance *parent) +{ + const char *escaped_name = escapeDividers(name, this); + return network_edit_->makeNet(escaped_name, parent); +} + +//////////////////////////////////////////////////////////////// + +// Helper to parse an instance path (with optional net/port tail). +// Since dividers are not escaped in SDC, look for an instance for +// each sub-section of the path. If none is found, escape the divider +// and keep looking. For the path a/b/c this looks for instances +// a +// a\/b +// a\/b\/c +void +SdcNetwork::parsePath(const char *path, + // Return values. + Instance *&inst, + const char *&path_tail) const +{ + int divider_count, path_length; + scanPath(path, divider_count, path_length); + if (divider_count > 0) + parsePath(path, divider_count, path_length, inst, path_tail); + else { + inst = nullptr; + path_tail = 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 +{ + divider_count = 0; + path_length = 0; + for (const char *s = path; *s; s++) { + char ch = *s; + if (ch == escape_) { + // Make sure we don't skip the null if escape is the last char. + if (s[1] != '\0') { + s++; + path_length++; + } + } + else if (ch == divider_) + divider_count++; + path_length++; + } +} + +void +SdcNetwork::parsePath(const char *path, + int divider_count, + int path_length, + // Return values. + Instance *&inst, + const char *&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]; + inst = nullptr; + path_tail = path; + char *p = inst_path; + for (const char *s = path; *s; s++) { + char ch = *s; + 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++; + } + } + 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; + } + else { + // No match for sub-path. Escape the divider and keep looking. + *p++ = escape_; + *p++ = divider_; + } + } + else + *p++ = ch; + if (p - inst_path + 1 > inst_path_length) + internalError("inst path string lenth estimate busted"); + } + *p = '\0'; + stringDelete(inst_path); +} + +// Helper to visit instance path matches. +// Since dividers are not escaped in SDC, look for instance matches for +// each sub-section of the path. If none are found, escape the divider +// and keep looking. For the path a/b/c this looks for instances +// a +// a\/b +// a\/b\/c +bool +SdcNetwork::visitMatches(const Instance *parent, + const PatternMatch *pattern, + const std::function + visit_tail) const +{ + int divider_count, path_length; + scanPath(pattern->pattern(), divider_count, path_length); + + // 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; + bool has_brkts = false; + bool found_match = false; + for (const char *s = pattern->pattern(); *s; s++) { + char ch = *s; + 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++; + } + } + else if (ch == divider_) { + // Terminate the sub-path up to this divider. + *p = '\0'; + PatternMatch matcher(inst_path, pattern); + InstanceSeq matches; + findChildrenMatching(parent, &matcher, &matches); + if (has_brkts && matches.empty()) { + // Look for matches after escaping brackets. + const PatternMatch escaped_brkts(escapeBrackets(inst_path, this), + pattern); + network_->findChildrenMatching(parent, &escaped_brkts, &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()) { + 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); + } + } + // Escape the divider and keep looking. + *p++ = escape_; + *p++ = divider_; + } + else { + if (ch == '[' || ch == ']') + has_brkts = true; + *p++ = ch; + } + if (p - inst_path + 1 > inst_path_length) + internalError("inst path string lenth estimate busted"); + } + *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. + char *escaped_path = stringCopy(escapeBrackets(inst_path, this)); + const PatternMatch escaped_tail(escaped_path, pattern); + found_match |= visit_tail(parent, &escaped_tail); + stringDelete(escaped_path); + } + } + stringDelete(inst_path); + return found_match; +} + +//////////////////////////////////////////////////////////////// + +static const char * +escapeDividers(const char *token, + const Network *network) +{ + return escapeChars(token, network->pathDivider(), '\0', + network->pathEscape()); +} + +static const char * +escapeBrackets(const char *token, + const Network *network) +{ + return escapeChars(token, '[', ']', network->pathEscape()); +} + +} // namespace diff --git a/network/SdcNetwork.hh b/network/SdcNetwork.hh new file mode 100644 index 0000000..b23f0e1 --- /dev/null +++ b/network/SdcNetwork.hh @@ -0,0 +1,262 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SDC_NETWORK_H +#define STA_SDC_NETWORK_H + +#include +#include "DisallowCopyAssign.hh" +#include "Network.hh" + +namespace sta { + +// Base class to encapsulate a network to map names to/from a namespace. +// It forwards member functions to the encapsulated network. +class NetworkNameAdapter : public NetworkEdit +{ +public: + NetworkNameAdapter(Network *network); + virtual bool linkNetwork(const char *top_cell_name, + bool make_black_boxes, + Report *report); + + virtual LibraryIterator *libraryIterator() const; + virtual LibertyLibraryIterator *libertyLibraryIterator() const; + virtual Library *findLibrary(const char *name); + virtual LibertyLibrary *findLibertyFilename(const char *filename); + virtual LibertyLibrary *findLiberty(const char *name); + virtual const char *name(const Library *library) const; + virtual Cell *findCell(const Library *library, + const char *name) const; + virtual void findCellsMatching(const Library *library, + const PatternMatch *pattern, + CellSeq *cells) const; + + virtual const char *name(const Cell *cell) const; + virtual Library *library(const Cell *cell) const; + virtual LibertyCell *libertyCell(Cell *cell) const; + virtual const LibertyCell *libertyCell(const Cell *cell) const; + virtual Cell *cell(LibertyCell *cell) const; + virtual const Cell *cell(const LibertyCell *cell) const; + virtual const char *filename(const Cell *cell); + virtual Port *findPort(const Cell *cell, + const char *name) const; + virtual void findPortsMatching(const Cell *cell, + const PatternMatch *pattern, + PortSeq *ports) const; + virtual bool isLeaf(const Cell *cell) const; + virtual CellPortIterator *portIterator(const Cell *cell) const; + virtual CellPortBitIterator *portBitIterator(const Cell *cell) const; + virtual int portBitCount(const Cell *cell) const; + + virtual const char *name(const Port *port) const; + virtual Cell *cell(const Port *port) const; + virtual LibertyPort *libertyPort(const Port *port) const; + virtual PortDirection *direction(const Port *port) const; + virtual bool isBundle(const Port *port) const; + virtual bool isBus(const Port *port) const; + virtual int size(const Port *port) const; + virtual const char *busName(const Port *port) const; + virtual Port *findBusBit(const Port *port, + int index) const; + virtual int fromIndex(const Port *port) const; + virtual int toIndex(const Port *port) const; + virtual Port *findMember(const Port *port, + int index) const; + virtual PortMemberIterator *memberIterator(const Port *port) const; + virtual bool hasMembers(const Port *port) const; + + virtual Instance *topInstance() const; + virtual Cell *cell(const Instance *instance) const; + virtual Instance *parent(const Instance *instance) const; + virtual bool isLeaf(const Instance *instance) const; + virtual Pin *findPin(const Instance *instance, + const Port *port) const; + virtual Pin *findPin(const Instance *instance, + const LibertyPort *port) const; + + virtual InstanceChildIterator * + childIterator(const Instance *instance) const; + virtual InstancePinIterator * + pinIterator(const Instance *instance) const; + virtual InstanceNetIterator * + netIterator(const Instance *instance) const; + + virtual Port *port(const Pin *pin) const; + virtual Instance *instance(const Pin *pin) const; + virtual Net *net(const Pin *pin) const; + virtual Term *term(const Pin *pin) const; + virtual PortDirection *direction(const Pin *pin) const; + virtual VertexIndex vertexIndex(const Pin *pin) const; + virtual void setVertexIndex(Pin *pin, + VertexIndex index); + + virtual Net *net(const Term *term) const; + virtual Pin *pin(const Term *term) const; + + virtual Instance *instance(const Net *net) const; + virtual bool isPower(const Net *net) const; + virtual bool isGround(const Net *net) const; + virtual NetPinIterator *pinIterator(const Net *net) const; + virtual NetTermIterator *termIterator(const Net *net) const; + + virtual ConstantPinIterator *constantPinIterator(); + + virtual char pathDivider() const; + virtual void setPathDivider(char divider); + virtual char pathEscape() const; + virtual void setPathEscape(char escape); + + + virtual bool isEditable() const; + virtual LibertyLibrary *makeLibertyLibrary(const char *name, + const char *filename); + virtual Instance *makeInstance(LibertyCell *cell, + const char *name, + Instance *parent); + virtual void makePins(Instance *inst); + virtual void replaceCell(Instance *inst, + Cell *to_cell); + virtual Net *makeNet(const char *name, + Instance *parent); + virtual Pin *connect(Instance *inst, + Port *port, + Net *net); + virtual Pin *connect(Instance *inst, + LibertyPort *port, + Net *net); + virtual void disconnectPin(Pin *pin); + virtual void deleteNet(Net *net); + virtual void deletePin(Pin *pin); + virtual void deleteInstance(Instance *inst); + virtual void mergeInto(Net *net, + Net *into_net); + virtual Net *mergedInto(Net *net); + + // Any overloaded functions from the base class must be listed here. + using Network::name; + using Network::isLeaf; + using Network::netIterator; + using Network::findPin; + using Network::libertyLibrary; + using Network::libertyCell; + using Network::libertyPort; + +protected: + // Network that is being adapted to the sdc namespace. + Network *network_; + // network_ if it supports edits. + NetworkEdit *network_edit_; + +private: + DISALLOW_COPY_AND_ASSIGN(NetworkNameAdapter); +}; + +//////////////////////////////////////////////////////////////// + +// Encapsulate a network to map names to/from the sdc namespace. +class SdcNetwork : public NetworkNameAdapter +{ +public: + SdcNetwork(Network *network); + + virtual Port *findPort(const Cell *cell, + const char *name) const; + virtual void findPortsMatching(const Cell *cell, + const PatternMatch *pattern, + PortSeq *ports) const; + virtual const char *name(const Port *port) const; + virtual const char *busName(const Port *port) const; + + virtual const char *name(const Instance *instance) const; + virtual const char *pathName(const Instance *instance) const; + virtual const char *pathName(const Pin *pin) const; + virtual const char *portName(const Pin *pin) const; + virtual const char *name(const Net *net) const; + virtual const char *pathName(const Net *net) const; + + virtual Instance *findInstance(const char *path_name) const; + virtual void findInstancesMatching(const Instance *context, + const PatternMatch *pattern, + InstanceSeq *insts) const; + virtual Net *findNet(const char *path_name) const; + virtual Net *findNet(const Instance *instance, + const char *net_name) const; + virtual void findNetsMatching(const Instance *parent, + const PatternMatch *pattern, + NetSeq *nets) const; + virtual void findInstNetsMatching(const Instance *instance, + const PatternMatch *pattern, + NetSeq *nets) const; + virtual Instance *findChild(const Instance *parent, + const char *name) const; + virtual Pin *findPin(const char *path_name) const; + virtual Pin *findPin(const Instance *instance, + const char *port_name) const; + virtual void findPinsMatching(const Instance *instance, + const PatternMatch *pattern, + PinSeq *pins) const; + + virtual Instance *makeInstance(LibertyCell *cell, + const char *name, + Instance *parent); + virtual Net *makeNet(const char *name, + Instance *parent); + + // The following member functions are inherited from the + // Network class work as is: + // findInstPinsMatching(instance, pattern) + + using Network::name; + 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; + bool visitMatches(const Instance *parent, + const PatternMatch *pattern, + std::function + visit_tail) const; + bool visitPinTail(const Instance *instance, + const PatternMatch *tail, + PinSeq *pins) const; + + const char *staToSdc(const char *sta_name) const; + +private: + DISALLOW_COPY_AND_ASSIGN(SdcNetwork); +}; + +// Encapsulate a network to map names to/from the sdc namespace. +Network * +makeSdcNetwork(Network *network); + +} // namespace +#endif diff --git a/network/VerilogNamespace.cc b/network/VerilogNamespace.cc new file mode 100644 index 0000000..038e859 --- /dev/null +++ b/network/VerilogNamespace.cc @@ -0,0 +1,154 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "StringUtil.hh" +#include "ParseBus.hh" +#include "VerilogNamespace.hh" + +namespace sta { + +const char * +instanceVerilogName(const char *sta_name, + const char escape) +{ + return staToVerilog(sta_name, escape); +} + +const char * +netVerilogName(const char *sta_name, + const char escape) +{ + char *bus_name; + int index; + parseBusName(sta_name, '[', ']', escape, bus_name, index); + if (bus_name) { + const char *vname = stringPrintTmp("%s[%d]", + staToVerilog(bus_name, escape), + index); + stringDelete(bus_name); + return vname; + } + else + return staToVerilog(sta_name, escape); +} + +const char * +portVerilogName(const char *sta_name, + const char escape) +{ + return staToVerilog(sta_name, escape); +} + +// Append ch to str at insert. Resize str if necessary. +static inline void +vstringAppend(char *&str, + char *&str_end, + char *&insert, + char ch) +{ + if (insert == str_end) { + size_t length = str_end - str; + size_t length2 = length * 2; + char *new_str = makeTmpString(length2); + strncpy(new_str, str, length); + str = new_str; + str_end = &str[length2]; + insert = &str[length]; + } + *insert++ = ch; +} + +const char * +staToVerilog(const char *sta_name, + const char escape) +{ + const char bus_brkt_left = '['; + const char bus_brkt_right = ']'; + // Leave room for leading escape and trailing space if the name + // needs to be escaped. + size_t verilog_name_length = strlen(sta_name) + 3; + char *verilog_name = makeTmpString(verilog_name_length); + char *verilog_name_end = &verilog_name[verilog_name_length]; + char *v = verilog_name; + // Assume the name has to be escaped and start copying while scanning. + bool escaped = false; + *v++ = '\\'; + for (const char *s = sta_name; *s ; s++) { + char ch = s[0]; + if (ch == escape) { + char next_ch = s[1]; + if (next_ch == escape) { + vstringAppend(verilog_name, verilog_name_end, v, ch); + vstringAppend(verilog_name, verilog_name_end, v, next_ch); + s++; + } + else + // Skip escape. + escaped = true; + } + else { + bool is_brkt = (ch == bus_brkt_left || ch == bus_brkt_right); + if ((!(isalnum(ch) || ch == '_') && !is_brkt) + || is_brkt) + escaped = true; + vstringAppend(verilog_name, verilog_name_end, v, ch); + } + } + if (escaped) { + // Add a terminating space. + vstringAppend(verilog_name, verilog_name_end, v, ' '); + vstringAppend(verilog_name, verilog_name_end, v, '\0'); + return verilog_name; + } + else + return sta_name; +} + +const char * +verilogToSta(const char *verilog_name) +{ + if (verilog_name[0] == '\\') { + const char divider = '/'; + const char escape = '\\'; + const char bus_brkt_left = '['; + const char bus_brkt_right = ']'; + size_t sta_name_length = strlen(verilog_name) + 1; + char *sta_name = makeTmpString(sta_name_length); + char *sta_name_end = &sta_name[sta_name_length]; + char *s = sta_name; + for (const char *v = &verilog_name[1]; *v ; v++) { + char ch = *v; + if (ch == divider + || ch == bus_brkt_left + || ch == bus_brkt_right + || ch == escape) + // Escape dividers, bus brackets and escapes. + vstringAppend(sta_name, sta_name_end, s, escape); + vstringAppend(sta_name, sta_name_end, s, ch); + // Don't include the last character, which is always a space. + if (v[2] == '\0') + break; + } + vstringAppend(sta_name, sta_name_end, s, '\0'); + return sta_name; + } + else + return verilog_name; +} + +} // namespace diff --git a/network/VerilogNamespace.hh b/network/VerilogNamespace.hh new file mode 100644 index 0000000..dec08f7 --- /dev/null +++ b/network/VerilogNamespace.hh @@ -0,0 +1,38 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_VERILOG_NAMESPACE_H +#define STA_VERILOG_NAMESPACE_H + +namespace sta { + +const char * +staToVerilog(const char *sta_name, + const char escape); +const char * +verilogToSta(const char *verilog_name); +const char * +instanceVerilogName(const char *sta_name, + const char escape); +const char * +netVerilogName(const char *sta_name, + const char escape); +const char * +portVerilogName(const char *sta_name, + const char escape); + +} // namespace +#endif diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc new file mode 100644 index 0000000..96973c4 --- /dev/null +++ b/parasitics/ConcreteParasitics.cc @@ -0,0 +1,1707 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include // max +#include "Machine.hh" +#include "Report.hh" +#include "Debug.hh" +#include "Error.hh" +#include "Mutex.hh" +#include "Set.hh" +#include "MinMax.hh" +#include "Network.hh" +#include "Wireload.hh" +#include "Liberty.hh" +#include "Sdc.hh" +#include "Parasitics.hh" +#include "ReduceParasitics.hh" +#include "MakeConcreteParasitics.hh" +#include "ConcreteParasitics.hh" +#include "Parasitics.hh" +#include "Corner.hh" +#include "ConcreteParasiticsPvt.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 +// inheritance graph (ConcreteParasitic only included once). + +namespace sta { + +using std::max; + +ConcreteParasitic::~ConcreteParasitic() +{ +} + +bool +ConcreteParasitic::isPiElmore() const +{ + return false; +} + +bool +ConcreteParasitic::isPiModel() const +{ + return false; +} + +bool +ConcreteParasitic::isPiPoleResidue() const +{ + return false; +} + +bool +ConcreteParasitic::isPoleResidue() const +{ + return false; +} + +bool +ConcreteParasitic::isParasiticNetwork() const +{ + return false; +} + +void +ConcreteParasitic::piModel(float &, + float &, + float &) const +{ +} + +void +ConcreteParasitic::setPiModel(float, + float, + float) +{ +} + +bool +ConcreteParasitic::isReducedParasiticNetwork() const +{ + return false; +} + +void +ConcreteParasitic::setIsReduced(bool) +{ +} + +void +ConcreteParasitic::findElmore(const Pin *, + float &, + bool &exists) const +{ + exists = false; +} + +void +ConcreteParasitic::setElmore(const Pin *, + float) +{ +} + +Parasitic * +ConcreteParasitic::findPoleResidue(const Pin *) const +{ + return nullptr; +} + +void +ConcreteParasitic::setPoleResidue(const Pin *, + ComplexFloatSeq *, + ComplexFloatSeq *) +{ +} + +ParasiticDeviceIterator * +ConcreteParasitic::deviceIterator() +{ + return nullptr; +} + +ParasiticNodeIterator * +ConcreteParasitic::nodeIterator() +{ + return nullptr; +} + +//////////////////////////////////////////////////////////////// + +ConcreteElmore::ConcreteElmore() : + loads_(nullptr) +{ +} + +ConcreteElmore::~ConcreteElmore() +{ + delete loads_; +} + +void +ConcreteElmore::findElmore(const Pin *load_pin, + float &elmore, + bool &exists) const +{ + if (loads_) + loads_->findKey(load_pin, elmore, exists); + else + exists = false; +} + +void +ConcreteElmore::deleteLoad(const Pin *load_pin) +{ + loads_->erase(load_pin); +} + +void +ConcreteElmore::setElmore(const Pin *load_pin, + float elmore) +{ + if (loads_ == nullptr) + loads_ = new ConcreteElmoreLoadMap; + (*loads_)[load_pin] = elmore; +} + +//////////////////////////////////////////////////////////////// + +ConcretePi::ConcretePi(float c2, + float rpi, + float c1) : + c2_(c2), + rpi_(rpi), + c1_(c1), + is_reduced_(false) +{ +} + +float +ConcretePi::capacitance() const +{ + return c1_ + c2_; +} + +void +ConcretePi::setPiModel(float c2, + float rpi, + float c1) +{ + c2_ = c2; + rpi_ = rpi; + c1_ = c1; +} + +void +ConcretePi::piModel(float &c2, + float &rpi, + float &c1) const +{ + c2 = c2_; + rpi = rpi_; + c1 = c1_; +} + +void +ConcretePi::setIsReduced(bool reduced) +{ + is_reduced_ = reduced; +} + +//////////////////////////////////////////////////////////////// + +ConcretePiElmore::ConcretePiElmore(float c2, + float rpi, + float c1) : + ConcretePi(c2, rpi, c1), + ConcreteElmore() +{ +} + +float +ConcretePiElmore::capacitance() const +{ + return ConcretePi::capacitance(); +} + +void +ConcretePiElmore::piModel(float &c2, + float &rpi, + float &c1) const +{ + ConcretePi::piModel(c2, rpi, c1); +} + +void +ConcretePiElmore::setPiModel(float c2, + float rpi, + float c1) +{ + ConcretePi::setPiModel(c2, rpi, c1); +} + +bool +ConcretePiElmore::isReducedParasiticNetwork() const +{ + return ConcretePi::isReducedParasiticNetwork(); +} + +void +ConcretePiElmore::setIsReduced(bool reduced) +{ + ConcretePi::setIsReduced(reduced); +} + +void +ConcretePiElmore::findElmore(const Pin *load_pin, + float &elmore, + bool &exists) const +{ + ConcreteElmore::findElmore(load_pin, elmore, exists); +} + +void +ConcretePiElmore::setElmore(const Pin *load_pin, + float elmore) +{ + ConcreteElmore::setElmore(load_pin, elmore); +} + +//////////////////////////////////////////////////////////////// + +ConcretePiElmoreEstimated::ConcretePiElmoreEstimated(float c2, + float rpi, + float c1, + float elmore_res, + float elmore_cap, + bool elmore_use_load_cap, + const TransRiseFall *tr, + const OperatingConditions *op, + const Corner *corner, + const MinMax *min_max, + Sdc *sdc): + ConcretePi(c2, rpi, c1), + elmore_res_(elmore_res), + elmore_cap_(elmore_cap), + elmore_use_load_cap_(elmore_use_load_cap), + tr_(tr), + op_cond_(op), + corner_(corner), + min_max_(min_max), + sdc_(sdc) +{ +} + +float +ConcretePiElmoreEstimated::capacitance() const +{ + return ConcretePi::capacitance(); +} + +void +ConcretePiElmoreEstimated::piModel(float &c2, + float &rpi, + float &c1) const +{ + ConcretePi::piModel(c2, rpi, c1); +} + +void +ConcretePiElmoreEstimated::findElmore(const Pin *load_pin, + float &elmore, + bool &exists) const +{ + float load_cap = 0.0; + if (elmore_use_load_cap_) + load_cap = sdc_->pinCapacitance(load_pin, tr_, op_cond_, + corner_, min_max_); + elmore = elmore_res_ * (elmore_cap_ + load_cap); + exists = true; +} + +void +ConcretePiElmoreEstimated::setElmore(const Pin *, + float) +{ + // Cannot set elmore on estimated parasitic. +} + +//////////////////////////////////////////////////////////////// + +ConcretePoleResidue:: +ConcretePoleResidue(ComplexFloatSeq *poles, + ComplexFloatSeq *residues) : + poles_(poles), + residues_(residues) +{ +} + +ConcretePoleResidue::~ConcretePoleResidue() +{ + delete poles_; + delete residues_; +} + +size_t +ConcretePoleResidue::poleResidueCount() const +{ + return poles_->size(); +} + +void +ConcretePoleResidue::poleResidue(int index, + ComplexFloat &pole, + ComplexFloat &residue) const +{ + pole = (*poles_)[index]; + residue = (*residues_)[index]; +} + +void +ConcretePoleResidue::setPoleResidue(ComplexFloatSeq *poles, + ComplexFloatSeq *residues) +{ + poles_ = poles; + residues_ = residues; +} + +//////////////////////////////////////////////////////////////// + +ConcretePiPoleResidue::ConcretePiPoleResidue(float c2, + float rpi, + float c1) : + ConcretePi(c2, rpi, c1), + load_pole_residue_(nullptr) +{ +} + +ConcretePiPoleResidue::~ConcretePiPoleResidue() +{ + if (load_pole_residue_) { + load_pole_residue_->deleteContents(); + delete load_pole_residue_; + } +} + +float +ConcretePiPoleResidue::capacitance() const +{ + return ConcretePi::capacitance(); +} + +void +ConcretePiPoleResidue::piModel(float &c2, + float &rpi, + float &c1) const +{ + ConcretePi::piModel(c2, rpi, c1); +} + +void +ConcretePiPoleResidue::setPiModel(float c2, + float rpi, + float c1) +{ + ConcretePi::setPiModel(c2, rpi, c1); +} + +bool +ConcretePiPoleResidue::isReducedParasiticNetwork() const +{ + return ConcretePi::isReducedParasiticNetwork(); +} + +void +ConcretePiPoleResidue::setIsReduced(bool reduced) +{ + ConcretePi::setIsReduced(reduced); +} + +Parasitic * +ConcretePiPoleResidue::findPoleResidue(const Pin *load_pin) const +{ + if (load_pole_residue_) + return load_pole_residue_->findKey(load_pin); + else + return nullptr; +} + +void +ConcretePiPoleResidue::setPoleResidue(const Pin *load_pin, + ComplexFloatSeq *poles, + ComplexFloatSeq *residues) +{ + if (load_pole_residue_ == nullptr) + load_pole_residue_ = new ConcretePoleResidueMap; + ConcretePoleResidue *pole_residue = load_pole_residue_->findKey(load_pin); + if (pole_residue == nullptr) { + pole_residue = new ConcretePoleResidue(poles, residues); + (*load_pole_residue_)[load_pin] = pole_residue; + } + else + pole_residue->setPoleResidue(poles, residues); +} + +void +ConcretePiPoleResidue::deleteLoad(const Pin *load_pin) +{ + ConcretePoleResidue *pole_residue = load_pole_residue_->findKey(load_pin); + if (pole_residue) { + load_pole_residue_->erase(load_pin); + delete pole_residue; + } +} + +//////////////////////////////////////////////////////////////// + +ConcreteParasiticNode::ConcreteParasiticNode() : + cap_(0.0) +{ +} + +void +ConcreteParasiticNode::incrCapacitance(float cap) +{ + cap_ += cap; +} + +float +ConcreteParasiticNode::capacitance() const +{ + return cap_; +} + +void +ConcreteParasiticNode::addDevice(ConcreteParasiticDevice *device) +{ + devices_.push_back(device); +} + +//////////////////////////////////////////////////////////////// + +ConcreteParasiticSubNode::ConcreteParasiticSubNode(const Net *net, + int id) : + ConcreteParasiticNode(), + net_(net), + id_(id) +{ +} + +const char * +ConcreteParasiticSubNode::name(const Network *network) const +{ + const char *net_name = network->pathName(net_); + return stringPrintTmp("%s:%d", net_name, id_); +} + +//////////////////////////////////////////////////////////////// + +ConcreteParasiticPinNode::ConcreteParasiticPinNode(const Pin *pin) : + ConcreteParasiticNode(), + pin_(pin) +{ +} + +const char * +ConcreteParasiticPinNode::name(const Network *network) const +{ + return network->pathName(pin_); +} + +//////////////////////////////////////////////////////////////// + +ConcreteParasiticDevice::ConcreteParasiticDevice(const char *name, + ConcreteParasiticNode *node, + float value) : + name_(name), + node_(node), + value_(value) +{ +} + +ConcreteParasiticDevice::~ConcreteParasiticDevice() +{ + if (name_) + stringDelete(name_); +} + +ConcreteParasiticResistor:: +ConcreteParasiticResistor(const char *name, + ConcreteParasiticNode *node, + ConcreteParasiticNode *other_node, + float res) : + ConcreteParasiticDevice(name, node, res), + other_node_(other_node) +{ +} + +ParasiticNode * +ConcreteParasiticResistor::otherNode(ParasiticNode *node) const +{ + if (node == node_) + return other_node_; + else if (node == other_node_) + return node_; + else + return nullptr; +} + +void +ConcreteParasiticResistor::replaceNode(ConcreteParasiticNode *from_node, + ConcreteParasiticNode *to_node) +{ + if (from_node == node_) + node_ = to_node; + else + other_node_ = to_node; +} + +//////////////////////////////////////////////////////////////// + +ConcreteCouplingCap::ConcreteCouplingCap(const char *name, + ConcreteParasiticNode *node, + float cap) : + ConcreteParasiticDevice(name, node, cap) +{ +} + +void +ConcreteCouplingCap::replaceNode(ConcreteParasiticNode *from_node, + ConcreteParasiticNode *to_node) +{ + if (from_node == node_) + node_ = to_node; +} + +//////////////////////////////////////////////////////////////// + +ConcreteCouplingCapInt:: +ConcreteCouplingCapInt(const char *name, + ConcreteParasiticNode *node, + ConcreteParasiticNode *other_node, + float cap) : + ConcreteCouplingCap(name, node, cap), + other_node_(other_node) +{ +} + +ParasiticNode * +ConcreteCouplingCapInt::otherNode(ParasiticNode *node) const +{ + if (node == node_) + return other_node_; + else if (node == other_node_) + return node_; + else + return nullptr; +} + +void +ConcreteCouplingCapInt::replaceNode(ConcreteParasiticNode *from_node, + ConcreteParasiticNode *to_node) +{ + if (from_node == node_) + node_ = to_node; + else + other_node_ = to_node; +} + +//////////////////////////////////////////////////////////////// + +ConcreteCouplingCapExtNode:: +ConcreteCouplingCapExtNode(const char *name, + ConcreteParasiticNode *node, + Net *, + int, + float cap) : + ConcreteCouplingCap(name, node, cap) +{ +} + +ParasiticNode * +ConcreteCouplingCapExtNode::otherNode(ParasiticNode *) const +{ + return nullptr; +} + +void +ConcreteCouplingCapExtNode::replaceNode(ConcreteParasiticNode *from_node, + ConcreteParasiticNode *to_node) +{ + if (from_node == node_) + node_ = to_node; +} + +//////////////////////////////////////////////////////////////// + +ConcreteCouplingCapExtPin:: +ConcreteCouplingCapExtPin(const char *name, + ConcreteParasiticNode *node, + Pin *, + float cap) : + ConcreteCouplingCap(name, node, cap) +{ +} + +ParasiticNode * +ConcreteCouplingCapExtPin::otherNode(ParasiticNode *) const +{ + return nullptr; +} + +void +ConcreteCouplingCapExtPin::replaceNode(ConcreteParasiticNode *from_node, + ConcreteParasiticNode *to_node) +{ + if (from_node == node_) + node_ = to_node; +} + +//////////////////////////////////////////////////////////////// + +ConcreteParasiticNetwork::ConcreteParasiticNetwork(bool includes_pin_caps) : + max_node_id_(0), + includes_pin_caps_(includes_pin_caps) +{ +} + +ConcreteParasiticNetwork::~ConcreteParasiticNetwork() +{ + deleteDevices(); + deleteNodes(); +} + +void +ConcreteParasiticNetwork::deleteNodes() +{ + ConcreteParasiticSubNodeMap::Iterator node_iter1(sub_nodes_); + while (node_iter1.hasNext()) { + NetId *net_id; + ConcreteParasiticSubNode *node; + node_iter1.next(net_id, node); + delete net_id; + delete node; + } + pin_nodes_.deleteContents(); +} + +void +ConcreteParasiticNetwork::deleteDevices() +{ + ConcreteParasiticDeviceSet devices1; + devices(&devices1); + devices1.deleteContents(); +} + +ParasiticNodeIterator * +ConcreteParasiticNetwork::nodeIterator() +{ + ConcreteParasiticNodeSeq *nodes = new ConcreteParasiticNodeSeq(); + ConcreteParasiticPinNodeMap::Iterator node_iter2(pin_nodes_); + while (node_iter2.hasNext()) { + ConcreteParasiticPinNode *node = node_iter2.next(); + nodes->push_back(node); + } + ConcreteParasiticSubNodeMap::Iterator node_iter1(sub_nodes_); + while (node_iter1.hasNext()) { + ConcreteParasiticSubNode *node = node_iter1.next(); + nodes->push_back(node); + } + return new ConcreteParasiticNodeSeqIterator(nodes); +} + +ParasiticDeviceIterator * +ConcreteParasiticNetwork::deviceIterator() +{ + ConcreteParasiticDeviceSet *devices1 = new ConcreteParasiticDeviceSet(); + devices(devices1); + return new ConcreteParasiticDeviceSetIterator(devices1); +} + +void +ConcreteParasiticNetwork::devices(ConcreteParasiticDeviceSet *devices) +{ + // Collect devices into a set so they are only deleted once + // because multiple sub-nodes or pin nodes can refer to them. + ConcreteParasiticSubNodeMap::Iterator node_iter1(sub_nodes_); + while (node_iter1.hasNext()) { + ConcreteParasiticSubNode *node = node_iter1.next(); + ConcreteParasiticDeviceSeq::Iterator device_iter(node->devices()); + while (device_iter.hasNext()) { + ConcreteParasiticDevice *device = device_iter.next(); + devices->insert(device); + } + } + + ConcreteParasiticPinNodeMap::Iterator node_iter2(pin_nodes_); + while (node_iter2.hasNext()) { + ConcreteParasiticPinNode *node = node_iter2.next(); + ConcreteParasiticDeviceSeq::Iterator device_iter(node->devices()); + while (device_iter.hasNext()) { + ConcreteParasiticDevice *device = device_iter.next(); + devices->insert(device); + } + } +} + +float +ConcreteParasiticNetwork::capacitance() const +{ + float cap = 0.0; + ConcreteParasiticSubNodeMap::ConstIterator sub_node_iter(sub_nodes_); + while (sub_node_iter.hasNext()) { + ConcreteParasiticSubNode *node = sub_node_iter.next(); + cap += node->capacitance(); + } + + ConcreteParasiticPinNodeMap::ConstIterator pin_node_iter(pin_nodes_); + while (pin_node_iter.hasNext()) { + ConcreteParasiticPinNode *node = pin_node_iter.next(); + cap += node->capacitance(); + } + return cap; +} + +ConcreteParasiticNode * +ConcreteParasiticNetwork::ensureParasiticNode(const Net *net, + int id) +{ + NetId net_id(net, id); + ConcreteParasiticSubNode *node = sub_nodes_.findKey(&net_id); + if (node == nullptr) { + node = new ConcreteParasiticSubNode(net, id); + sub_nodes_[new NetId(net, id)] = node; + max_node_id_ = max((int) max_node_id_, id); + } + return node; +} + +ConcreteParasiticNode * +ConcreteParasiticNetwork::findNode(const Pin *pin) +{ + return pin_nodes_.findKey(pin); +} + +void +ConcreteParasiticNetwork::disconnectPin(const Pin *pin, + Net *net) +{ + ConcreteParasiticNode *node = pin_nodes_.findKey(pin); + if (node) { + // Make a subnode to replace the pin node. + ConcreteParasiticNode *subnode = ensureParasiticNode(net,max_node_id_+1); + // Hand over the devices. + ConcreteParasiticDeviceSeq::Iterator device_iter(node->devices()); + while (device_iter.hasNext()) { + ConcreteParasiticDevice *device = device_iter.next(); + subnode->addDevice(device); + device->replaceNode(node, subnode); + } + + pin_nodes_.erase(pin); + delete node; + } +} + +ConcreteParasiticNode * +ConcreteParasiticNetwork::ensureParasiticNode(const Pin *pin) +{ + ConcreteParasiticPinNode *node = pin_nodes_.findKey(pin); + if (node == nullptr) { + node = new ConcreteParasiticPinNode(pin); + pin_nodes_[pin] = node; + } + return node; +} + +bool +NetIdLess::operator()(const NetId *net_id1, + const NetId *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; + return net1 < net2 + || (net1 == net2 + && id1 < id2); +} + +//////////////////////////////////////////////////////////////// + +Parasitics * +makeConcreteParasitics(StaState *sta) +{ + return new ConcreteParasitics(sta); +} + +ConcreteParasitics::ConcreteParasitics(StaState *sta) : + Parasitics(sta) +{ +} + +ConcreteParasitics::~ConcreteParasitics() +{ + clear(); +} + +bool +ConcreteParasitics::haveParasitics() +{ + return !drvr_parasitic_map_.empty() + || !parasitic_network_map_.empty(); +} + +void +ConcreteParasitics::clear() +{ + deleteParasitics(); +} + +int +ConcreteParasitics::parasiticAnalysisPtIndex(const ParasiticAnalysisPt *ap, + const TransRiseFall *tr) const +{ + return ap->index() * TransRiseFall::index_count + tr->index(); +} + +void +ConcreteParasitics::deleteParasitics() +{ + int ap_count = corners_->parasiticAnalysisPtCount(); + int ap_tr_count = ap_count * TransRiseFall::index_count; + for (auto drvr_parasitics : drvr_parasitic_map_) { + ConcreteParasitic **parasitics = drvr_parasitics.second; + if (parasitics) { + for (int i = 0; i < ap_tr_count; i++) + delete parasitics[i]; + delete [] parasitics; + } + } + drvr_parasitic_map_.clear(); + + for (auto net_parasitics : parasitic_network_map_) { + ConcreteParasiticNetwork **parasitics = net_parasitics.second; + 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 tr : TransRiseFall::range()) { + int ap_tr_index = parasiticAnalysisPtIndex(ap, tr); + delete parasitics[ap_tr_index]; + parasitics[ap_tr_index] = nullptr; + } + } +} + +void +ConcreteParasitics::deleteParasitics(const Net *net, + const ParasiticAnalysisPt *ap) +{ + PinSet *drivers = network_->drivers(net); + for (auto drvr_pin : *drivers) + deleteParasitics(drvr_pin, ap); + + ConcreteParasiticNetwork **parasitics = parasitic_network_map_[net]; + if (parasitics) { + delete parasitics[ap->index()]; + parasitics[ap->index()] = nullptr; + } +} + +void +ConcreteParasitics::deleteUnsavedParasitic(Parasitic *parasitic) +{ + ConcreteParasitic *cparasitic = static_cast(parasitic); + delete cparasitic; +} + +void +ConcreteParasitics::save() +{ + // No database to save to. +} + +float +ConcreteParasitics::capacitance(Parasitic *parasitic) const +{ + ConcreteParasitic *cparasitic = static_cast(parasitic); + return cparasitic->capacitance(); +} + +bool +ConcreteParasitics::isReducedParasiticNetwork(Parasitic *parasitic) const +{ + ConcreteParasitic *cparasitic = static_cast(parasitic); + return cparasitic->isReducedParasiticNetwork(); +} + +void +ConcreteParasitics::setIsReducedParasiticNetwork(Parasitic *parasitic, + bool is_reduced) +{ + ConcreteParasitic *cparasitic = static_cast(parasitic); + cparasitic->setIsReduced(is_reduced); +} + +void +ConcreteParasitics::disconnectPinBefore(const Pin *pin) +{ + if (haveParasitics()) { + deleteReducedParasitics(pin); + + 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); + } + } + } + } +} + +void +ConcreteParasitics::loadPinCapacitanceChanged(const Pin *pin) +{ + // Delete reduced models that depend on load pin capacitances. + deleteReducedParasitics(pin); +} + +// Delete reduced models on pin's net. +void +ConcreteParasitics::deleteReducedParasitics(const Pin *pin) +{ + if (!drvr_parasitic_map_.empty()) { + PinSet *drivers = network_->drivers(pin); + if (drivers) { + for (auto drvr_pin : *drivers) + deleteDrvrReducedParasitics(drvr_pin); + } + } +} + +void +ConcreteParasitics::deleteDrvrReducedParasitics(const Pin *drvr_pin) +{ + ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin]; + if (parasitics) { + int ap_count = corners_->parasiticAnalysisPtCount(); + int ap_tr_count = ap_count * TransRiseFall::index_count; + for (int i = 0; i < ap_tr_count; i++) + delete parasitics[i]; + delete [] parasitics; + } + drvr_parasitic_map_[drvr_pin] = nullptr; +} + +//////////////////////////////////////////////////////////////// + +bool +ConcreteParasitics::isPiElmore(Parasitic *parasitic) const +{ + ConcreteParasitic *cparasitic = static_cast(parasitic); + return cparasitic && cparasitic->isPiElmore(); +} + +Parasitic * +ConcreteParasitics::findPiElmore(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap) const +{ + if (!drvr_parasitic_map_.empty()) { + int ap_tr_index = parasiticAnalysisPtIndex(ap, tr); + UniqueLock lock(lock_); + ConcreteParasitic **parasitics = drvr_parasitic_map_.findKey(drvr_pin); + if (parasitics) { + ConcreteParasitic *parasitic = parasitics[ap_tr_index]; + if (parasitic == nullptr && tr == TransRiseFall::fall()) { + ap_tr_index = parasiticAnalysisPtIndex(ap, TransRiseFall::rise()); + parasitic = parasitics[ap_tr_index]; + } + if (parasitic && parasitic->isPiElmore()) + return parasitic; + } + } + return nullptr; +} + +Parasitic * +ConcreteParasitics::makePiElmore(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap, + float c2, + float rpi, + float c1) +{ + UniqueLock lock(lock_); + ConcreteParasitic **parasitics = drvr_parasitic_map_.findKey(drvr_pin); + if (parasitics == nullptr) { + int ap_count = corners_->parasiticAnalysisPtCount(); + int ap_tr_count = ap_count * TransRiseFall::index_count; + parasitics = new ConcreteParasitic*[ap_tr_count]; + for (int i = 0; i < ap_tr_count; i++) + parasitics[i] = nullptr; + drvr_parasitic_map_[drvr_pin] = parasitics; + } + int ap_tr_index = parasiticAnalysisPtIndex(ap, tr); + ConcreteParasitic *parasitic = parasitics[ap_tr_index]; + ConcretePiElmore *pi_elmore = nullptr; + if (parasitic) { + if (parasitic->isPiElmore()) { + pi_elmore = dynamic_cast(parasitic); + pi_elmore->setPiModel(c2, rpi, c1); + } + else { + delete parasitic; + pi_elmore = new ConcretePiElmore(c2, rpi, c1); + parasitics[ap_tr_index] = pi_elmore; + } + } + else { + pi_elmore = new ConcretePiElmore(c2, rpi, c1); + parasitics[ap_tr_index] = pi_elmore; + } + return pi_elmore; +} + +//////////////////////////////////////////////////////////////// + +bool +ConcreteParasitics::isPiModel(Parasitic *parasitic) const +{ + ConcreteParasitic *cparasitic = static_cast(parasitic); + return cparasitic && cparasitic->isPiModel(); +} + +void +ConcreteParasitics::piModel(Parasitic *parasitic, + float &c2, + float &rpi, + float &c1) const +{ + ConcreteParasitic *cparasitic = static_cast(parasitic); + cparasitic->piModel(c2, rpi, c1); +} + +void +ConcreteParasitics::setPiModel(Parasitic *parasitic, + float c2, + float rpi, + float c1) +{ + ConcreteParasitic *cparasitic = static_cast(parasitic); + cparasitic->setPiModel(c2, rpi, c1); +} + +//////////////////////////////////////////////////////////////// + +void +ConcreteParasitics::findElmore(Parasitic *parasitic, + const Pin *load_pin, + float &elmore, + bool &exists) const +{ + ConcreteParasitic *cparasitic = static_cast(parasitic); + cparasitic->findElmore(load_pin, elmore, exists); +} + +void +ConcreteParasitics::setElmore(Parasitic *parasitic, + const Pin *load_pin, + float elmore) +{ + ConcreteParasitic *cparasitic = static_cast(parasitic); + cparasitic->setElmore(load_pin, elmore); +} + +//////////////////////////////////////////////////////////////// + +bool +ConcreteParasitics::isPiPoleResidue(Parasitic* parasitic) const +{ + ConcreteParasitic *cparasitic = static_cast(parasitic); + return cparasitic && cparasitic->isPiPoleResidue(); +} + +Parasitic * +ConcreteParasitics::findPiPoleResidue(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap) const +{ + if (!drvr_parasitic_map_.empty()) { + int ap_tr_index = parasiticAnalysisPtIndex(ap, tr); + UniqueLock lock(lock_); + ConcreteParasitic **parasitics = drvr_parasitic_map_.findKey(drvr_pin); + if (parasitics) { + ConcreteParasitic *parasitic = parasitics[ap_tr_index]; + if (parasitic == nullptr && tr == TransRiseFall::fall()) { + ap_tr_index = parasiticAnalysisPtIndex(ap, TransRiseFall::rise()); + parasitic = parasitics[ap_tr_index]; + } + if (parasitic->isPiPoleResidue()) + return parasitic; + } + } + return nullptr; +} + +Parasitic * +ConcreteParasitics::makePiPoleResidue(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap, + float c2, + float rpi, + float c1) +{ + UniqueLock lock(lock_); + ConcreteParasitic **parasitics = drvr_parasitic_map_.findKey(drvr_pin); + if (parasitics == nullptr) { + int ap_count = corners_->parasiticAnalysisPtCount(); + int ap_tr_count = ap_count * TransRiseFall::index_count; + parasitics = new ConcreteParasitic*[ap_tr_count]; + for (int i = 0; i < ap_tr_count; i++) + parasitics[i] = nullptr; + drvr_parasitic_map_[drvr_pin] = parasitics; + } + int ap_tr_index = parasiticAnalysisPtIndex(ap, tr); + ConcreteParasitic *parasitic = parasitics[ap_tr_index]; + ConcretePiPoleResidue *pi_pole_residue = nullptr; + if (parasitic) { + if (parasitic->isPiElmore()) { + pi_pole_residue = dynamic_cast(parasitic); + pi_pole_residue->setPiModel(c2, rpi, c1); + } + else { + delete parasitic; + pi_pole_residue = new ConcretePiPoleResidue(c2, rpi, c1); + parasitics[ap_tr_index] = pi_pole_residue; + } + } + else { + pi_pole_residue = new ConcretePiPoleResidue(c2, rpi, c1); + parasitics[ap_tr_index] = pi_pole_residue; + } + return pi_pole_residue; +} + +Parasitic * +ConcreteParasitics::findPoleResidue(const Parasitic *parasitic, + const Pin *load_pin) const +{ + const ConcreteParasitic *cparasitic = + static_cast(parasitic); + return cparasitic->findPoleResidue(load_pin); +} + +void +ConcreteParasitics::setPoleResidue(Parasitic *parasitic, + const Pin *load_pin, + ComplexFloatSeq *poles, + ComplexFloatSeq *residues) +{ + ConcreteParasitic *cparasitic = + static_cast(parasitic); + cparasitic->setPoleResidue(load_pin, poles, residues); +} + +//////////////////////////////////////////////////////////////// + +bool +ConcreteParasitics::isPoleResidue(const Parasitic *parasitic) const +{ + const ConcreteParasitic *cparasitic = + static_cast(parasitic); + return cparasitic->isPoleResidue(); +} + +size_t +ConcreteParasitics::poleResidueCount(const Parasitic *parasitic) const +{ + const ConcretePoleResidue *pr_parasitic = + static_cast(parasitic); + return pr_parasitic->poleResidueCount(); +} + +void +ConcreteParasitics::poleResidue(const Parasitic *parasitic, + int pole_index, + ComplexFloat &pole, + ComplexFloat &residue) const +{ + const ConcretePoleResidue *pr_parasitic = + static_cast(parasitic); + pr_parasitic->poleResidue(pole_index, pole, residue); +} + +//////////////////////////////////////////////////////////////// + +bool +ConcreteParasitics::isParasiticNetwork(Parasitic *parasitic) const +{ + ConcreteParasitic *cparasitic = static_cast(parasitic); + return cparasitic && cparasitic->isParasiticNetwork(); +} + +Parasitic * +ConcreteParasitics::findParasiticNetwork(const Net *net, + const ParasiticAnalysisPt *ap) const +{ + if (!parasitic_network_map_.empty()) { + UniqueLock lock(lock_); + if (!parasitic_network_map_.empty()) { + ConcreteParasiticNetwork **parasitics=parasitic_network_map_.findKey(net); + if (parasitics) + return parasitics[ap->index()]; + } + } + return nullptr; +} + +Parasitic * +ConcreteParasitics::findParasiticNetwork(const Pin *pin, + const ParasiticAnalysisPt *ap) const +{ + if (!parasitic_network_map_.empty()) { + UniqueLock lock(lock_); + if (!parasitic_network_map_.empty()) { + // Only call findParasiticNet if parasitics exist. + Net *net = findParasiticNet(pin); + ConcreteParasiticNetwork **parasitics=parasitic_network_map_.findKey(net); + if (parasitics) + return parasitics[ap->index()]; + } + } + return nullptr; +} + +Parasitic * +ConcreteParasitics::makeParasiticNetwork(const Net *net, + bool includes_pin_caps, + const ParasiticAnalysisPt *ap) +{ + UniqueLock 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; + } + int ap_index = ap->index(); + ConcreteParasiticNetwork *parasitic = parasitics[ap_index]; + if (parasitic) + delete parasitic; + parasitic = new ConcreteParasiticNetwork(includes_pin_caps); + parasitics[ap_index] = parasitic; + return parasitic; +} + +void +ConcreteParasitics::deleteParasiticNetwork(const Net *net, + const ParasiticAnalysisPt *ap) +{ + if (!parasitic_network_map_.empty()) { + UniqueLock lock(lock_); + ConcreteParasiticNetwork **parasitics = parasitic_network_map_.findKey(net); + if (parasitics) { + int ap_index = ap->index(); + delete parasitics[ap_index]; + parasitics[ap_index] = nullptr; + } + } +} + +bool +ConcreteParasitics::includesPinCaps(Parasitic *parasitic) const +{ + ConcreteParasiticNetwork *cparasitic = + static_cast(parasitic); + return cparasitic->includesPinCaps(); +} + +ParasiticNode * +ConcreteParasitics::ensureParasiticNode(Parasitic *parasitic, + const Net *net, + int id) +{ + ConcreteParasiticNetwork *cparasitic = + static_cast(parasitic); + return cparasitic->ensureParasiticNode(net, id); +} + +ParasiticNode * +ConcreteParasitics::ensureParasiticNode(Parasitic *parasitic, + const Pin *pin) +{ + ConcreteParasiticNetwork *cparasitic = + static_cast(parasitic); + return cparasitic->ensureParasiticNode(pin); +} + +void +ConcreteParasitics::incrCap(ParasiticNode *node, + float cap, + const ParasiticAnalysisPt *) +{ + ConcreteParasiticNode *cnode = static_cast(node); + cnode->incrCapacitance(cap); +} + +void +ConcreteParasitics::makeCouplingCap(const char *name, + ParasiticNode *node, + ParasiticNode *other_node, + float cap, + const ParasiticAnalysisPt *) +{ + ConcreteParasiticNode *cnode = static_cast(node); + ConcreteParasiticNode *other_cnode = + static_cast(other_node); + ConcreteCouplingCap *coupling_cap = + new ConcreteCouplingCapInt(name, cnode, other_cnode, cap); + cnode->addDevice(coupling_cap); + other_cnode->addDevice(coupling_cap); +} + +void +ConcreteParasitics::makeCouplingCap(const char *name, + ParasiticNode *node, + Net *other_node_net, + int other_node_id, + float cap, + const ParasiticAnalysisPt *) +{ + ConcreteParasiticNode *cnode = static_cast(node); + ConcreteCouplingCap *coupling_cap = + new ConcreteCouplingCapExtNode(name, cnode, other_node_net, + other_node_id, cap); + cnode->addDevice(coupling_cap); +} + +void +ConcreteParasitics::makeCouplingCap(const char *name, + ParasiticNode *node, + Pin *other_node_pin, + float cap, + const ParasiticAnalysisPt *) +{ + ConcreteParasiticNode *cnode = static_cast(node); + ConcreteCouplingCap *coupling_cap = + new ConcreteCouplingCapExtPin(name, cnode, other_node_pin, cap); + cnode->addDevice(coupling_cap); +} + +void +ConcreteParasitics::makeResistor(const char *name, + ParasiticNode *node1, + ParasiticNode *node2, + float res, + const ParasiticAnalysisPt *) +{ + ConcreteParasiticNode *cnode1 = static_cast(node1); + ConcreteParasiticNode *cnode2 = static_cast(node2); + ConcreteParasiticDevice *resistor = + new ConcreteParasiticResistor(name, cnode1, cnode2, res); + cnode1->addDevice(resistor); + cnode2->addDevice(resistor); +} + +ParasiticDeviceIterator * +ConcreteParasitics::deviceIterator(Parasitic *parasitic) +{ + ConcreteParasitic *cparasitic = static_cast(parasitic); + return cparasitic->deviceIterator(); +} + +ParasiticNodeIterator * +ConcreteParasitics::nodeIterator(Parasitic *parasitic) +{ + ConcreteParasitic *cparasitic = static_cast(parasitic); + return cparasitic->nodeIterator(); +} + +float +ConcreteParasitics::nodeGndCap(const ParasiticNode *node, + const ParasiticAnalysisPt *) const +{ + const ConcreteParasiticNode *cnode = + static_cast(node); + return cnode->capacitance(); +} + +const char * +ConcreteParasitics::name(const ParasiticNode *node) +{ + const ConcreteParasiticNode *cnode = + static_cast(node); + return cnode->name(network_); +} + +const Pin * +ConcreteParasitics::connectionPin(const ParasiticNode *node) const +{ + const ConcreteParasiticNode *cnode = + static_cast(node); + if (cnode->isPinNode()) { + const ConcreteParasiticPinNode *pin_node = + dynamic_cast(cnode); + return pin_node->pin(); + } + else + return nullptr; +} + +ParasiticNode * +ConcreteParasitics::findNode(Parasitic *parasitic, + const Pin *pin) const +{ + ConcreteParasiticNetwork *cparasitic = + static_cast(parasitic); + return cparasitic->findNode(pin); +} + +ParasiticDeviceIterator * +ConcreteParasitics::deviceIterator(ParasiticNode *node) const +{ + ConcreteParasiticNode *cnode = static_cast(node); + return new ConcreteParasiticDeviceSeqIterator(cnode->devices()); +} + +const char * +ConcreteParasitics::name(const ParasiticDevice *device) const +{ + const ConcreteParasiticDevice *cdevice = + static_cast(device); + return cdevice->name(); +} + +bool +ConcreteParasitics::isResistor(const ParasiticDevice *device) const +{ + const ConcreteParasiticDevice *cdevice = + static_cast(device); + return cdevice->isResistor(); +} + +bool +ConcreteParasitics::isCouplingCap(const ParasiticDevice *device) const +{ + const ConcreteParasiticDevice *cdevice = + static_cast(device); + return cdevice->isCouplingCap(); +} + +float +ConcreteParasitics::value(const ParasiticDevice *device, + const ParasiticAnalysisPt *) const +{ + const ConcreteParasiticDevice *cdevice = + static_cast(device); + return cdevice->value(); +} + +ParasiticNode * +ConcreteParasitics::node1(const ParasiticDevice *device) const +{ + const ConcreteParasiticDevice *cdevice = + static_cast(device); + return cdevice->node1(); +} + +ParasiticNode * +ConcreteParasitics::node2(const ParasiticDevice *device) const +{ + const ConcreteParasiticDevice *cdevice = + static_cast(device); + return cdevice->node2(); +} + +ParasiticNode * +ConcreteParasitics::otherNode(const ParasiticDevice *device, + ParasiticNode *node) const +{ + const ConcreteParasiticDevice *cdevice = + static_cast(device); + return cdevice->otherNode(node); +} + +//////////////////////////////////////////////////////////////// + +void +ConcreteParasitics::reduceTo(Parasitic *parasitic, + const Net *net, + ReduceParasiticsTo reduce_to, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap) +{ + switch (reduce_to) { + case ReduceParasiticsTo::pi_elmore: + reduceToPiElmore(parasitic, net, op_cond, corner, cnst_min_max, ap); + break; + case ReduceParasiticsTo::pi_pole_residue2: + reduceToPiPoleResidue2(parasitic, net, op_cond, corner, + cnst_min_max, ap); + break; + case ReduceParasiticsTo::none: + break; + } +} + +void +ConcreteParasitics::reduceToPiElmore(Parasitic *parasitic, + const Net *net, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap) +{ + debugPrint1(debug_, "parasitic_reduce", 1, "Reduce net %s\n", + network_->pathName(net)); + NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + if (network_->isDriver(pin)) { + sta::reduceToPiElmore(parasitic, pin, ap->couplingCapFactor(), + op_cond, corner, cnst_min_max, ap, this); + } + } + delete pin_iter; +} + +void +ConcreteParasitics::reduceToPiElmore(Parasitic *parasitic, + const Pin *drvr_pin, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap) +{ + sta::reduceToPiElmore(parasitic, drvr_pin, ap->couplingCapFactor(), + op_cond, corner, cnst_min_max, ap, this); +} + +void +ConcreteParasitics::reduceToPiPoleResidue2(Parasitic *parasitic, + const Net *net, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap) +{ + debugPrint1(debug_, "parasitic_reduce", 1, "Reduce net %s\n", + network_->pathName(net)); + NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + if (network_->isDriver(pin)) + sta::reduceToPiPoleResidue2(parasitic, pin, ap->couplingCapFactor(), + op_cond, corner, cnst_min_max, ap, this); + } + delete pin_iter; +} + +void +ConcreteParasitics::reduceToPiPoleResidue2(Parasitic *parasitic, + const Pin *drvr_pin, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap) +{ + sta::reduceToPiPoleResidue2(parasitic, drvr_pin, + ap->couplingCapFactor(), + op_cond, corner, cnst_min_max, + ap, this); +} + +//////////////////////////////////////////////////////////////// + +Parasitic * +ConcreteParasitics::estimatePiElmore(const Pin *drvr_pin, + const TransRiseFall *tr, + const Wireload *wireload, + float fanout, + float net_pin_cap, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + const ParasiticAnalysisPt *) +{ + float c2, rpi, c1, elmore_res, elmore_cap; + bool elmore_use_load_cap; + estimatePiElmore(drvr_pin, tr, wireload, fanout, net_pin_cap, + op_cond, corner, min_max, this, + c2, rpi, c1, + elmore_res, elmore_cap, elmore_use_load_cap); + + return new ConcretePiElmoreEstimated(c2, rpi, c1, elmore_res, elmore_cap, + elmore_use_load_cap, + tr, op_cond, corner, min_max, + sdc_); +} + +//////////////////////////////////////////////////////////////// + +ConcreteParasiticDeviceSeqIterator:: +ConcreteParasiticDeviceSeqIterator(ConcreteParasiticDeviceSeq *devices) : + iter_(devices) +{ +} + +ConcreteParasiticDeviceSetIterator:: +ConcreteParasiticDeviceSetIterator(ConcreteParasiticDeviceSet *devices) : + iter_(devices) +{ +} + +ConcreteParasiticDeviceSetIterator::~ConcreteParasiticDeviceSetIterator() +{ + delete iter_.container(); +} + +ConcreteParasiticNodeSeqIterator:: +ConcreteParasiticNodeSeqIterator(ConcreteParasiticNodeSeq *nodes) : + iter_(nodes) +{ +} + +ConcreteParasiticNodeSeqIterator::~ConcreteParasiticNodeSeqIterator() +{ + delete iter_.container(); +} + +} // namespace diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh new file mode 100644 index 0000000..f525acc --- /dev/null +++ b/parasitics/ConcreteParasitics.hh @@ -0,0 +1,218 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CONCRETE_PARASITICS_H +#define STA_CONCRETE_PARASITICS_H + +#include +#include "Map.hh" +#include "Set.hh" +#include "MinMax.hh" +#include "EstimateParasitics.hh" +#include "Parasitics.hh" + +namespace sta { + +class ConcreteParasitic; +class ConcretePiElmore; +class ConcretePiPoleResidue; +class ConcreteParasiticNetwork; +class ConcreteParasiticNode; +class ConcreteParasiticDevice; + +typedef Map ConcreteParasiticMap; +typedef Map ConcreteParasiticNetworkMap; + +// This class acts as a BUILDER for all parasitics. +class ConcreteParasitics : public Parasitics, public EstimateParasitics +{ +public: + ConcreteParasitics(StaState *sta); + virtual ~ConcreteParasitics(); + virtual bool haveParasitics(); + virtual void clear(); + + virtual void save(); + virtual void deleteParasitics(); + virtual void deleteParasitics(const Net *net, + const ParasiticAnalysisPt *ap); + virtual void deleteParasitics(const Pin *drvr_pin, + const ParasiticAnalysisPt *ap); + virtual void deleteUnsavedParasitic(Parasitic *parasitic); + + virtual bool isReducedParasiticNetwork(Parasitic *parasitic) const; + virtual void setIsReducedParasiticNetwork(Parasitic *parasitic, + bool is_reduced); + + virtual float capacitance(Parasitic *parasitic) const; + + virtual bool isPiElmore(Parasitic *parasitic) const; + virtual Parasitic *findPiElmore(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap) const; + virtual Parasitic *makePiElmore(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap, + float c2, float rpi, float c1); + + virtual bool isPiModel(Parasitic *parasitic) const; + virtual void piModel(Parasitic *parasitic, float &c2, float &rpi, + float &c1) const; + virtual void setPiModel(Parasitic *parasitic, float c2, float rpi, float c1); + + virtual void findElmore(Parasitic *parasitic, const Pin *load_pin, + float &elmore, bool &exists) const; + virtual void setElmore(Parasitic *parasitic, const Pin *load_pin, + float elmore); + + virtual bool isPiPoleResidue(Parasitic* parasitic) const; + virtual Parasitic *findPiPoleResidue(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap) const; + virtual Parasitic *findPoleResidue(const Parasitic *parasitic, + const Pin *load_pin) const; + virtual Parasitic *makePiPoleResidue(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap, + float c2, float rpi, float c1); + virtual void setPoleResidue(Parasitic *parasitic, const Pin *load_pin, + ComplexFloatSeq *poles, + ComplexFloatSeq *residues); + virtual bool isPoleResidue(const Parasitic* parasitic) const; + virtual size_t poleResidueCount(const Parasitic *parasitic) const; + virtual void poleResidue(const Parasitic *parasitic, int pole_index, + ComplexFloat &pole, ComplexFloat &residue) const; + + virtual bool isParasiticNetwork(Parasitic *parasitic) const; + virtual Parasitic *findParasiticNetwork(const Net *net, + const ParasiticAnalysisPt *ap) const; + virtual Parasitic *findParasiticNetwork(const Pin *pin, + const ParasiticAnalysisPt *ap) const; + virtual Parasitic *makeParasiticNetwork(const Net *net, + bool includes_pin_caps, + const ParasiticAnalysisPt *ap); + virtual void deleteParasiticNetwork(const Net *net, + const ParasiticAnalysisPt *ap); + virtual bool includesPinCaps(Parasitic *parasitic) const; + virtual ParasiticNode *ensureParasiticNode(Parasitic *parasitic, + const Net *net, + int id); + virtual ParasiticNode *ensureParasiticNode(Parasitic *parasitic, + const Pin *pin); + virtual void incrCap(ParasiticNode *node, float cap, + const ParasiticAnalysisPt *ap); + virtual void makeCouplingCap(const char *name, + ParasiticNode *node, + ParasiticNode *other_node, + float cap, const ParasiticAnalysisPt *ap); + virtual void makeCouplingCap(const char *name, + ParasiticNode *node, + Net *other_node_net, int other_node_id, + float cap, const ParasiticAnalysisPt *ap); + virtual void makeCouplingCap(const char *name, + ParasiticNode *node, + Pin *other_node_pin, + float cap, const ParasiticAnalysisPt *ap); + virtual void makeResistor(const char *name, ParasiticNode *node1, + ParasiticNode *node2, + float res, const ParasiticAnalysisPt *ap); + virtual ParasiticDeviceIterator *deviceIterator(Parasitic *parasitic); + virtual ParasiticNodeIterator *nodeIterator(Parasitic *parasitic); + + virtual const char *name(const ParasiticNode *node); + virtual const Pin *connectionPin(const ParasiticNode *node) const; + virtual ParasiticNode *findNode(Parasitic *parasitic, const Pin *pin) const; + virtual float nodeGndCap(const ParasiticNode *node, + const ParasiticAnalysisPt *ap) const; + virtual ParasiticDeviceIterator * + deviceIterator(ParasiticNode *node) const; + virtual bool isResistor(const ParasiticDevice *device) const; + virtual bool isCouplingCap(const ParasiticDevice *device) const; + virtual const char *name(const ParasiticDevice *device) const; + virtual float value(const ParasiticDevice *device, + const ParasiticAnalysisPt *ap) const; + virtual ParasiticNode *node1(const ParasiticDevice *device) const; + virtual ParasiticNode *node2(const ParasiticDevice *device) const; + virtual ParasiticNode *otherNode(const ParasiticDevice *device, + ParasiticNode *node) const; + + virtual Parasitic *estimatePiElmore(const Pin *drvr_pin, + const TransRiseFall *tr, + const Wireload *wireload, + float fanout, + float net_pin_cap, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + const ParasiticAnalysisPt *ap); + virtual void disconnectPinBefore(const Pin *pin); + virtual void loadPinCapacitanceChanged(const Pin *pin); + + virtual void reduceTo(Parasitic *parasitic, + const Net *net, + ReduceParasiticsTo reduce_to, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap); + virtual void reduceToPiElmore(Parasitic *parasitic, + const Net *net, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap); + virtual void reduceToPiElmore(Parasitic *parasitic, + const Pin *drvr_pin, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap); + virtual void reduceToPiPoleResidue2(Parasitic *parasitic, + const Net *net, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap); + virtual void reduceToPiPoleResidue2(Parasitic *parasitic, + const Pin *drvr_pin, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap); + virtual void deleteDrvrReducedParasitics(const Pin *drvr_pin); + +protected: + int parasiticAnalysisPtIndex(const ParasiticAnalysisPt *ap, + const TransRiseFall *tr) const; + Parasitic *ensureRspf(const Pin *drvr_pin); + void makeAnalysisPtAfter(); + void deleteReducedParasitics(const Pin *pin); + + // Driver pin to array of parasitics indexed by analysis pt index + // and transition. + ConcreteParasiticMap drvr_parasitic_map_; + ConcreteParasiticNetworkMap parasitic_network_map_; + mutable std::mutex lock_; + + using EstimateParasitics::estimatePiElmore; + friend class ConcretePiElmore; + friend class ConcreteParasiticNode; + friend class ConcreteParasiticResistor; + friend class ConcreteParasiticNetwork; +}; + +} // namespace +#endif diff --git a/parasitics/ConcreteParasiticsPvt.hh b/parasitics/ConcreteParasiticsPvt.hh new file mode 100644 index 0000000..dafa886 --- /dev/null +++ b/parasitics/ConcreteParasiticsPvt.hh @@ -0,0 +1,448 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CONCRETE_PARASITICS_PVT_H +#define STA_CONCRETE_PARASITICS_PVT_H + +#include "Parasitics.hh" + +namespace sta { + +class ConcretePoleResidue; +class ConcreteParasiticDevice; +class ConcreteParasiticPinNode; +class ConcreteParasiticSubNode; +class ConcreteParasiticNode; + +typedef Map ConcreteElmoreLoadMap; +typedef ConcreteElmoreLoadMap::Iterator ConcretePiElmoreLoadIterator; +typedef Map ConcretePoleResidueMap; +typedef std::pair NetId; +struct NetIdLess +{ + bool operator()(const NetId *net_id1, + const NetId *net_id2) const; +}; +typedef Map ConcreteParasiticSubNodeMap; +typedef Map ConcreteParasiticPinNodeMap; +typedef Vector ConcreteParasiticDeviceSeq; +typedef Set ConcreteParasiticDeviceSet; +typedef Vector ConcreteParasiticNodeSeq; + +// Empty base class definitions so casts are not required on returned +// objects. +class Parasitic {}; +class ParasiticNode {}; +class ParasiticDevice {}; +class ParasiticNetwork {}; + +// Base class for parasitics. +class ConcreteParasitic : public Parasitic +{ +public: + virtual ~ConcreteParasitic(); + virtual float capacitance() const = 0; + virtual bool isPiElmore() const; + virtual bool isPiModel() const; + virtual bool isPiPoleResidue() const; + virtual bool isPoleResidue() const; + virtual bool isParasiticNetwork() const; + virtual void piModel(float &c2, + float &rpi, + float &c1) const; + virtual void setPiModel(float c2, + 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; + virtual void setElmore(const Pin *load_pin, + float elmore); + virtual Parasitic *findPoleResidue(const Pin *load_pin) const; + virtual void setPoleResidue(const Pin *load_pin, + ComplexFloatSeq *poles, + ComplexFloatSeq *residues); + virtual ParasiticDeviceIterator *deviceIterator(); + virtual ParasiticNodeIterator *nodeIterator(); +}; + +class ConcreteElmore +{ +public: + void findElmore(const Pin *load_pin, + float &elmore, + bool &exists) const; + void deleteLoad(const Pin *load_pin); + void setElmore(const Pin *load_pin, + float elmore); + +protected: + ConcreteElmore(); + virtual ~ConcreteElmore(); + +private: + ConcreteElmoreLoadMap *loads_; +}; + +// Pi model for a driver pin. +class ConcretePi +{ +public: + ConcretePi(float c2, + float rpi, + float c1); + float capacitance() const; + void piModel(float &c2, + float &rpi, + float &c1) const; + void setPiModel(float c2, + float rpi, + float c1); + bool isReducedParasiticNetwork() const { return is_reduced_; } + void setIsReduced(bool reduced); + +protected: + float c2_; + float rpi_; + float c1_; + bool is_reduced_; +}; + +// Pi model for a driver pin and the elmore delay to each load. +class ConcretePiElmore : public ConcretePi, + public ConcreteElmore, + public ConcreteParasitic +{ +public: + ConcretePiElmore(float c2, + float rpi, + float c1); + virtual bool isPiElmore() const { return true; } + virtual bool isPiModel() const { return true; } + virtual float capacitance() const; + virtual void piModel(float &c2, float &rpi, float &c1) const; + virtual void setPiModel(float c2, 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; + virtual void setElmore(const Pin *load_pin, float elmore); +}; + +// PiElmore from wireload model estimate. +class ConcretePiElmoreEstimated : public ConcretePi, + public ConcreteParasitic +{ +public: + ConcretePiElmoreEstimated(float c2, + float rpi, + float c1, + float elmore_res, + float elmore_cap, + bool elmore_use_load_cap, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + Sdc *sdc); + virtual float capacitance() const; + virtual bool isPiElmore() const { return true; } + virtual bool isPiModel() const { return true; } + virtual void piModel(float &c2, float &rpi, float &c1) const; + virtual void findElmore(const Pin *load_pin, float &elmore, + bool &exists) const; + virtual void setElmore(const Pin *load_pin, float elmore); + +private: + float elmore_res_; + float elmore_cap_; + bool elmore_use_load_cap_; + const TransRiseFall *tr_; + const OperatingConditions *op_cond_; + const Corner *corner_; + const MinMax *min_max_; + Sdc *sdc_; +}; + +class ConcretePoleResidue : public ConcreteParasitic +{ +public: + ConcretePoleResidue(ComplexFloatSeq *poles, + ComplexFloatSeq *residues); + virtual ~ConcretePoleResidue(); + virtual bool isPoleResidue() const { return true; } + size_t poleResidueCount() const; + void poleResidue(int index, ComplexFloat &pole, ComplexFloat &residue) const; + void setPoleResidue(ComplexFloatSeq *poles, ComplexFloatSeq *residues); + float capacitance() const { return 0.0; } + + using ConcreteParasitic::setPoleResidue; + +private: + ComplexFloatSeq *poles_; + ComplexFloatSeq *residues_; +}; + +// Pi model for a driver pin and the pole/residues to each load. +class ConcretePiPoleResidue : public ConcretePi, + public ConcreteParasitic +{ +public: + ConcretePiPoleResidue(float c2, + float rpi, + float c1); + virtual ~ConcretePiPoleResidue(); + virtual bool isPiPoleResidue() const { return true; } + virtual bool isPiModel() const { return true; } + virtual float capacitance() const; + virtual void piModel(float &c2, + float &rpi, + float &c1) const; + virtual void setPiModel(float c2, + float rpi, + float c1); + virtual bool isReducedParasiticNetwork() const; + virtual void setIsReduced(bool reduced); + virtual Parasitic *findPoleResidue(const Pin *load_pin) const; + virtual void setPoleResidue(const Pin *load_pin, + ComplexFloatSeq *poles, + ComplexFloatSeq *residues); + void deleteLoad(const Pin *load_pin); + +private: + ConcretePoleResidueMap *load_pole_residue_; +}; + +class ConcreteParasiticNode : public ParasiticNode +{ +public: + virtual ~ConcreteParasiticNode() {} + float capacitance() const; + virtual const char *name(const Network *network) const = 0; + virtual bool isPinNode() const { return false; } + ConcreteParasiticDeviceSeq *devices() { return &devices_; } + void incrCapacitance(float cap); + void addDevice(ConcreteParasiticDevice *device); + +protected: + ConcreteParasiticNode(); + + float cap_; + ConcreteParasiticDeviceSeq devices_; + + friend class ConcreteParasiticNetwork; +}; + +class ConcreteParasiticSubNode : public ConcreteParasiticNode +{ +public: + ConcreteParasiticSubNode(const Net *net, + int id); + virtual const char *name(const Network *network) const; + +private: + const Net *net_; + int id_; +}; + +class ConcreteParasiticPinNode : public ConcreteParasiticNode +{ +public: + ConcreteParasiticPinNode(const Pin *pin); + const Pin *pin() const { return pin_; } + virtual bool isPinNode() const { return true; } + virtual const char *name(const Network *network) const; + +private: + const Pin *pin_; +}; + +class ConcreteParasiticDevice : public ParasiticDevice +{ +public: + ConcreteParasiticDevice(const char *name, + ConcreteParasiticNode *node, + float value); + virtual ~ConcreteParasiticDevice(); + virtual bool isResistor() const { return false; } + virtual bool isCouplingCap() const { return false; } + const char *name() const { return name_; } + float value() const { return value_; } + ConcreteParasiticNode *node1() const { return node_; } + virtual ConcreteParasiticNode *node2() const = 0; + virtual ParasiticNode *otherNode(ParasiticNode *node) const = 0; + virtual void replaceNode(ConcreteParasiticNode *from_node, + ConcreteParasiticNode *to_node) = 0; + +protected: + const char *name_; + ConcreteParasiticNode *node_; + float value_; + + friend class ConcreteParasiticNetwork; +}; + +class ConcreteParasiticResistor : public ConcreteParasiticDevice +{ +public: + ConcreteParasiticResistor(const char *name, + ConcreteParasiticNode *node, + ConcreteParasiticNode *other_node, + float res); + virtual bool isResistor() const { return true; } + virtual ConcreteParasiticNode *node2() const { return other_node_; } + virtual ParasiticNode *otherNode(ParasiticNode *node) const; + virtual void replaceNode(ConcreteParasiticNode *from_node, + ConcreteParasiticNode *to_node); + +private: + ConcreteParasiticNode *other_node_; +}; + +// Base class for coupling capacitors. +class ConcreteCouplingCap : public ConcreteParasiticDevice +{ +public: + ConcreteCouplingCap(const char *name, + ConcreteParasiticNode *node, + float cap); + virtual bool isCouplingCap() const { return true; } + virtual ConcreteParasiticNode *node2() const { return nullptr; } + virtual void replaceNode(ConcreteParasiticNode *from_node, + ConcreteParasiticNode *to_node); +}; + +class ConcreteCouplingCapInt : public ConcreteCouplingCap +{ +public: + ConcreteCouplingCapInt(const char *name, + ConcreteParasiticNode *node, + ConcreteParasiticNode *other_node, + float cap); + virtual bool isCouplingCap() const { return true; } + virtual ConcreteParasiticNode *node2() const { return other_node_; } + virtual ParasiticNode *otherNode(ParasiticNode *node) const; + virtual void replaceNode(ConcreteParasiticNode *from_node, + ConcreteParasiticNode *to_node); + +private: + ConcreteParasiticNode *other_node_; +}; + +class ConcreteCouplingCapExtNode : public ConcreteCouplingCap +{ +public: + ConcreteCouplingCapExtNode(const char *name, + ConcreteParasiticNode *node, + Net *other_node_net, + int other_node_id, + float cap); + virtual bool isCouplingCap() const { return true; } + virtual ParasiticNode *otherNode(ParasiticNode *node) const; + virtual void replaceNode(ConcreteParasiticNode *from_node, + ConcreteParasiticNode *to_node); + +private: +}; + +class ConcreteCouplingCapExtPin : public ConcreteCouplingCap +{ +public: + ConcreteCouplingCapExtPin(const char *name, + ConcreteParasiticNode *node, + Pin *other_node_pin, + float cap); + virtual bool isCouplingCap() const { return true; } + virtual ParasiticNode *otherNode(ParasiticNode *node) const; + virtual void replaceNode(ConcreteParasiticNode *from_node, + ConcreteParasiticNode *to_node); + +private: +}; + +class ConcreteParasiticDeviceSetIterator : public ParasiticDeviceIterator +{ +public: + ConcreteParasiticDeviceSetIterator(ConcreteParasiticDeviceSet *devices); + virtual ~ConcreteParasiticDeviceSetIterator(); + bool hasNext() { return iter_.hasNext(); } + ParasiticDevice *next() { return iter_.next(); } + +private: + ConcreteParasiticDeviceSet::ConstIterator iter_; +}; + +class ConcreteParasiticDeviceSeqIterator : public ParasiticDeviceIterator +{ +public: + ConcreteParasiticDeviceSeqIterator(ConcreteParasiticDeviceSeq *devices); + bool hasNext() { return iter_.hasNext(); } + ParasiticDevice *next() { return iter_.next(); } + +private: + ConcreteParasiticDeviceSeq::ConstIterator iter_; +}; + +class ConcreteParasiticNodeSeqIterator : public ParasiticNodeIterator +{ +public: + ConcreteParasiticNodeSeqIterator(ConcreteParasiticNodeSeq *devices); + virtual ~ConcreteParasiticNodeSeqIterator(); + bool hasNext() { return iter_.hasNext(); } + ParasiticNode *next() { return iter_.next(); } + +private: + ConcreteParasiticNodeSeq::ConstIterator iter_; +}; + +class ConcreteParasiticNetwork : public ParasiticNetwork, + public ConcreteParasitic +{ +public: + ConcreteParasiticNetwork(bool includes_pin_caps); + virtual ~ConcreteParasiticNetwork(); + virtual bool isParasiticNetwork() const { return true; } + bool includesPinCaps() const { return includes_pin_caps_; } + ConcreteParasiticNode *ensureParasiticNode(const Net *net, + int id); + ConcreteParasiticNode *findNode(const Pin *pin); + ConcreteParasiticNode *ensureParasiticNode(const Pin *pin); + virtual float capacitance() const; + ConcreteParasiticPinNodeMap *pinNodes() { return &pin_nodes_; } + ConcreteParasiticSubNodeMap *subNodes() { return &sub_nodes_; } + void disconnectPin(const Pin *pin, + Net *net); + virtual ParasiticDeviceIterator *deviceIterator(); + virtual ParasiticNodeIterator *nodeIterator(); + virtual void devices(// Return value. + ConcreteParasiticDeviceSet *devices); + +private: + void deleteNodes(); + void deleteDevices(); + + ConcreteParasiticSubNodeMap sub_nodes_; + ConcreteParasiticPinNodeMap pin_nodes_; + unsigned max_node_id_:31; + bool includes_pin_caps_:1; +}; + +} // namespace +#endif diff --git a/parasitics/EstimateParasitics.cc b/parasitics/EstimateParasitics.cc new file mode 100644 index 0000000..e9931d7 --- /dev/null +++ b/parasitics/EstimateParasitics.cc @@ -0,0 +1,237 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Wireload.hh" +#include "Liberty.hh" +#include "Parasitics.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "EstimateParasitics.hh" + +namespace sta { + +// For multiple driver nets, output pin capacitances are treated as +// loads when driven by a different pin. +void +EstimateParasitics::estimatePiElmore(const Pin *drvr_pin, + const TransRiseFall *tr, + const Wireload *wireload, + float fanout, + float net_pin_cap, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + const StaState *sta, + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap) +{ + float wireload_cap, wireload_res; + wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res); + + WireloadTree tree = WireloadTree::unknown; + if (op_cond) + tree = op_cond->wireloadTree(); + switch (tree) { + case WireloadTree::worst_case: + estimatePiElmoreWorst(drvr_pin, wireload_cap, wireload_res, + fanout, net_pin_cap, tr, op_cond, corner, + min_max, sta, + 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, tr, op_cond, + corner, min_max,sta, + c2, rpi, c1, elmore_res, + elmore_cap, elmore_use_load_cap); + break; + case WireloadTree::best_case: + estimatePiElmoreBest(drvr_pin, wireload_cap, net_pin_cap, tr, + op_cond, corner, min_max, + c2, rpi, c1, elmore_res, elmore_cap, + elmore_use_load_cap); + break; + } +} + +// No wire resistance, so load is lumped capacitance. +void +EstimateParasitics::estimatePiElmoreBest(const Pin *, + float wireload_cap, + float net_pin_cap, + const TransRiseFall *, + const OperatingConditions *, + const Corner *, + 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; + c1 = 0.0; + elmore_res = 0.0; + elmore_cap = 0.0; + elmore_use_load_cap = false; +} + +// All load capacitance (except driver pin cap) is on the far side of +// the resistor. +void +EstimateParasitics::estimatePiElmoreWorst(const Pin *drvr_pin, + float wireload_cap, + float wireload_res, + float, + float net_pin_cap, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + const StaState *sta, + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap) +{ + Sdc *sdc = sta->sdc(); + float drvr_pin_cap = 0.0; + drvr_pin_cap = sdc->pinCapacitance(drvr_pin, tr, op_cond, corner, min_max); + c2 = drvr_pin_cap; + rpi = wireload_res; + c1 = net_pin_cap - drvr_pin_cap + wireload_cap; + elmore_res = wireload_res; + elmore_cap = c1; + elmore_use_load_cap = false; +} + +// Each load capacitance and wireload cap/fanout has resistance/fanout +// connecting it to the driver. +// 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 TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + const StaState *sta, + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap) +{ + if (wireload_res == 0.0) { + // No resistance, so load is capacitance only. + c2 = wireload_cap + net_pin_cap; + rpi = 0.0; + c1 = 0.0; + elmore_res = 0.0; + elmore_cap = 0.0; + elmore_use_load_cap = false; + } + else { + Sdc *sdc = sta->sdc(); + Network *network = sta->network(); + double res_fanout = wireload_res / fanout; + double cap_fanout = wireload_cap / fanout; + // Find admittance moments. + double y1 = 0.0; + double y2 = 0.0; + double y3 = 0.0; + y1 = sdc->pinCapacitance(drvr_pin, tr, op_cond, corner, min_max); + PinConnectedPinIterator *load_iter = + network->connectedPinIterator(drvr_pin); + while (load_iter->hasNext()) { + const Pin *load_pin = load_iter->next(); + // Bidirects don't count themselves as loads. + if (load_pin != drvr_pin && network->isLoad(load_pin)) { + Port *port = network->port(load_pin); + double load_cap = 0.0; + if (network->isLeaf(load_pin)) + load_cap = sdc->pinCapacitance(load_pin, tr, op_cond, + corner, min_max); + else if (network->isTopLevelPort(load_pin)) + load_cap = sdc->portExtCap(port, tr, min_max); + else + internalError("load pin not leaf or top level"); + double cap = load_cap + cap_fanout; + double y2_ = res_fanout * cap * cap; + y1 += cap; + y2 += -y2_; + y3 += y2_ * res_fanout * cap; + } + } + delete load_iter; + + if (y3 == 0) { + // No loads. + c1 = 0.0; + c2 = 0.0; + rpi = 0.0; + } + else { + c1 = static_cast(y2 * y2 / y3); + c2 = static_cast(y1 - y2 * y2 / y3); + rpi = static_cast(-y3 * y3 / (y2 * y2 * y2)); + } + elmore_res = static_cast(res_fanout); + elmore_cap = static_cast(cap_fanout); + elmore_use_load_cap = true; + } +} + +#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 diff --git a/parasitics/EstimateParasitics.hh b/parasitics/EstimateParasitics.hh new file mode 100644 index 0000000..d7f6e6a --- /dev/null +++ b/parasitics/EstimateParasitics.hh @@ -0,0 +1,94 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_ESTIMATE_PARASITICS_H +#define STA_ESTIMATE_PARASITICS_H + +#include "LibertyClass.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" +#include "ParasiticsClass.hh" + +namespace sta { + +class EstimateParasitics +{ +public: + +protected: + // Helper function for wireload estimation. + void estimatePiElmore(const Pin *drvr_pin, + const TransRiseFall *tr, + const Wireload *wireload, + float fanout, + float net_pin_cap, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + const StaState *sta, + // Return values. + float &c2, + float &rpi, + float &c1, + float &elmore_res, + float &elmore_cap, + bool &elmore_use_load_cap); + void estimatePiElmoreBest(const Pin *drvr_pin, + float net_pin_cap, + float wireload_cap, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + 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; + void estimatePiElmoreWorst(const Pin *drvr_pin, + float wireload_cap, + float wireload_res, + float fanout, + float net_pin_cap, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + const StaState *sta, + // 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 TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + const StaState *sta, + // Return values. + float &c2, float &rpi, float &c1, + float &elmore_res, float &elmore_cap, + bool &elmore_use_load_cap); +}; + +} // namespace +#endif diff --git a/parasitics/MakeConcreteParasitics.hh b/parasitics/MakeConcreteParasitics.hh new file mode 100644 index 0000000..f550827 --- /dev/null +++ b/parasitics/MakeConcreteParasitics.hh @@ -0,0 +1,26 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_MAKE_CONCRETE_PARASITICS_H +#define STA_MAKE_CONCRETE_PARASITICS_H + +namespace sta { + +Parasitics * +makeConcreteParasitics(StaState *sta); + +} // namespace +#endif diff --git a/parasitics/Makefile.am b/parasitics/Makefile.am new file mode 100644 index 0000000..9367e80 --- /dev/null +++ b/parasitics/Makefile.am @@ -0,0 +1,64 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +lib_LTLIBRARIES = libparasitics.la + +include_HEADERS = \ + ConcreteParasitics.hh \ + ConcreteParasiticsPvt.hh \ + EstimateParasitics.hh \ + NullParasitics.hh \ + Parasitics.hh \ + ParasiticsClass.hh \ + ReduceParasitics.hh \ + SpefNamespace.hh \ + SpefReader.hh + +libparasitics_la_SOURCES = \ + ConcreteParasitics.cc \ + EstimateParasitics.cc \ + NullParasitics.cc \ + Parasitics.cc \ + ReduceParasitics.cc \ + SpefLex.ll \ + SpefNamespace.cc \ + SpefParse.yy \ + SpefReader.cc \ + SpefReaderPvt.hh \ + SpfReaderPvt.hh + +SpefLex.ll: SpefParse.hh + +SpefLex.cc: SpefLex.ll + $(LEX) $(LFLAGS) -o SpefLex.cc --prefix=SpefLex_ --header-file=SpefLex.hh SpefLex.ll + +SpefParse.hh: SpefParse.cc + if test -f SpefParse.h; then \ + cp SpefParse.h SpefParse.hh; \ + fi + +TCL_SRCS = \ + Parasitics.i \ + Parasitics.tcl + +EXTRA_DIST = \ + $(TCL_SRCS) \ + SpefParse.hh + +libs: $(lib_LTLIBRARIES) + +xtags: $(SOURCES) $(HEADERS) + etags -a -o ../TAGS $(SOURCES) $(HEADERS) $(TCL_SRCS) diff --git a/parasitics/NullParasitics.cc b/parasitics/NullParasitics.cc new file mode 100644 index 0000000..318bb88 --- /dev/null +++ b/parasitics/NullParasitics.cc @@ -0,0 +1,457 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "NullParasitics.hh" + +namespace sta { + +NullParasitics::NullParasitics(StaState *sta) : + Parasitics(sta) +{ +} + +bool +NullParasitics::haveParasitics() +{ + return false; +} + +void +NullParasitics::clear() +{ +} + +void +NullParasitics::save() +{ +} + +void +NullParasitics::deleteParasitics() +{ +} + +void +NullParasitics::deleteParasitics(const Net *, + const ParasiticAnalysisPt *) +{ +} + +void +NullParasitics::deleteParasitics(const Pin *, const ParasiticAnalysisPt *) +{ +} + +void +NullParasitics::deleteUnsavedParasitic(Parasitic *) +{ +} + +void +NullParasitics::deleteDrvrReducedParasitics(const Pin *) +{ +} + +float +NullParasitics::capacitance(Parasitic *) const +{ + return 0.0; +} + +Parasitic * +NullParasitics::findPiElmore(const Pin *, + const TransRiseFall *, + const ParasiticAnalysisPt *) const +{ + return nullptr; +} + +Parasitic * +NullParasitics::makePiElmore(const Pin *, + const TransRiseFall *, + const ParasiticAnalysisPt *, + float, + float, + float) +{ + return nullptr; +} + +bool +NullParasitics::isPiElmore(Parasitic *) const +{ + return false; +} + +bool +NullParasitics::isReducedParasiticNetwork(Parasitic *) const +{ + return false; +} + +void +NullParasitics::setIsReducedParasiticNetwork(Parasitic *, + bool) +{ +} + +void +NullParasitics::piModel(Parasitic *, + float &, + float &, + float &) const +{ +} + +void +NullParasitics::setPiModel(Parasitic *, + float, + float, + float) +{ +} + +void +NullParasitics::findElmore(Parasitic *, + const Pin *, + float &, + bool &) const +{ +} + +void +NullParasitics::setElmore(Parasitic *, + const Pin *, + float) +{ +} + +bool +NullParasitics::isPiModel(Parasitic*) const +{ + return false; +} + +bool +NullParasitics::isPiPoleResidue(Parasitic* ) const +{ + return false; +} + +Parasitic * +NullParasitics::findPiPoleResidue(const Pin *, + const TransRiseFall *, + const ParasiticAnalysisPt *) const +{ + return nullptr; +} + +Parasitic * +NullParasitics::makePiPoleResidue(const Pin *, + const TransRiseFall *, + const ParasiticAnalysisPt *, + float, + float, + float) +{ + return nullptr; +} + +Parasitic * +NullParasitics::findPoleResidue(const Parasitic *, + const Pin *) const +{ + return nullptr; +} + +void +NullParasitics::setPoleResidue(Parasitic *, + const Pin *, + ComplexFloatSeq *, + ComplexFloatSeq *) +{ +} + +bool +NullParasitics::isPoleResidue(const Parasitic *) const +{ + return false; +} + +size_t +NullParasitics::poleResidueCount(const Parasitic *) const +{ + return 0; +} + +void +NullParasitics::poleResidue(const Parasitic *, + int, + ComplexFloat &, + ComplexFloat &) const +{ +} + +bool +NullParasitics::isParasiticNetwork(Parasitic *) const +{ + return false; +} + +Parasitic * +NullParasitics::findParasiticNetwork(const Net *, + const ParasiticAnalysisPt *) const +{ + return nullptr; +} + +Parasitic * +NullParasitics::findParasiticNetwork(const Pin *, + const ParasiticAnalysisPt *) const +{ + return nullptr; +} + +Parasitic * +NullParasitics::makeParasiticNetwork(const Net *, + bool, + const ParasiticAnalysisPt *) +{ + return nullptr; +} + +bool +NullParasitics::includesPinCaps(Parasitic *) const +{ + return false; +} + +void +NullParasitics::deleteParasiticNetwork(const Net *, + const ParasiticAnalysisPt *) +{ +} + +ParasiticNode * +NullParasitics::ensureParasiticNode(Parasitic *, + const Net *, + int) +{ + return nullptr; +} + +ParasiticNode * +NullParasitics::ensureParasiticNode(Parasitic *, + const Pin *) +{ + return nullptr; +} + +void +NullParasitics::incrCap(ParasiticNode *, + float, + const ParasiticAnalysisPt *) +{ +} + +void +NullParasitics::makeCouplingCap(const char *, + ParasiticNode *, + ParasiticNode *, + float, + const ParasiticAnalysisPt *) +{ +} + +void NullParasitics::makeCouplingCap(const char *, + ParasiticNode *, + Net *, + int, + float, + const ParasiticAnalysisPt *) +{ +} + +void +NullParasitics::makeCouplingCap(const char *, + ParasiticNode *, + Pin *, + float, + const ParasiticAnalysisPt *) +{ +} + +void +NullParasitics::makeResistor(const char *, + ParasiticNode *, + ParasiticNode *, + float, + const ParasiticAnalysisPt *) +{ +} + +const char * +NullParasitics::name(const ParasiticNode *) +{ + return nullptr; +} + +const Pin * +NullParasitics::connectionPin(const ParasiticNode *) const +{ + return nullptr; +} + +ParasiticNode * +NullParasitics::findNode(Parasitic *, + const Pin *) const +{ + return nullptr; +} + +float +NullParasitics::nodeGndCap(const ParasiticNode *, + const ParasiticAnalysisPt *) const +{ + return 0.0; +} + +ParasiticDeviceIterator * +NullParasitics::deviceIterator(ParasiticNode *) const +{ + return 0; +} + +bool +NullParasitics::isResistor(const ParasiticDevice *) const +{ + return false; +} + +bool +NullParasitics::isCouplingCap(const ParasiticDevice *) const +{ + return false; +} + +const char * +NullParasitics::name(const ParasiticDevice *) const +{ + return nullptr; +} + +float +NullParasitics::value(const ParasiticDevice *, + const ParasiticAnalysisPt *) const +{ + return 0.0; +} + +ParasiticNode * +NullParasitics::node1(const ParasiticDevice *) const +{ + return nullptr; +} + +ParasiticNode * +NullParasitics::node2(const ParasiticDevice *) const +{ + return nullptr; +} + +ParasiticNode * +NullParasitics::otherNode(const ParasiticDevice *, + ParasiticNode *) const +{ + return nullptr; +} + +void +NullParasitics::reduceTo(Parasitic *, + const Net *, + ReduceParasiticsTo , + const OperatingConditions *, + const Corner *, + const MinMax *, + const ParasiticAnalysisPt *) +{ +} + +void +NullParasitics::reduceToPiElmore(Parasitic *, + const Net *, + const OperatingConditions *, + const Corner *, + const MinMax *, + const ParasiticAnalysisPt *) +{ +} + +void +NullParasitics::reduceToPiElmore(Parasitic *, + const Pin *, + const OperatingConditions *, + const Corner *, + const MinMax *, + const ParasiticAnalysisPt *) +{ +} + +void +NullParasitics::reduceToPiPoleResidue2(Parasitic *, const Net *, + const OperatingConditions *, + const Corner *, + const MinMax *, + const ParasiticAnalysisPt *) +{ +} + +void +NullParasitics::reduceToPiPoleResidue2(Parasitic *, + const Pin *, + const OperatingConditions *, + const Corner *, + const MinMax *, + const ParasiticAnalysisPt *) +{ +} + +Parasitic * +NullParasitics::estimatePiElmore(const Pin *, + const TransRiseFall *, + const Wireload *, + float, + float, + const OperatingConditions *, + const Corner *, + const MinMax *, + const ParasiticAnalysisPt *) +{ + return nullptr; +} + +void +NullParasitics::disconnectPinBefore(const Pin *) +{ +} + +void +NullParasitics::loadPinCapacitanceChanged(const Pin *) +{ +} + +} // namespace diff --git a/parasitics/NullParasitics.hh b/parasitics/NullParasitics.hh new file mode 100644 index 0000000..406b1eb --- /dev/null +++ b/parasitics/NullParasitics.hh @@ -0,0 +1,193 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_NULL_PARASITICS_H +#define STA_NULL_PARASITICS_H + +#include "DisallowCopyAssign.hh" +#include "Parasitics.hh" + +namespace sta { + +// Parasitics that are not in the house. +class NullParasitics : public Parasitics +{ +public: + NullParasitics(StaState *sta); + virtual bool haveParasitics(); + virtual void clear(); + virtual void save(); + virtual void deleteParasitics(); + virtual void deleteParasitics(const Net *net, + const ParasiticAnalysisPt *ap); + virtual void deleteParasitics(const Pin *pin, + const ParasiticAnalysisPt *ap); + virtual void deleteUnsavedParasitic(Parasitic *parasitic); + virtual void deleteDrvrReducedParasitics(const Pin *drvr_pin); + + virtual float capacitance(Parasitic *parasitic) const; + + virtual Parasitic * + findPiElmore(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap) const; + virtual Parasitic *makePiElmore(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap, + float c2, float rpi, float c1); + virtual bool isPiElmore(Parasitic *parasitic) const; + virtual bool + isReducedParasiticNetwork(Parasitic *parasitic) const; + virtual void setIsReducedParasiticNetwork(Parasitic *parasitic, + bool is_reduced); + virtual void piModel(Parasitic *parasitic, float &c2, float &rpi, + float &c1) const; + virtual void setPiModel(Parasitic *parasitic, float c2, float rpi, + float c1); + virtual void findElmore(Parasitic *parasitic, const Pin *load_pin, + float &elmore, bool &exists) const; + virtual void setElmore(Parasitic *parasitic, const Pin *load_pin, + float elmore); + + virtual bool isPiModel(Parasitic* parasitic) const; + virtual bool isPiPoleResidue(Parasitic* parasitic) const; + virtual Parasitic * + findPiPoleResidue(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap) const; + virtual Parasitic *makePiPoleResidue(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap, + float c2, float rpi, + float c1); + virtual Parasitic *findPoleResidue(const Parasitic *parasitic, + const Pin *load_pin) const; + virtual void setPoleResidue(Parasitic *parasitic, const Pin *load_pin, + ComplexFloatSeq *poles, + ComplexFloatSeq *residues); + virtual bool isPoleResidue(const Parasitic* parasitic) const; + virtual size_t poleResidueCount(const Parasitic *parasitic) const; + virtual void poleResidue(const Parasitic *parasitic, int pole_index, + ComplexFloat &pole, ComplexFloat &residue) const; + + virtual bool isParasiticNetwork(Parasitic *parasitic) const; + virtual Parasitic *findParasiticNetwork(const Net *net, + const ParasiticAnalysisPt *ap) const; + virtual Parasitic * + findParasiticNetwork(const Pin *pin, + const ParasiticAnalysisPt *ap) const; + virtual Parasitic * + makeParasiticNetwork(const Net *net, + bool pin_cap_included, + const ParasiticAnalysisPt *ap); + virtual ParasiticDeviceIterator *deviceIterator(Parasitic *) { return nullptr; } + virtual ParasiticNodeIterator *nodeIterator(Parasitic *) { return nullptr; } + virtual bool includesPinCaps(Parasitic *parasitic) const; + virtual void deleteParasiticNetwork(const Net *net, + const ParasiticAnalysisPt *ap); + virtual ParasiticNode *ensureParasiticNode(Parasitic *parasitic, + const Net *net, + int id); + virtual ParasiticNode *ensureParasiticNode(Parasitic *parasitic, + const Pin *pin); + virtual void incrCap(ParasiticNode *node, float cap, + const ParasiticAnalysisPt *ap); + virtual void makeCouplingCap(const char *name, + ParasiticNode *node, + ParasiticNode *other_node, + float cap, const ParasiticAnalysisPt *ap); + virtual void makeCouplingCap(const char *name, + ParasiticNode *node, + Net *other_node_net, int other_node_id, + float cap, const ParasiticAnalysisPt *ap); + virtual void makeCouplingCap(const char *name, + ParasiticNode *node, + Pin *other_node_pin, + float cap, const ParasiticAnalysisPt *ap); + virtual void makeResistor(const char *name, ParasiticNode *node1, + ParasiticNode *node2, float res, + const ParasiticAnalysisPt *ap); + + virtual const char *name(const ParasiticNode *node); + virtual const Pin *connectionPin(const ParasiticNode *node) const; + virtual ParasiticNode *findNode(Parasitic *parasitic, + const Pin *pin) const; + virtual float nodeGndCap(const ParasiticNode *node, + const ParasiticAnalysisPt *ap) const; + virtual ParasiticDeviceIterator * + deviceIterator(ParasiticNode *node) const; + virtual bool isResistor(const ParasiticDevice *device) const; + virtual bool isCouplingCap(const ParasiticDevice *device)const; + virtual const char *name(const ParasiticDevice *device) const; + virtual float value(const ParasiticDevice *device, + const ParasiticAnalysisPt *ap) const; + virtual ParasiticNode *node1(const ParasiticDevice *device) const; + virtual ParasiticNode *node2(const ParasiticDevice *device) const; + virtual ParasiticNode *otherNode(const ParasiticDevice *device, + ParasiticNode *node) const; + // Reduce parasitic network to reduce_to model. + virtual void reduceTo(Parasitic *parasitic, + const Net *net, + ReduceParasiticsTo reduce_to, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap); + virtual void reduceToPiElmore(Parasitic *parasitic, + const Net *net, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap); + // Reduce parasitic network to pi elmore model for drvr_pin. + virtual void reduceToPiElmore(Parasitic *parasitic, + const Pin *drvr_pin, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap); + virtual void reduceToPiPoleResidue2(Parasitic *parasitic, + const Net *net, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap); + virtual void reduceToPiPoleResidue2(Parasitic *parasitic, + const Pin *drvr_pin, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap); + virtual Parasitic * + estimatePiElmore(const Pin *drvr_pin, + const TransRiseFall *tr, + const Wireload *wireload, + float fanout, + float net_pin_cap, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + const ParasiticAnalysisPt *ap); + + virtual void disconnectPinBefore(const Pin *pin); + virtual void loadPinCapacitanceChanged(const Pin *pin); + +private: + DISALLOW_COPY_AND_ASSIGN(NullParasitics); +}; + +} // namespace +#endif diff --git a/parasitics/Parasitics.cc b/parasitics/Parasitics.cc new file mode 100644 index 0000000..dfac126 --- /dev/null +++ b/parasitics/Parasitics.cc @@ -0,0 +1,212 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Error.hh" +#include "Debug.hh" +#include "Wireload.hh" +#include "PortDirection.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "ReduceParasitics.hh" +#include "Parasitics.hh" + +namespace sta { + +Parasitics::Parasitics(StaState *sta) : + StaState(sta) +{ +} + +Net * +Parasitics::findParasiticNet(const Pin *pin) const +{ + Net *net = network_->net(pin); + // Pins on the top level instance may not have nets. + // Use the net connected to the pin's terminal. + if (net == nullptr && network_->isTopLevelPort(pin)) { + Term *term = network_->term(pin); + if (term) + return network_->net(term); + else + return nullptr; + } + if (net) + return network_->highestConnectedNet(net); + else + return nullptr; +} + +void +Parasitics::check(Parasitic *) const +{ +#if 0 + ConcreteParasiticSubNodeMap::Iterator sub_node_iter(sub_nodes_); + while (sub_node_iter.hasNext()) { + ConcreteParasiticSubNode *node = sub_node_iter.next(); + ConcreteParasiticDeviceSeq::Iterator device_iter(node->devices()); + int res_count = 0; + while (device_iter.hasNext()) { + ConcreteParasiticDevice *device = device_iter.next(); + if (device->isResistor()) + res_count++; + } + if (res_count == 0) + report->warn("sub node %s has no resistor connections\n", + node->name(network)); + else if (res_count == 1) + report->warn("sub node %s has one resistor connection\n", + node->name(network)); + } +#endif +} + +//////////////////////////////////////////////////////////////// + +Parasitic * +Parasitics::makeWireloadNetwork(const Pin *drvr_pin, + const Wireload *wireload, + float fanout, + const OperatingConditions *op_cond, + const ParasiticAnalysisPt *ap) +{ + Net *net = network_->net(drvr_pin); + Parasitic *parasitic = makeParasiticNetwork(net, false, ap); + float wireload_cap, wireload_res; + wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res); + + WireloadTree tree = WireloadTree::balanced; + if (op_cond) + tree = op_cond->wireloadTree(); + switch (tree) { + case WireloadTree::worst_case: + makeWireloadNetworkWorst(parasitic, drvr_pin, wireload_cap, + wireload_res, fanout, ap); + break; + case WireloadTree::balanced: + makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap, + wireload_res, fanout, ap); + break; + case WireloadTree::best_case: + case WireloadTree::unknown: + makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap, + wireload_res, fanout, ap); + break; + } + return parasitic; +} + +// All load capacitance (except driver pin cap) is on the far side of +// the resistor. +void +Parasitics::makeWireloadNetworkWorst(Parasitic *parasitic, + const Pin *drvr_pin, + float wireload_cap, + float wireload_res, + float /* fanout */, + const ParasiticAnalysisPt *ap) +{ + ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin); + Net *net = network_->net(drvr_pin); + ParasiticNode *load_node = ensureParasiticNode(parasitic, net, 0); + makeResistor(nullptr, drvr_node, load_node, wireload_res, ap); + parasitics_->incrCap(load_node, wireload_cap, ap); + 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)) { + ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin); + makeResistor(nullptr, load_node, load_node1, 0.0, ap); + } + } +} + +// 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 ParasiticAnalysisPt *ap) +{ + ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin); + parasitics_->incrCap(drvr_node, wireload_cap, ap); + 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)) { + ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin); + makeResistor(nullptr, drvr_node, load_node1, 0.0, ap); + } + } +} + +// Each load capacitance and wireload cap/fanout has resistance/fanout +// connecting it to the driver. +void +Parasitics::makeWireloadNetworkBalanced(Parasitic *parasitic, + const Pin *drvr_pin, + float wireload_cap, + float wireload_res, + float fanout, + const ParasiticAnalysisPt *ap) +{ + float fanout_cap = wireload_cap / fanout; + float fanout_res = wireload_res / fanout; + ParasiticNode *drvr_node = ensureParasiticNode(parasitic, 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)) { + ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin); + makeResistor(nullptr, drvr_node, load_node1, fanout_res, ap); + parasitics_->incrCap(load_node1, fanout_cap, ap); + } + } +} + +//////////////////////////////////////////////////////////////// + +ParasiticAnalysisPt::ParasiticAnalysisPt(const char *name, + int index, + const MinMax *min_max) : + name_(stringCopy(name)), + index_(index), + min_max_(min_max), + coupling_cap_factor_(1.0) +{ +} + +ParasiticAnalysisPt::~ParasiticAnalysisPt() +{ + stringDelete(name_); +} + +void +ParasiticAnalysisPt::setCouplingCapFactor(float factor) +{ + coupling_cap_factor_ = factor; +} + +} // namespace diff --git a/parasitics/Parasitics.hh b/parasitics/Parasitics.hh new file mode 100644 index 0000000..023e581 --- /dev/null +++ b/parasitics/Parasitics.hh @@ -0,0 +1,332 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PARASITICS_H +#define STA_PARASITICS_H + +#include +#include "DisallowCopyAssign.hh" +#include "StaState.hh" +#include "LibertyClass.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" +#include "ParasiticsClass.hh" + +namespace sta { + +class Wireload; +class Corner; + +typedef std::complex ComplexFloat; +typedef Vector ComplexFloatSeq; +typedef Iterator ParasiticDeviceIterator; +typedef Iterator ParasiticNodeIterator; + +// 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. +// If there is only one parasitic for both rise and fall transitions +// the sta parasitic readers will save it under the rise transition. +class Parasitics : public StaState +{ +public: + virtual ~Parasitics() {} + virtual bool haveParasitics() = 0; + // Clear all state. + virtual void clear() = 0; + + // Save parasitics to database file. + virtual void save() = 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; + // Delete all parasitics on pin at analysis point. + virtual void deleteParasitics(const Pin *pin, + const ParasiticAnalysisPt *ap) = 0; + virtual void deleteUnsavedParasitic(Parasitic *parasitic) = 0; + virtual void deleteDrvrReducedParasitics(const Pin *drvr_pin) = 0; + + virtual bool isReducedParasiticNetwork(Parasitic *parasitic) const = 0; + // Flag this parasitic as reduced from a parasitic network. + virtual void setIsReducedParasiticNetwork(Parasitic *parasitic, + bool is_reduced) = 0; + + // Capacitance value of parasitic object. + virtual float capacitance(Parasitic *parasitic) const = 0; + + //////////////////////////////////////////////////////////////// + // Pi model driver load with elmore delays to load pins (RSPF). + // This follows the SPEF documentation of c2/c1, with c2 being the + // capacitor on the driver pin. + virtual bool isPiElmore(Parasitic *parasitic) const = 0; + virtual Parasitic *findPiElmore(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap) const = 0; + virtual Parasitic *makePiElmore(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap, + float c2, + float rpi, + float c1) = 0; + + //////////////////////////////////////////////////////////////// + // Pi models are common to PiElmore and PiPoleResidue. + virtual bool isPiModel(Parasitic *parasitic) const = 0; + virtual void piModel(Parasitic *parasitic, + float &c2, + float &rpi, + float &c1) const = 0; + // Set PI model parameters. + virtual void setPiModel(Parasitic *parasitic, + float c2, + float rpi, + float c1) = 0; + + //////////////////////////////////////////////////////////////// + // Elmore driver to load delay. + // Common to LumpedElmore and PiElmore parasitics. + virtual void findElmore(Parasitic *parasitic, + 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; + + //////////////////////////////////////////////////////////////// + // Pi model driver load with pole/residue interconnect model to load pins. + virtual bool isPiPoleResidue(Parasitic* parasitic) const = 0; + virtual Parasitic *findPiPoleResidue(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap) const=0; + virtual Parasitic *makePiPoleResidue(const Pin *drvr_pin, + const TransRiseFall *tr, + const ParasiticAnalysisPt *ap, + float c2, + float rpi, + float c1) = 0; + virtual Parasitic *findPoleResidue(const Parasitic *parasitic, + 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; + 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; + + //////////////////////////////////////////////////////////////// + // 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(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 *makeParasiticNetwork(const Net *net, + bool includes_pin_caps, + const ParasiticAnalysisPt *ap) = 0; + virtual ParasiticDeviceIterator *deviceIterator(Parasitic *parasitic) = 0; + virtual ParasiticNodeIterator *nodeIterator(Parasitic *parasitic) = 0; + // Delete parasitic network if it exists. + virtual void deleteParasiticNetwork(const Net *net, + const ParasiticAnalysisPt *ap) = 0; + // True if the parasitic network caps include pin capacitances. + virtual bool includesPinCaps(Parasitic *parasitic) const = 0; + // Parasitic network component builders. + // Make a subnode of the parasitic network net. + virtual ParasiticNode *ensureParasiticNode(Parasitic *parasitic, + const Net *net, + int id) = 0; + // Make a subnode of the parasitic network net connected to pin. + virtual ParasiticNode *ensureParasiticNode(Parasitic *parasitic, + const Pin *pin) = 0; + // Increment the grounded capacitance on node. + virtual void incrCap(ParasiticNode *node, + float cap, + const ParasiticAnalysisPt *ap) = 0; + // Coupling capacitor between parasitic nodes on a net. + // name is optional. The device takes ownership of the name string. + virtual void makeCouplingCap(const char *name, + ParasiticNode *node, + ParasiticNode *other_node, + float cap, + const ParasiticAnalysisPt *ap) = 0; + // Coupling capacitor to parasitic node on a different net. + // name is optional. The device takes ownership of the name string. + virtual void makeCouplingCap(const char *name, + ParasiticNode *node, + Net *other_node_net, + int other_node_id, + float cap, + const ParasiticAnalysisPt *ap) = 0; + // Coupling capacitor to pin on a different net. + // name is optional. The device takes ownership of the name string. + virtual void makeCouplingCap(const char *name, + ParasiticNode *node, + Pin *other_node_pin, + float cap, + const ParasiticAnalysisPt *ap) = 0; + // name is optional. The device takes ownership of the name string. + virtual void makeResistor(const char *name, + ParasiticNode *node1, + ParasiticNode *node2, + float res, + const ParasiticAnalysisPt *ap) = 0; + // Check integrity of parasitic network. + void check(Parasitic *parasitic) const; + + virtual const char *name(const ParasiticNode *node) = 0; + virtual const Pin *connectionPin(const ParasiticNode *node) const = 0; + // Find the parasitic node connected to pin. + virtual ParasiticNode *findNode(Parasitic *parasitic, + const Pin *pin) const = 0; + // Node capacitance to ground. + virtual float nodeGndCap(const ParasiticNode *node, + const ParasiticAnalysisPt *ap) const = 0; + virtual ParasiticDeviceIterator * + deviceIterator(ParasiticNode *node) const = 0; + virtual bool isResistor(const ParasiticDevice *device) const = 0; + virtual bool isCouplingCap(const ParasiticDevice *device) const = 0; + virtual const char *name(const ParasiticDevice *device) const = 0; + // Device "value" (resistance, capacitance). + virtual float value(const ParasiticDevice *device, + const ParasiticAnalysisPt *ap) const = 0; + virtual ParasiticNode *node1(const ParasiticDevice *device) const = 0; + virtual ParasiticNode *node2(const ParasiticDevice *device) const = 0; + virtual ParasiticNode *otherNode(const ParasiticDevice *device, + ParasiticNode *node) const = 0; + + // Reduce parasitic network to reduce_to model. + virtual void reduceTo(Parasitic *parasitic, + const Net *net, + ReduceParasiticsTo reduce_to, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap) = 0; + // Reduce parasitic network to pi elmore models. + virtual void reduceToPiElmore(Parasitic *parasitic, + const Net *net, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap) = 0; + // Reduce parasitic network to pi elmore model for drvr_pin. + virtual void reduceToPiElmore(Parasitic *parasitic, + const Pin *drvr_pin, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap) = 0; + // Reduce parasitic network to pi and 2nd order pole/residue models. + virtual void reduceToPiPoleResidue2(Parasitic *parasitic, + const Net *net, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap) = 0; + // Reduce parasitic network to pi and 2nd order pole/residue models + // for drvr_pin. + virtual void reduceToPiPoleResidue2(Parasitic *parasitic, + const Pin *drvr_pin, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap) = 0; + + // Estimate parasitic as pi elmore using wireload model. + virtual Parasitic *estimatePiElmore(const Pin *drvr_pin, + const TransRiseFall *tr, + const Wireload *wireload, + float fanout, + float net_pin_cap, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + const ParasiticAnalysisPt *ap) = 0; + Parasitic *makeWireloadNetwork(const Pin *drvr_pin, + const Wireload *wireload, + float fanout, + const OperatingConditions *op_cond, + const ParasiticAnalysisPt *ap); + // Network edit before/after methods. + virtual void disconnectPinBefore(const Pin *pin) = 0; + virtual void loadPinCapacitanceChanged(const Pin *pin) = 0; + +protected: + void makeWireloadNetworkWorst(Parasitic *parasitic, + const Pin *drvr_pin, + float wireload_cap, + float wireload_res, + float fanout, + const ParasiticAnalysisPt *ap); + void makeWireloadNetworkBest(Parasitic *parasitic, + const Pin *drvr_pin, + float wireload_cap, + float wireload_res, + float fanout, + const ParasiticAnalysisPt *ap); + void makeWireloadNetworkBalanced(Parasitic *parasitic, + const Pin *drvr_pin, + float wireload_cap, + float wireload_res, + float fanout, + const ParasiticAnalysisPt *ap); + + Parasitics(StaState *sta); + Net *findParasiticNet(const Pin *pin) const; + +private: + DISALLOW_COPY_AND_ASSIGN(Parasitics); +}; + +// Managed by the Corner class. +class ParasiticAnalysisPt +{ +public: + ParasiticAnalysisPt(const char *name, + int index, + const MinMax *min_max); + ~ParasiticAnalysisPt(); + const char *name() const { return name_; } + int index() const { return index_; } + const MinMax *minMax() const { return min_max_; } + // Coupling capacitor factor used by all reduction functions. + float couplingCapFactor() const { return coupling_cap_factor_; } + void setCouplingCapFactor(float factor); + +private: + const char *name_; + int index_; + const MinMax *min_max_; + float coupling_cap_factor_; +}; + +} // namespace +#endif diff --git a/parasitics/Parasitics.i b/parasitics/Parasitics.i new file mode 100644 index 0000000..9ecefbc --- /dev/null +++ b/parasitics/Parasitics.i @@ -0,0 +1,107 @@ +%module parasitics + +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Sta.hh" + +using sta::Sta; +using sta::cmdLinkedNetwork; +using sta::Instance; +using sta::MinMaxAll; +using sta::ReduceParasiticsTo; +using sta::TransRiseFall; +using sta::Pin; +using sta::TmpFloatSeq; + +%} + +%inline %{ + +bool +read_spef_cmd(const char *filename, + Instance *instance, + MinMaxAll *min_max, + bool increment, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + ReduceParasiticsTo reduce_to, + bool delete_after_reduce, + bool quiet, + bool save) +{ + cmdLinkedNetwork(); + return Sta::sta()->readSpef(filename, instance, min_max, + increment, pin_cap_included, + keep_coupling_caps, coupling_cap_factor, + reduce_to, delete_after_reduce, + save, quiet); +} + +TmpFloatSeq * +find_pi_elmore(Pin *drvr_pin, + TransRiseFall *tr, + MinMax *min_max) +{ + float c2, rpi, c1; + bool exists; + Sta::sta()->findPiElmore(drvr_pin, tr, min_max, c2, rpi, c1, exists); + TmpFloatSeq *floats = new FloatSeq; + if (exists) { + floats->push_back(c2); + floats->push_back(rpi); + floats->push_back(c1); + } + return floats; +} + +float +find_elmore(Pin *drvr_pin, + Pin *load_pin, + TransRiseFall *tr, + MinMax *min_max) +{ + float elmore = 0.0; + bool exists; + Sta::sta()->findElmore(drvr_pin, load_pin, tr, min_max, elmore, exists); + return elmore; +} + +void +set_pi_model_cmd(Pin *drvr_pin, + TransRiseFall *tr, + MinMaxAll *min_max, + float c2, + float rpi, + float c1) +{ + Sta::sta()->makePiElmore(drvr_pin, tr, min_max, c2, rpi, c1); +} + +void +set_elmore_cmd(Pin *drvr_pin, + Pin *load_pin, + TransRiseFall *tr, + MinMaxAll *min_max, + float elmore) +{ + Sta::sta()->setElmore(drvr_pin, load_pin, tr, min_max, elmore); +} + +%} // inline diff --git a/parasitics/Parasitics.tcl b/parasitics/Parasitics.tcl new file mode 100644 index 0000000..5f3e630 --- /dev/null +++ b/parasitics/Parasitics.tcl @@ -0,0 +1,122 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +namespace eval sta { + +define_cmd_args "read_spef" \ + {[-min]\ + [-max]\ + [-elmore]\ + [-path path]\ + [-increment]\ + [-pin_cap_included]\ + [-keep_capacitive_coupling]\ + [-coupling_reduction_factor factor]\ + [-reduce_to pi_elmore|pi_pole_residue2]\ + [-delete_after_reduce]\ + [-quiet]\ + [-save]\ + filename} + +proc_redirect read_spef { + parse_key_args "read_spef" args \ + keys {-path -coupling_reduction_factor -reduce_to} \ + flags {-min -max -elmore -increment -pin_cap_included \ + -keep_capacitive_coupling \ + -delete_after_reduce -quiet -save} + check_argc_eq1 "report_spef" $args + + set instance [top_instance] + if [info exists keys(-path)] { + set path $keys(-path) + set instance [find_instance $path] + if { $instance == "NULL" } { + sta_error "path instance '$path' not found." + } + } + set min_max [parse_min_max_all_flags flags] + set increment [info exists flags(-increment)] + set coupling_reduction_factor 1.0 + if [info exists keys(-coupling_reduction_factor)] { + set coupling_reduction_factor $keys(-coupling_reduction_factor) + check_positive_float "-coupling_reduction_factor" $coupling_reduction_factor + } + set keep_coupling_caps [info exists flags(-keep_capacitive_coupling)] + set pin_cap_included [info exists flags(-pin_cap_included)] + + set reduce_to "none" + if [info exists keys(-reduce_to)] { + set reduce_to $keys(-reduce_to) + if { !($reduce_to == "pi_elmore" || $reduce_to == "pi_pole_residue2") } { + sta_error "-reduce_to must be pi_elmore or pi_pole_residue2." + } + } + set delete_after_reduce [info exists flags(-delete_after_reduce)] + set quiet [info exists flags(-quiet)] + set save [info exists flags(-save)] + set filename $args + return [read_spef_cmd $filename $instance $min_max $increment \ + $pin_cap_included $keep_coupling_caps $coupling_reduction_factor \ + $reduce_to $delete_after_reduce \ + $save $quiet] +} + +# set_pi_model [-min] [-max] drvr_pin c2 rpi c1 +proc set_pi_model { args } { + parse_key_args "set_pi_model" args keys {} flags {-max -min} + check_argc_eq4 "set_pi_model" $args + + set drvr_pin_name [lindex $args 0] + + set c2 [lindex $args 1] + check_positive_float "c2" $c2 + set c2 [capacitance_ui_sta $c2] + + set rpi [lindex $args 2] + check_positive_float "Rpi" $rpi + set rpi [resistance_ui_sta $rpi] + + set c1 [lindex $args 3] + check_positive_float "c1" $c1 + set c1 [capacitance_ui_sta $c1] + + set min_max [parse_min_max_all_check_flags flags] + + set drvr_pin [get_port_pin_error "drvr_pin" $drvr_pin_name] + set_pi_model_cmd $drvr_pin "rise" $min_max $c2 $rpi $c1 + set_pi_model_cmd $drvr_pin "fall" $min_max $c2 $rpi $c1 +} + +# set_elmore [-min] [-max] drvr_pin_name load_pin_name elmore +proc set_elmore { args } { + parse_key_args "set_elmore" args keys {} flags {-min -max} + check_argc_eq3 "set_elmore" $args + + set drvr_pin_arg [lindex $args 0] + set drvr_pin [get_port_pin_error "drvr_pin" $drvr_pin_arg] + set load_pin_arg [lindex $args 1] + set load_pin [get_port_pin_error "load_pin" $load_pin_arg] + set elmore [lindex $args 2] + check_positive_float "elmore delay" $elmore + set elmore [time_ui_sta $elmore] + set min_max [parse_min_max_all_check_flags flags] + + set_elmore_cmd $drvr_pin $load_pin "rise" $min_max $elmore + set_elmore_cmd $drvr_pin $load_pin "fall" $min_max $elmore +} + +# sta namespace end +} diff --git a/parasitics/ParasiticsClass.hh b/parasitics/ParasiticsClass.hh new file mode 100644 index 0000000..03503bf --- /dev/null +++ b/parasitics/ParasiticsClass.hh @@ -0,0 +1,31 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PARASITICS_CLASS_H +#define STA_PARASITICS_CLASS_H + +namespace sta { + +class Parasitics; +class Parasitic; +class ParasiticDevice; +class ParasiticNode; +class ParasiticAnalysisPt; + +enum class ReduceParasiticsTo { pi_elmore, pi_pole_residue2, none }; + +} // namespace +#endif diff --git a/parasitics/ReduceParasitics.cc b/parasitics/ReduceParasitics.cc new file mode 100644 index 0000000..9ae27e1 --- /dev/null +++ b/parasitics/ReduceParasitics.cc @@ -0,0 +1,729 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Error.hh" +#include "Debug.hh" +#include "MinMax.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "Corner.hh" +#include "Parasitics.hh" +#include "ReduceParasitics.hh" + +namespace sta { + +typedef Map ParasiticNodeValueMap; +typedef Map ParasiticDeviceValueMap; +typedef Set ParasiticNodeSet; +typedef Set ParasiticDeviceSet; + +class ReduceToPi : public StaState +{ +public: + ReduceToPi(StaState *sta); + void reduceToPi(const Pin *drvr_pin, + ParasiticNode *drvr_node, + bool includes_pin_caps, + float coupling_cap_factor, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap, + float &c2, + float &rpi, + float &c1); + bool pinCapsOneValue() { return pin_caps_one_value_; } + +protected: + void reducePiDfs(const Pin *drvr_pin, + ParasiticNode *node, + ParasiticDevice *from_res, + const ParasiticAnalysisPt *ap, + double &y1, + double &y2, + double &y3, + double &dwn_cap); + void visit(ParasiticNode *node); + bool isVisited(ParasiticNode *node); + void leave(ParasiticNode *node); + void setDownstreamCap(ParasiticNode *node, + float cap); + float downstreamCap(ParasiticNode *node); + float pinCapacitance(ParasiticNode *node); + bool isLoopResistor(ParasiticDevice *device); + void markLoopResistor(ParasiticDevice *device); + + bool includes_pin_caps_; + float coupling_cap_multiplier_; + const TransRiseFall *tr_; + const OperatingConditions *op_cond_; + const Corner *corner_; + const MinMax *cnst_min_max_; + ParasiticNodeSet visited_nodes_; + ParasiticNodeValueMap node_values_; + ParasiticDeviceSet loop_resistors_; + bool pin_caps_one_value_; +}; + +ReduceToPi::ReduceToPi(StaState *sta) : + StaState(sta), + coupling_cap_multiplier_(1.0), + tr_(nullptr), + op_cond_(nullptr), + corner_(nullptr), + cnst_min_max_(nullptr), + pin_caps_one_value_(true) +{ +} + +// "Modeling the Driving-Point Characteristic of Resistive +// Interconnect for Accurate Delay Estimation", Peter O'Brien and +// Thomas Savarino, Proceedings of the 1989 Design Automation +// Conference. +void +ReduceToPi::reduceToPi(const Pin *drvr_pin, + ParasiticNode *drvr_node, + bool includes_pin_caps, + float coupling_cap_factor, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap, + float &c2, + float &rpi, + float &c1) +{ + includes_pin_caps_ = includes_pin_caps; + coupling_cap_multiplier_ = coupling_cap_factor; + tr_ = tr; + op_cond_ = op_cond; + corner_ = corner; + cnst_min_max_ = cnst_min_max; + + double y1, y2, y3, dcap; + reducePiDfs(drvr_pin, drvr_node, 0, ap, y1, y2, y3, dcap); + + if (y2 == 0.0 && y3 == 0.0) { + // Capacitive load. + c1 = y1; + c2 = 0.0; + rpi = 0.0; + } + else { + c1 = y2 * y2 / y3; + c2 = y1 - y2 * y2 / y3; + rpi = -y3 * y3 / (y2 * y2 * y2); + } + debugPrint3(debug_, "parasitic_reduce", 2, + " Pi model c2=%.3g rpi=%.3g c1=%.3g\n", + c2, rpi, c1); +} + +// Find admittance moments. +void +ReduceToPi::reducePiDfs(const Pin *drvr_pin, + ParasiticNode *node, + ParasiticDevice *from_res, + const ParasiticAnalysisPt *ap, + double &y1, + double &y2, + double &y3, + double &dwn_cap) +{ + double coupling_cap = 0.0; + ParasiticDeviceIterator *device_iter1 = parasitics_->deviceIterator(node); + while (device_iter1->hasNext()) { + ParasiticDevice *device = device_iter1->next(); + if (parasitics_->isCouplingCap(device)) + coupling_cap += parasitics_->value(device, ap); + } + delete device_iter1; + + y1 = dwn_cap = parasitics_->nodeGndCap(node, ap) + + coupling_cap * coupling_cap_multiplier_ + + pinCapacitance(node); + y2 = y3 = 0.0; + + visit(node); + ParasiticDeviceIterator *device_iter2 = parasitics_->deviceIterator(node); + while (device_iter2->hasNext()) { + ParasiticDevice *device = device_iter2->next(); + if (parasitics_->isResistor(device) + && !isLoopResistor(device)) { + ParasiticNode *onode = parasitics_->otherNode(device, node); + // Cadence Fire&Ice likes to create resistors with identical + // from/to nodes. + if (onode != node + && device != from_res) { + if (isVisited(onode)) { + // Resistor loop. + debugPrint1(debug_, "parasitic_reduce", 2, + " loop detected thru resistor %s\n", + parasitics_->name(device)); + markLoopResistor(device); + } + else { + double yd1, yd2, yd3, dcap; + reducePiDfs(drvr_pin, onode, device, ap, yd1, yd2, yd3,dcap); + // Rule 3. Upstream traversal of a series resistor. + // Rule 4. Parallel admittances add. + double r = parasitics_->value(device, ap); + y1 += yd1; + y2 += yd2 - r * yd1 * yd1; + y3 += yd3 - 2 * r * yd1 * yd2 + r * r * yd1 * yd1 * yd1; + dwn_cap += dcap; + } + } + } + } + delete device_iter2; + + setDownstreamCap(node, dwn_cap); + leave(node); + debugPrint5(debug_, "parasitic_reduce", 3, + " node %s y1=%.3g y2=%.3g y3=%.3g cap=%.3g\n", + parasitics_->name(node), y1, y2, y3, dwn_cap); +} + +float +ReduceToPi::pinCapacitance(ParasiticNode *node) +{ + const Pin *pin = parasitics_->connectionPin(node); + float pin_cap = 0.0; + if (pin) { + Port *port = network_->port(pin); + LibertyPort *lib_port = network_->libertyPort(port); + if (lib_port) { + if (!includes_pin_caps_) { + pin_cap = sdc_->pinCapacitance(pin, tr_, op_cond_, corner_, + cnst_min_max_); + pin_caps_one_value_ &= lib_port->capacitanceIsOneValue(); + } + } + else if (network_->isTopLevelPort(pin)) + pin_cap = sdc_->portExtCap(port, tr_, cnst_min_max_); + } + return pin_cap; +} + +void +ReduceToPi::visit(ParasiticNode *node) +{ + visited_nodes_.insert(node); +} + +bool +ReduceToPi::isVisited(ParasiticNode *node) +{ + return visited_nodes_.hasKey(node); +} + +void +ReduceToPi::leave(ParasiticNode *node) +{ + visited_nodes_.erase(node); +} + +bool +ReduceToPi::isLoopResistor(ParasiticDevice *device) +{ + return loop_resistors_.hasKey(device); +} + +void +ReduceToPi::markLoopResistor(ParasiticDevice *device) +{ + loop_resistors_.insert(device); +} + +void +ReduceToPi::setDownstreamCap(ParasiticNode *node, + float cap) +{ + node_values_[node] = cap; +} + +float +ReduceToPi::downstreamCap(ParasiticNode *node) +{ + return node_values_[node]; +} + +//////////////////////////////////////////////////////////////// + +class ReduceToPiElmore : public ReduceToPi +{ +public: + ReduceToPiElmore(StaState *sta); + void makePiElmore(Parasitic *parasitic_network, + const Pin *drvr_pin, + ParasiticNode *drvr_node, + float coupling_cap_factor, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap); + void reduceElmoreDfs(const Pin *drvr_pin, + ParasiticNode *node, + ParasiticDevice *from_res, + double elmore, + Parasitic *pi_elmore, + const ParasiticAnalysisPt *ap); +}; + +void +reduceToPiElmore(Parasitic *parasitic_network, + const Pin *drvr_pin, + float coupling_cap_factor, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap, + StaState *sta) +{ + Parasitics *parasitics = sta->parasitics(); + ParasiticNode *drvr_node = parasitics->findNode(parasitic_network, + drvr_pin); + if (drvr_node) { + debugPrint1(sta->debug(), "parasitic_reduce", 1, + "Reduce driver %s\n", + sta->network()->pathName(drvr_pin)); + ReduceToPiElmore reducer(sta); + reducer.makePiElmore(parasitic_network, drvr_pin, drvr_node, + coupling_cap_factor, TransRiseFall::rise(), + op_cond, corner, cnst_min_max, ap); + if (!reducer.pinCapsOneValue()) + reducer.makePiElmore(parasitic_network, drvr_pin, drvr_node, + coupling_cap_factor, TransRiseFall::fall(), + op_cond, corner, cnst_min_max, ap); + } +} + +ReduceToPiElmore::ReduceToPiElmore(StaState *sta) : + ReduceToPi(sta) +{ +} + +void +ReduceToPiElmore::makePiElmore(Parasitic *parasitic_network, + const Pin *drvr_pin, + ParasiticNode *drvr_node, + float coupling_cap_factor, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap) +{ + float c2, rpi, c1; + reduceToPi(drvr_pin, drvr_node, + parasitics_->includesPinCaps(parasitic_network), + coupling_cap_factor, + tr, op_cond, corner, cnst_min_max, ap, + c2, rpi, c1); + Parasitic *pi_elmore = parasitics_->makePiElmore(drvr_pin, tr, ap, + c2, rpi, c1); + parasitics_->setIsReducedParasiticNetwork(pi_elmore, true); + reduceElmoreDfs(drvr_pin, drvr_node, 0, 0.0, pi_elmore, ap); +} + +// Find elmore delays on 2nd DFS search using downstream capacitances +// set by reducePiDfs. +void +ReduceToPiElmore::reduceElmoreDfs(const Pin *drvr_pin, + ParasiticNode *node, + ParasiticDevice *from_res, + double elmore, + Parasitic *pi_elmore, + const ParasiticAnalysisPt *ap) +{ + const Pin *pin = parasitics_->connectionPin(node); + if (from_res && pin) { + if (network_->isLoad(pin)) { + debugPrint2(debug_, "parasitic_reduce", 2, + " Load %s elmore=%.3g\n", + network_->pathName(pin), + elmore); + parasitics_->setElmore(pi_elmore, pin, elmore); + } + } + visit(node); + ParasiticDeviceIterator *device_iter = parasitics_->deviceIterator(node); + while (device_iter->hasNext()) { + ParasiticDevice *device = device_iter->next(); + if (parasitics_->isResistor(device)) { + ParasiticNode *onode = parasitics_->otherNode(device, node); + if (device != from_res + && !isVisited(onode) + && !isLoopResistor(device)) { + float r = parasitics_->value(device, ap); + double onode_elmore = elmore + r * downstreamCap(onode); + reduceElmoreDfs(drvr_pin, onode, device, onode_elmore, + pi_elmore, ap); + } + } + } + delete device_iter; + leave(node); +} + +//////////////////////////////////////////////////////////////// + +class ReduceToPiPoleResidue2 : public ReduceToPi +{ +public: + ReduceToPiPoleResidue2(StaState *sta); + ~ReduceToPiPoleResidue2(); + void findPolesResidues(Parasitic *parasitic_network, + Parasitic *pi_pole_residue, + const Pin *drvr_pin, + ParasiticNode *drvr_node, + const ParasiticAnalysisPt *ap); + void makePiPoleResidue2(Parasitic *parasitic_network, + const Pin *drvr_pin, + ParasiticNode *drvr_node, + float coupling_cap_factor, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap); + +private: + void findMoments(const Pin *drvr_pin, + ParasiticNode *drvr_node, + int moment_count, + const ParasiticAnalysisPt *ap); + void findMoments(const Pin *drvr_pin, + ParasiticNode *node, + double from_volt, + ParasiticDevice *from_res, + int moment_index, + const ParasiticAnalysisPt *ap); + double findBranchCurrents(const Pin *drvr_pin, + ParasiticNode *node, + ParasiticDevice *from_res, + int moment_index, + const ParasiticAnalysisPt *ap); + double moment(ParasiticNode *node, + int moment_index); + void setMoment(ParasiticNode *node, + double moment, + int moment_index); + double current(ParasiticDevice *res); + void setCurrent(ParasiticDevice *res, + double i); + void findPolesResidues(Parasitic *pi_pole_residue, + const Pin *drvr_pin, + const Pin *load_pin, + ParasiticNode *load_node); + + // Resistor/capacitor currents. + ParasiticDeviceValueMap currents_; + ParasiticNodeValueMap *moments_; +}; + +ReduceToPiPoleResidue2::ReduceToPiPoleResidue2(StaState *sta) : + ReduceToPi(sta), + moments_(nullptr) +{ +} + +// The interconnect moments are found using RICE. +// "RICE: Rapid Interconnect Circuit Evaluation Using AWE", +// Curtis Ratzlaff and Lawrence Pillage, IEEE Transactions on +// Computer-Aided Design of Integrated Circuits and Systems, +// Vol 13, No 6, June 1994, pg 763-776. +// +// The poles and residues are found using these algorithms. +// "An Explicit RC-Circuit Delay Approximation Based on the First +// Three Moments of the Impulse Response", Proceedings of the 33rd +// Design Automation Conference, 1996, pg 611-616. +void +reduceToPiPoleResidue2(Parasitic *parasitic_network, + const Pin *drvr_pin, + float coupling_cap_factor, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap, + StaState *sta) +{ + Parasitics *parasitics = sta->parasitics(); + ParasiticNode *drvr_node = parasitics->findNode(parasitic_network, drvr_pin); + if (drvr_node) { + debugPrint1(sta->debug(), "parasitic_reduce", 1, + "Reduce driver %s\n", + sta->network()->pathName(drvr_pin)); + ReduceToPiPoleResidue2 reducer(sta); + reducer.makePiPoleResidue2(parasitic_network, drvr_pin, drvr_node, + coupling_cap_factor, TransRiseFall::rise(), + op_cond, corner, cnst_min_max, ap); + if (!reducer.pinCapsOneValue()) + reducer.makePiPoleResidue2(parasitic_network, drvr_pin, drvr_node, + coupling_cap_factor, TransRiseFall::fall(), + op_cond, corner, cnst_min_max, ap); + } +} + +void +ReduceToPiPoleResidue2::makePiPoleResidue2(Parasitic *parasitic_network, + const Pin *drvr_pin, + ParasiticNode *drvr_node, + float coupling_cap_factor, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap) +{ + float c2, rpi, c1; + reduceToPi(drvr_pin, drvr_node, + parasitics_->includesPinCaps(parasitic_network), + coupling_cap_factor, + tr, op_cond, corner, cnst_min_max, ap, + c2, rpi, c1); + Parasitic *pi_pole_residue = parasitics_->makePiPoleResidue(drvr_pin, + tr, ap, + c2, rpi, c1); + parasitics_->setIsReducedParasiticNetwork(pi_pole_residue, true); + findPolesResidues(parasitic_network, pi_pole_residue, + drvr_pin, drvr_node, ap); +} + +ReduceToPiPoleResidue2::~ReduceToPiPoleResidue2() +{ + delete [] moments_; +} + +void +ReduceToPiPoleResidue2::findPolesResidues(Parasitic *parasitic_network, + Parasitic *pi_pole_residue, + const Pin *drvr_pin, + ParasiticNode *drvr_node, + const ParasiticAnalysisPt *ap) +{ + moments_ = new ParasiticNodeValueMap[4]; + findMoments(drvr_pin, drvr_node, 4, ap); + + PinConnectedPinIterator *pin_iter = network_->connectedPinIterator(drvr_pin); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (network_->isLoad(pin)) { + ParasiticNode *load_node = parasitics_->findNode(parasitic_network, pin); + if (load_node) { + findPolesResidues(pi_pole_residue, drvr_pin, pin, load_node); + } + } + } + delete pin_iter; +} + +void +ReduceToPiPoleResidue2::findMoments(const Pin *drvr_pin, + ParasiticNode *drvr_node, + int moment_count, + const ParasiticAnalysisPt *ap) +{ + // Driver model thevenin resistance. + double rd = 0.0; + // Zero'th moments are all 1 because Vin(0)=1 and there is no + // 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, ap); + double rd_volt = rd_i * rd; + setMoment(drvr_node, 0.0, moment_index); + findMoments(drvr_pin, drvr_node, -rd_volt, 0, moment_index, ap); + } +} + +double +ReduceToPiPoleResidue2::findBranchCurrents(const Pin *drvr_pin, + ParasiticNode *node, + ParasiticDevice *from_res, + int moment_index, + const ParasiticAnalysisPt *ap) +{ + visit(node); + double branch_i = 0.0; + double coupling_cap = 0.0; + ParasiticDeviceIterator *device_iter = parasitics_->deviceIterator(node); + while (device_iter->hasNext()) { + ParasiticDevice *device = device_iter->next(); + if (parasitics_->isResistor(device)) { + ParasiticNode *onode = parasitics_->otherNode(device, node); + // Cadence Fire&Ice likes to create resistors with identical + // from/to nodes. + if (onode != node + && device != from_res + && !isVisited(onode) + && !isLoopResistor(device)) { + branch_i += findBranchCurrents(drvr_pin, onode, device, + moment_index, ap); + } + } + else if (parasitics_->isCouplingCap(device)) + coupling_cap += parasitics_->value(device, ap); + } + delete device_iter; + double cap = parasitics_->nodeGndCap(node, ap) + + coupling_cap * coupling_cap_multiplier_ + + pinCapacitance(node); + branch_i += cap * moment(node, moment_index - 1); + leave(node); + if (from_res) { + setCurrent(from_res, branch_i); + debugPrint1(debug_, "parasitic_reduce", 3, + " res i=%.3g\n", branch_i); + } + return branch_i; +} + +void +ReduceToPiPoleResidue2::findMoments(const Pin *drvr_pin, + ParasiticNode *node, + double from_volt, + ParasiticDevice *from_res, + int moment_index, + const ParasiticAnalysisPt *ap) +{ + visit(node); + ParasiticDeviceIterator *device_iter = parasitics_->deviceIterator(node); + while (device_iter->hasNext()) { + ParasiticDevice *device = device_iter->next(); + if (parasitics_->isResistor(device)) { + ParasiticNode *onode = parasitics_->otherNode(device, node); + // Cadence Fire&Ice likes to create resistors with identical + // from/to nodes. + if (onode != node + && device != from_res + && !isVisited(onode) + && !isLoopResistor(device)) { + double r = parasitics_->value(device, ap); + double r_volt = r * current(device); + double onode_volt = from_volt - r_volt; + setMoment(onode, onode_volt, moment_index); + debugPrint3(debug_, "parasitic_reduce", 3, + " moment %s %d %.3g\n", + parasitics_->name(onode), + moment_index, + onode_volt); + findMoments(drvr_pin, onode, onode_volt, device, moment_index, ap); + } + } + } + delete device_iter; + leave(node); +} + +double +ReduceToPiPoleResidue2::moment(ParasiticNode *node, + int moment_index) +{ + // Zero'th moments are all 1. + if (moment_index == 0) + return 1.0; + else { + ParasiticNodeValueMap &map = moments_[moment_index]; + return map[node]; + } +} + +void +ReduceToPiPoleResidue2::setMoment(ParasiticNode *node, + double moment, + int moment_index) +{ + // Zero'th moments are all 1. + if (moment_index > 0) { + ParasiticNodeValueMap &map = moments_[moment_index]; + map[node] = moment; + } +} + +double +ReduceToPiPoleResidue2::current(ParasiticDevice *res) +{ + return currents_[res]; +} + +void +ReduceToPiPoleResidue2::setCurrent(ParasiticDevice *res, + double i) +{ + currents_[res] = i; +} + +void +ReduceToPiPoleResidue2::findPolesResidues(Parasitic *pi_pole_residue, + const Pin *, + const Pin *load_pin, + ParasiticNode *load_node) +{ + double m1 = moment(load_node, 1); + double m2 = moment(load_node, 2); + double m3 = moment(load_node, 3); + double p1 = -m2 / m3; + double p2 = p1 * (1.0 / m1 - m1 / m2) / (m1 / m2 - m2 / m3); + if (p1 <= 0.0 + || p2 <= 0.0 + // Coincident poles. Not handled by delay calculator. + || p1 == p2 + || m1 / m2 == m2 / m3) { + double p1 = -1.0 / m1; + double k1 = 1.0; + debugPrint3(debug_, "parasitic_reduce", 3, + " load %s p1=%.3g k1=%.3g\n", + network_->pathName(load_pin), p1, k1); + ComplexFloatSeq *poles = new ComplexFloatSeq(1); + ComplexFloatSeq *residues = new ComplexFloatSeq(1); + (*poles)[0] = ComplexFloat(p1, 0.0); + (*residues)[0] = ComplexFloat(k1, 0.0); + parasitics_->setPoleResidue(pi_pole_residue, load_pin, poles, residues); + } + else { + double k1 = p1 * p1 * (1.0 + m1 * p2) / (p1 - p2); + double k2 = -p2 * p2 * (1.0 + m1 * p1) / (p1 - p2); + if (k1 < 0.0 && k2 > 0.0) { + // Swap p1 and p2. + double p = p2, k = k2; + p2 = p1; + k2 = k1; + p1 = p; + k1 = k; + } + debugPrint5(debug_, "parasitic_reduce", 3, + " load %s p1=%.3g p2=%.3g k1=%.3g k2=%.3g\n", + network_->pathName(load_pin), p1, p2, k1, k2); + + ComplexFloatSeq *poles = new ComplexFloatSeq(2); + ComplexFloatSeq *residues = new ComplexFloatSeq(2); + (*poles)[0] = ComplexFloat(p1, 0.0); + (*residues)[0] = ComplexFloat(k1, 0.0); + (*poles)[1] = ComplexFloat(p2, 0.0); + (*residues)[1] = ComplexFloat(k2, 0.0); + parasitics_->setPoleResidue(pi_pole_residue, load_pin, poles, residues); + } +} + +} // namespace diff --git a/parasitics/ReduceParasitics.hh b/parasitics/ReduceParasitics.hh new file mode 100644 index 0000000..dd08895 --- /dev/null +++ b/parasitics/ReduceParasitics.hh @@ -0,0 +1,50 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_REDUCE_PARASITICS_H +#define STA_REDUCE_PARASITICS_H + +namespace sta { + +class Parasitic; +class ParasiticAnalysisPt; +class StaState; + +// Reduce parasitic network to pi elmore model for drvr_pin. +void +reduceToPiElmore(Parasitic *parasitic_network, + const Pin *drvr_pin, + float coupling_cap_factor, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap, + StaState *sta); + +// Reduce parasitic network to pi and 2nd order pole/residue models +// for drvr_pin. +void +reduceToPiPoleResidue2(Parasitic *parasitic_network, + const Pin *drvr_pin, + float coupling_cap_factor, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap, + StaState *sta); + +} // namespace +#endif diff --git a/parasitics/SpefLex.ll b/parasitics/SpefLex.ll new file mode 100644 index 0000000..8daebbe --- /dev/null +++ b/parasitics/SpefLex.ll @@ -0,0 +1,216 @@ +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include "Machine.hh" +#include "StringUtil.hh" +#include "SpefReaderPvt.hh" +#include "SpefParse.hh" + +#define YY_NO_INPUT + +static std::string spef_token; + +void +spefFlushBuffer() +{ + YY_FLUSH_BUFFER; +} + +// Reset the start condition to INITIAL. +void +spefResetScanner() +{ + BEGIN(0); +} + +%} + +/* %option debug */ +%option noyywrap +%option nounput +%option never-interactive + +%x COMMENT +%x QUOTE + +ALPHA [A-Za-z] +DIGIT [0-9] +BLANK [ \t\r] +POS_SIGN "+" +NEG_SIGN "-" +POS_INTEGER {DIGIT}+ +SIGN {POS_SIGN}|{NEG_SIGN} +INTEGER {SIGN}?{DIGIT}+ +DECIMAL {SIGN}?{DIGIT}+"."{DIGIT}* +FRACTION {SIGN}?"."{DIGIT}+ +RADIX {DECIMAL}|{FRACTION}|{INTEGER} +EXP {RADIX}[eE]{INTEGER} +FLOAT {DECIMAL}|{FRACTION}|{EXP} + +HCHAR "."|"/"|"|"|":" +PREFIX_BUS_DELIM "["|"{"|"("|"<" +SUFFIX_BUS_DELIM "]"|"}"|")"|">" +SPECIAL_CHAR "!"|"#"|"$"|"%"|"&"|"`"|"("|")"|"*"|"+"|","|"-"|"."|"/"|":"|";"|"<"|"="|">"|"?"|"@"|"["|"\\"|"]"|"^"|"'"|"{"|"|"|"}"|"~" +ESCAPED_CHAR_SET {SPECIAL_CHAR}|\" +ESCAPED_CHAR \\{ESCAPED_CHAR_SET} +IDENT_ACHAR {ESCAPED_CHAR}|{ALPHA}|"_" +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})+ +ID_OR_BIT {ID}|{BIT_IDENT} +IDENT {INTEGER}*{ID_OR_BIT}({HCHAR}|{INTEGER}|{ID_OR_BIT})* +IDENT_OR_BIT {ID}|{BIT_IDENT} +PATH {IDENT_OR_BIT}({HCHAR}{IDENT_OR_BIT})* +NAME_PAIR ({PATH}|{INDEX}){HCHAR}({INDEX}|{IDENT_OR_BIT}|{POS_INTEGER}) +INDEX "*"{POS_INTEGER} + +%% + +"*BUS_DELIMITER" { return BUS_DELIMITER; } +"*C2_R1_C1" { return C2_R1_C1; } +"*C" { return KW_C; } +"*CAP" { return CAP; } +"*CELL" { return CELL; } +"*CONN" { return CONN; } +"*C_UNIT" { return C_UNIT; } +"*SPEF" { return SPEF; } +"*DATE" { return DATE; } +"*DEFINE" { return DEFINE; } +"*DELIMITER" { return DELIMITER; } +"*DESIGN" { return DESIGN; } +"*DESIGN_FLOW" { return DESIGN_FLOW; } +"*DIVIDER" { return DIVIDER; } +"*DRIVER" { return DRIVER; } +"*D_NET" { return D_NET; } +"*D_PNET" { return D_PNET; } +"*D" { return KW_D; } +"*END" { return END; } +"*GROUND_NETS" { return GROUND_NETS; } +"*INDUC" { return INDUC; } +"*I" { return KW_I; } +"*K" { return KW_K; } +"*L" { return KW_L; } +"*LOADS" { return LOADS; } +"*L_UNIT" { return L_UNIT; } +"*NAME_MAP" { return NAME_MAP; } +"*N" { return KW_N; } +"*PDEFINE" { return PDEFINE; } +"*PHYSICAL_PORTS" { return PHYSICAL_PORTS; } +"*PORTS" { return PORTS; } +"*POWER_NETS" { return POWER_NETS; } +"*PROGRAM" { return PROGRAM; } +"*P" { return KW_P; } +"*Q" { return KW_Q; } +"*RC" { return RC; } +"*RES" { return RES; } +"*R_NET" { return R_NET; } +"*R_PNET" { return R_PNET; } +"*R_UNIT" { return R_UNIT; } +"*S" { return KW_S; } +"*T_UNIT" { return T_UNIT; } +"*VENDOR" { return VENDOR; } +"*VERSION" { return PVERSION; } +"*V" { return KW_V; } + +"//".*\n { /* Single line comment */ + sta::spef_reader->incrLine(); + } + +"/*" { BEGIN COMMENT; } +{ + +. + +\n { sta::spef_reader->incrLine(); } + +"*/" { BEGIN INITIAL; } + +<> { + SpefParse_error("unterminated comment"); + BEGIN(INITIAL); + yyterminate(); + } +} + +"\"" { BEGIN QUOTE; spef_token.erase(); } +{ + +\r?\n { + sta::spef_reader->incrLine(); + } + +"\\". { spef_token += yytext[1]; } + +"\"" { + BEGIN INITIAL; + SpefParse_lval.string = sta::stringCopy(spef_token.c_str()); + return QSTRING; + } + +. { spef_token += yytext[0]; } + +<> { + SpefParse_error("unterminated quoted string"); + BEGIN(INITIAL); + yyterminate(); + } +} + +{BLANK}*\n { + sta::spef_reader->incrLine(); + } + +{INTEGER} { + SpefParse_lval.integer = atoi(yytext); + return INTEGER; + } + +{FLOAT} { + SpefParse_lval.number = static_cast(atof(yytext)); + return FLOAT; + } + +{IDENT} { + SpefParse_lval.string = sta::spef_reader->translated(yytext); + return IDENT; + } + +{PATH}|{NAME_PAIR} { + SpefParse_lval.string = sta::spef_reader->translated(yytext); + return NAME; + } + +{INDEX} { + SpefParse_lval.string = sta::stringCopy(yytext); + return INDEX; + } + +{HCHAR} { + char ch = yytext[0]; + return ((int) ch); + } + +{BLANK} /* ignore blanks */ ; + + /* Send out of bound characters to parser. */ +. { return ((int) yytext[0]); } + +%% diff --git a/parasitics/SpefNamespace.cc b/parasitics/SpefNamespace.cc new file mode 100644 index 0000000..04b18b8 --- /dev/null +++ b/parasitics/SpefNamespace.cc @@ -0,0 +1,109 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include "Machine.hh" + +namespace sta { + +char * +spefToSta(const char *token, char spef_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; + if (ch == spef_escape) { + char next_ch = s[1]; + if (next_ch == spef_divider) { + // Translate spef escape to network escape. + *t++ = path_escape; + // Translate spef divider to network divider. + *t++ = path_divider; + } + else if (next_ch == '[' + || next_ch == ']' + || next_ch == spef_escape) { + // Translate spef escape to network escape. + *t++ = path_escape; + *t++ = next_ch; + } + else + // No need to escape other characters. + *t++ = next_ch; + s++; + } + else if (ch == spef_divider) + // Translate spef divider to network divider. + *t++ = path_divider; + else + // Just the normal noises. + *t++ = ch; + } + *t++ = '\0'; + return trans_token; +} + +char * +staToSpef(const char *token, char spef_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; + if (ch == path_escape) { + char next_ch = s[1]; + if (next_ch == path_divider) { + // Translate network escape to spef escape. + *t++ = spef_escape; + // Translate network divider to spef divider. + *t++ = spef_divider; + } + else if (next_ch == '[' + || next_ch == ']') { + // Translate network escape to spef escape. + *t++ = spef_escape; + *t++ = next_ch; + } + else + // No need to escape other characters. + *t++ = next_ch; + s++; + } + else if (ch == path_divider) + // Translate network divider to spef divider. + *t++ = spef_divider; + else if (!(isdigit(ch) || isalpha(ch) || ch == '_')) { + // Escape non-alphanum characters. + *t++ = spef_escape; + *t++ = ch; + } + else + // Just the normal noises. + *t++ = ch; + } + *t++ = '\0'; + return trans_token; +} + +} // namespace diff --git a/parasitics/SpefNamespace.hh b/parasitics/SpefNamespace.hh new file mode 100644 index 0000000..2101175 --- /dev/null +++ b/parasitics/SpefNamespace.hh @@ -0,0 +1,35 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SPEF_NAMESPACE_H +#define STA_SPEF_NAMESPACE_H + +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); + + +} // namespace +#endif diff --git a/parasitics/SpefParse.yy b/parasitics/SpefParse.yy new file mode 100755 index 0000000..100796a --- /dev/null +++ b/parasitics/SpefParse.yy @@ -0,0 +1,825 @@ +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "StringUtil.hh" +#include "StringSeq.hh" +#include "SpefReaderPvt.hh" + +int SpefLex_lex(); +#define SpefParse_lex SpefLex_lex +// use yacc generated parser errors +#define YYERROR_VERBOSE + +%} + +%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 SPEF DESIGN DATE VENDOR PROGRAM DESIGN_FLOW +%token PVERSION DIVIDER DELIMITER +%token BUS_DELIMITER T_UNIT C_UNIT R_UNIT L_UNIT NAME_MAP +%token POWER_NETS GROUND_NETS KW_C KW_L KW_S KW_D KW_V +%token PORTS PHYSICAL_PORTS DEFINE PDEFINE +%token D_NET D_PNET R_NET R_PNET END +%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 INTEGER +%type FLOAT +%type 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 number pos_number threshold +%type real_component imaginary_component pole poles +%type residue residues complex_par_value cnumber + +%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 qstrings +%type direction + +%type par_value total_cap + +%type pi_model + +%type pin_name driver_pair internal_connection + +%type net + +%start file + +%{ +%} + +%% + +file: + header_def + name_map + power_def + external_def + define_def + internal_def +; + +/****************************************************************/ + +prefix_bus_delim: + '[' + { $$ = '['; } +| '{' + { $$ = '}'; } +| '(' + { $$ = ')'; } +| '<' + { $$ = '<'; } +; + +suffix_bus_delim: + ']' + { $$ = ']'; } +| '}' + { $$ = '}'; } +| ')' + { $$ = ')'; } +| '>' + { $$ = '>'; } +; + +hchar: + '.' + { $$ = '.'; } +| '/' + { $$ = '/'; } +| '|' + { $$ = '|'; } +| ':' + { $$ = ':'; } +; + +/****************************************************************/ + +header_def: + spef_version + design_name + date + vendor + program_name + program_version + design_flow + hierarchy_div_def + pin_delim_def + bus_delim_def + unit_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: + DESIGN_FLOW qstrings + { sta::spef_reader->setDesignFlow($2); } +; + +qstrings: + QSTRING + { $$ = new sta::StringSeq; + $$->push_back($1); + } +| qstrings QSTRING + { $$->push_back($2); } +; + +hierarchy_div_def: + DIVIDER hchar + { sta::spef_reader->setDivider($2); } +; + +pin_delim_def: + DELIMITER hchar + { sta::spef_reader->setDelimiter($2); } +; + +bus_delim_def: + BUS_DELIMITER prefix_bus_delim + { sta::spef_reader->setBusBrackets($2, '\0'); } +| BUS_DELIMITER prefix_bus_delim suffix_bus_delim + { sta::spef_reader->setBusBrackets($2, $3); } +; + +/****************************************************************/ + +unit_def: + time_scale + cap_scale + res_scale + induc_scale +; + +time_scale: + T_UNIT pos_number IDENT + { sta::spef_reader->setTimeScale($2, $3); } +; + +cap_scale: + C_UNIT pos_number IDENT + { sta::spef_reader->setCapScale($2, $3); } +; + +res_scale: + R_UNIT pos_number IDENT + { sta::spef_reader->setResScale($2, $3); } +; + +induc_scale: + L_UNIT pos_number IDENT + { sta::spef_reader->setInductScale($2, $3); } +; + +/****************************************************************/ + +name_map: + /* empty */ +| NAME_MAP name_map_entries +; + +name_map_entries: + name_map_entry +| name_map_entries name_map_entry +; + +name_map_entry: + INDEX mapped_item + { sta::spef_reader->makeNameMapEntry($1, $2); + sta::stringDelete($1); + } +; + +mapped_item: + IDENT +| NAME +| QSTRING +; + +/****************************************************************/ + +power_def: + /* empty */ +| power_net_def +| ground_net_def +| power_net_def ground_net_def +; + +power_net_def: + POWER_NETS net_names +; + +ground_net_def: + GROUND_NETS net_names +; + +net_names: + net_name +| net_names net_name +; + +net_name: + name_or_index + { sta::stringDelete($1); } +; + +/****************************************************************/ + +external_def: + /* empty */ +| port_def +| physical_port_def +| port_def physical_port_def +; + +port_def: + PORTS port_entries +; + +port_entries: + port_entry +| port_entries port_entry +; + +port_entry: + port_name direction conn_attrs + { sta::stringDelete($1); } +; + +direction: + IDENT + { $$ = sta::spef_reader->portDirection($1); + sta::stringDelete($1); + } +; + +port_name: + name_or_index +; + +inst_name: + name_or_index +; + +physical_port_def: + PHYSICAL_PORTS pport_entries +; + +pport_entries: + pport_entry +| pport_entries pport_entry +; + +pport_entry: + pport_name IDENT conn_attrs +; + +pport_name: + name_or_index + { sta::stringDelete($1); } +| physical_inst ':' pport + { sta::stringDelete($1); + sta::stringDelete($3); + } +; + +pport: + name_or_index +; + +physical_inst: + name_or_index +; + +/****************************************************************/ + +conn_attrs: + /* empty */ +| conn_attrs conn_attr +; + +conn_attr: + coordinates +| cap_load +| slews +| driving_cell +; + +coordinates: + KW_C number number +; + +cap_load: + KW_L par_value + { delete $2; } +; + +par_value: + number + { $$ = new sta::SpefTriple($1); } +| number ':' number ':' number + { $$ = new sta::SpefTriple($1, $3, $5); } +; + +slews: + KW_S par_value par_value + { delete $2; + delete $3; + } +| KW_S par_value par_value threshold threshold + { delete $2; + delete $3; + } +; + +threshold: + pos_number +| pos_number ':' pos_number ':' pos_number +; + +driving_cell: + KW_D cell_type + { sta::stringDelete($2); } +; + +cell_type: + IDENT +| INDEX +; + +/****************************************************************/ + +define_def: + /* empty */ +| define_def define_entry +; + +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: + QSTRING +; + +/****************************************************************/ + +internal_def: + nets +| internal_def nets +; + +nets: + d_net +| r_net +| d_pnet +| r_pnet +; + +/****************************************************************/ + +d_net: + D_NET net total_cap + { sta::spef_reader->dspfBegin($2, $3); } + routing_conf conn_sec cap_sec res_sec induc_sec END + { sta::spef_reader->dspfFinish(); } +; + +net: + name_or_index + { $$ = sta::spef_reader->findNet($1); + sta::stringDelete($1); + } +; + +total_cap: + par_value +; + +routing_conf: + /* empty */ +| KW_V conf +; + +conf: + pos_integer +; + +/****************************************************************/ + +conn_sec: + /* empty */ +| CONN conn_defs internal_node_coords +; + +conn_defs: + conn_def +| conn_defs conn_def +; + +conn_def: + KW_P external_connection direction conn_attrs +| KW_I internal_connection direction conn_attrs +; + +external_connection: + name_or_index + { sta::stringDelete($1); } +| physical_inst ':' pport + { sta::stringDelete($1); + sta::stringDelete($3); + } +; + +internal_connection: + pin_name +; + +pin_name: + name_or_index + { $$ = sta::spef_reader->findPin($1); + sta::stringDelete($1); + } +; + +internal_node_coords: + /* empty */ +| internal_node_coords internal_node_coord +; + +internal_node_coord: + KW_N internal_parasitic_node coordinates +; + +internal_parasitic_node: + name_or_index + { sta::stringDelete($1); } +; + +/****************************************************************/ + +cap_sec: + /* empty */ +| CAP cap_elems +; + +cap_elems: + /* empty */ + { $$ = 0; } +| cap_elems cap_elem +; + +cap_elem: + cap_id parasitic_node par_value + { sta::spef_reader->makeCapacitor($1, $2, $3); } +| cap_id parasitic_node parasitic_node par_value + { sta::spef_reader->makeCapacitor($1, $2, $3, $4); } +; + +cap_id: + pos_integer +; + +parasitic_node: + name_or_index +; + +/****************************************************************/ + +res_sec: + /* empty */ +| RES res_elems +; + +res_elems: + /* empty */ + { $$ = 0; } +| res_elems res_elem +; + +res_elem: + res_id parasitic_node parasitic_node par_value + { sta::spef_reader->makeResistor($1, $2, $3, $4); } +; + +res_id: + pos_integer +; + +/****************************************************************/ + +induc_sec: + /* empty */ +| INDUC induc_elems +; + +induc_elems: + /* empty */ + { $$ = 0; } +| induc_elems induc_elem +; + +induc_elem: + induc_id parasitic_node parasitic_node par_value + { delete $4; } +; + +induc_id: + pos_integer +; + +/****************************************************************/ + +r_net: + R_NET net total_cap + { sta::spef_reader->rspfBegin($2, $3); } + routing_conf driver_reducs END + { sta::spef_reader->rspfFinish(); } +; + +driver_reducs: + /* empty */ +| driver_reducs driver_reduc +; + +driver_reduc: + driver_pair driver_cell pi_model + { sta::spef_reader->rspfDrvrBegin($1, $3); + sta::stringDelete($2); + } + load_desc + { sta::spef_reader->rspfDrvrFinish(); } +; + +driver_pair: + DRIVER pin_name + { $$ = $2; } +; + +driver_cell: + CELL cell_type + { $$ = $2; } +; + +pi_model: + C2_R1_C1 par_value par_value par_value + { $$ = new sta::SpefRspfPi($2, $3, $4); } +; + +/****************************************************************/ + +load_desc: + LOADS rc_descs +; + +rc_descs: + rc_desc +| rc_descs rc_desc +; + +rc_desc: + RC pin_name par_value + { sta::spef_reader->rspfLoad($2, $3); } +| RC pin_name par_value pole_residue_desc + { sta::spef_reader->rspfLoad($2, $3); } +; + +pole_residue_desc: + pole_desc residue_desc +; + +pole_desc: + KW_Q pos_integer poles +; + +poles: + pole +| poles pole +; + +pole: + complex_par_value +; + +complex_par_value: + cnumber +| number +| cnumber ':' cnumber ':' cnumber +| number ':' number ':' number +; + +cnumber: + '(' real_component imaginary_component ')' + { $$ = $2; } +; + +real_component: + number +; + +imaginary_component: + number +; + +residue_desc: + KW_K pos_integer residues +; + +residues: + residue +| residues residue +; + +residue: + complex_par_value +; + +/****************************************************************/ + +d_pnet: + D_PNET pnet_ref total_cap routing_conf pconn_sec cap_sec res_sec + induc_sec END + { sta::stringDelete($2); } +; + +pnet_ref: + name_or_index +; + +pconn_sec: + CONN pconn_defs internal_pnode_coords +; + +pconn_defs: + pconn_def +| pconn_defs pconn_def +; + +pconn_def: + KW_P pexternal_connection direction conn_attr +| KW_I internal_connection direction conn_attr +; + +pexternal_connection: + pport_name +; + +internal_pnode_coords: + /* empty */ +| internal_pnode_coords internal_pnode_coord +; + +internal_pnode_coord: + KW_N internal_pdspf_node coordinates +; + +internal_pdspf_node: + name_or_index + { + sta::stringDelete($1); + $$ = 0; + } +; + +name_or_index: + IDENT +| NAME +| 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: + pdriver_pair driver_cell pi_model load_desc +; + +pdriver_pair: + DRIVER internal_connection +; + +/****************************************************************/ + +number: + INTEGER + { $$ = static_cast($1); } +| FLOAT +; + +pos_integer: + INTEGER + { int value = $1; + if (value < 0) + sta::spef_reader->warn("%d is not positive.\n", value); + $$ = value; + } +; + +pos_number: + INTEGER + { float value = static_cast($1); + if (value < 0) + sta::spef_reader->warn("%.4f is not positive.\n", value); + $$ = value; + } +| FLOAT + { float value = static_cast($1); + if (value < 0) + sta::spef_reader->warn("%.4f is not positive.\n", value); + $$ = value; + } +; + +%% diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc new file mode 100644 index 0000000..e992502 --- /dev/null +++ b/parasitics/SpefReader.cc @@ -0,0 +1,728 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "Zlib.hh" +#include "Report.hh" +#include "Debug.hh" +#include "StringUtil.hh" +#include "Map.hh" +#include "PortDirection.hh" +#include "Transition.hh" +#include "Network.hh" +#include "Liberty.hh" +#include "Sdc.hh" +#include "Parasitics.hh" +#include "SpefReaderPvt.hh" +#include "SpefNamespace.hh" +#include "SpefReader.hh" + +int +SpefParse_parse(); +void +spefResetScanner(); + +namespace sta { + +SpefReader *spef_reader; + +bool +readSpefFile(const char *filename, + Instance *instance, + ParasiticAnalysisPt *ap, + bool increment, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + ReduceParasiticsTo reduce_to, + bool delete_after_reduce, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + bool save, + bool quiet, + Report *report, + Network *network, + Parasitics *parasitics) +{ + bool success = false; + // Use zlib to uncompress gzip'd files automagically. + gzFile stream = gzopen(filename, "rb"); + if (stream) { + SpefReader reader(filename, stream, instance, ap, increment, + pin_cap_included, keep_coupling_caps, coupling_cap_factor, + reduce_to, delete_after_reduce, op_cond, corner, + cnst_min_max, quiet, report, network, parasitics); + spef_reader = &reader; + ::spefResetScanner(); + // yyparse returns 0 on success. + success = (::SpefParse_parse() == 0); + gzclose(stream); + } + else + throw FileNotReadable(filename); + if (success && save) + parasitics->save(); + return success; +} + +SpefReader::SpefReader(const char *filename, + gzFile stream, + Instance *instance, + ParasiticAnalysisPt *ap, + bool increment, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + ReduceParasiticsTo reduce_to, + bool delete_after_reduce, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + bool quiet, + Report *report, + Network *network, + Parasitics *parasitics) : + filename_(filename), + instance_(instance), + ap_(ap), + increment_(increment), + pin_cap_included_(pin_cap_included), + keep_coupling_caps_(keep_coupling_caps), + reduce_to_(reduce_to), + delete_after_reduce_(delete_after_reduce), + op_cond_(op_cond), + corner_(corner), + cnst_min_max_(cnst_min_max), + keep_device_names_(false), + quiet_(quiet), + stream_(stream), + line_(1), + // defaults + divider_('\0'), + delimiter_('\0'), + bus_brkt_left_('\0'), + bus_brkt_right_('\0'), + net_(nullptr), + report_(report), + network_(network), + parasitics_(parasitics), + triple_index_(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; + } + + SpefNameMap::Iterator map_iter(name_map_); + while (map_iter.hasNext()) { + int index; + char *name; + map_iter.next(index, name); + stringDelete(name); + } +} + +void +SpefReader::setDivider(char divider) +{ + divider_ = divider; +} + +void +SpefReader::setDelimiter(char delimiter) +{ + delimiter_ = delimiter; +} + +void +SpefReader::setBusBrackets(char left, char right) +{ + if (!((left == '[' && right == ']') + || (left == '{' && right == '}') + || (left == '(' && right == ')') + || (left == '<' && right == '>') + || (left == ':' && right == '\0') + || (left == '.' && right == '\0'))) + warn("illegal bus delimiters.\n"); + bus_brkt_left_ = left; + bus_brkt_right_ = right; +} + +Instance * +SpefReader::findInstanceRelative(const char *name) +{ + return network_->findInstanceRelative(instance_, name); +} + +Net * +SpefReader::findNetRelative(const char *name) +{ + return network_->findNetRelative(instance_, name); +} + +Pin * +SpefReader::findPinRelative(const char *name) +{ + return network_->findPinRelative(instance_, name); +} + +Pin * +SpefReader::findPortPinRelative(const char *name) +{ + return network_->findPin(instance_, name); +} + +void +SpefReader::getChars(char *buf, + int &result, + size_t max_size) +{ + char *status = gzgets(stream_, buf, max_size); + if (status == Z_NULL) + result = 0; // YY_nullptr + else + result = static_cast(strlen(buf)); +} + +void +SpefReader::getChars(char *buf, + size_t &result, + size_t max_size) +{ + char *status = gzgets(stream_, buf, max_size); + if (status == Z_NULL) + result = 0; // YY_nullptr + else + result = strlen(buf); +} + +char * +SpefReader::translated(const char *token) +{ + return spefToSta(token, divider_, network_->pathDivider(), + network_->pathEscape()); +} + +void +SpefReader::incrLine() +{ + line_++; +} + +void +SpefReader::warn(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + report_->vfileWarn(filename_, line_, fmt, args); + va_end(args); +} + +void +SpefReader::setTimeScale(float scale, + const char *units) +{ + if (stringEq(units, "NS")) + time_scale_ = scale * 1E-9F; + else if (stringEq(units, "PS")) + time_scale_ = scale * 1E-12F; + else + warn("unknown units %s.\n", units); + stringDelete(units); +} + +void +SpefReader::setCapScale(float scale, + const char *units) +{ + if (stringEq(units, "PF")) + cap_scale_ = scale * 1E-12F; + else if (stringEq(units, "FF")) + cap_scale_ = scale * 1E-15F; + else + warn("unknown units %s.\n", units); + stringDelete(units); +} + +void +SpefReader::setResScale(float scale, + const char *units) +{ + if (stringEq(units, "OHM")) + res_scale_ = scale; + else if (stringEq(units, "KOHM")) + res_scale_ = scale * 1E+3F; + else + warn("unknown units %s.\n", units); + stringDelete(units); +} + +void +SpefReader::setInductScale(float scale, + const char *units) +{ + if (stringEq(units, "HENRY")) + induct_scale_ = scale; + else if (stringEq(units, "MH")) + induct_scale_ = scale * 1E-3F; + else if (stringEq(units, "UH")) + induct_scale_ = scale * 1E-6F; + else + warn("unknown units %s.\n", units); + stringDelete(units); +} + +void +SpefReader::makeNameMapEntry(char *index, + char *name) +{ + int i = atoi(index + 1); + name_map_[i] = name; +} + +char * +SpefReader::nameMapLookup(char *name) +{ + if (name && name[0] == '*') { + char *mapped_name; + bool exists; + int index = atoi(name + 1); + name_map_.findKey(index, mapped_name, exists); + if (exists) + return mapped_name; + else { + warn("no name map entry for %d.\n", index); + return 0; + } + } + else + return name; +} + +PortDirection * +SpefReader::portDirection(char *spef_dir) +{ + PortDirection *direction = PortDirection::unknown(); + if (stringEq(spef_dir, "I")) + direction = PortDirection::input(); + else if (stringEq(spef_dir, "O")) + direction = PortDirection::output(); + else if (stringEq(spef_dir, "B")) + direction = PortDirection::bidirect(); + else + warn("unknown port direction %s.\n", spef_dir); + return direction; +} + +void +SpefReader::setDesignFlow(StringSeq *flow) +{ + design_flow_ = flow; +} + +Pin * +SpefReader::findPin(char *name) +{ + Pin *pin = nullptr; + if (name) { + char *delim = strrchr(name, delimiter_); + if (delim) { + *delim = '\0'; + name = nameMapLookup(name); + Instance *inst = findInstanceRelative(name); + // Replace delimiter for error messages. + *delim = delimiter_; + const char *port_name = delim + 1; + if (inst) { + pin = network_->findPin(inst, port_name); + if (pin == nullptr) + warn("pin %s not found.\n", name); + } + else + warn("instance %s not found.\n", name); + } + else { + pin = findPortPinRelative(name); + if (pin == nullptr) + warn("pin %s not found.\n", name); + } + } + return pin; +} + +Net * +SpefReader::findNet(char *name) +{ + Net *net = nullptr; + name = nameMapLookup(name); + if (name) { + net = findNetRelative(name); + if (net == nullptr) + warn("net %s not found.\n", name); + } + return net; +} + +void +SpefReader::rspfBegin(Net *net, + SpefTriple *total_cap) +{ + if (net && !increment_) + parasitics_->deleteParasitics(net, ap_); + // Net total capacitance is ignored. + delete total_cap; +} + +void +SpefReader::rspfFinish() +{ +} + +void +SpefReader::rspfDrvrBegin(Pin *drvr_pin, + SpefRspfPi *pi) +{ + if (drvr_pin) { + // Incremental parasitics do not overwrite existing parasitics. + if (!(increment_ && + parasitics_->findPiElmore(drvr_pin, TransRiseFall::rise(), ap_))) { + 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_; + // Delete pi model and elmore delays. + parasitics_->deleteParasitics(drvr_pin, ap_); + // Only one parasitic, save it under rise transition. + parasitic_ = parasitics_->makePiElmore(drvr_pin, + TransRiseFall::rise(), + ap_, c2, rpi, c1); + } + } + delete pi; +} + +void +SpefReader::rspfLoad(Pin *load_pin, + SpefTriple *rc) +{ + if (parasitic_ && load_pin) { + float elmore = rc->value(triple_index_) * time_scale_; + parasitics_->setElmore(parasitic_, load_pin, elmore); + } + delete rc; +} + +void +SpefReader::rspfDrvrFinish() +{ + parasitic_ = nullptr; +} + +// Net cap (total_cap) is ignored. +void +SpefReader::dspfBegin(Net *net, + SpefTriple *total_cap) +{ + if (net) { + // Incremental parasitics do not overwrite existing parasitics. + if (increment_ + && parasitics_->findParasiticNetwork(net, ap_)) + parasitic_ = nullptr; + else + parasitic_ = parasitics_->makeParasiticNetwork(net, pin_cap_included_, + ap_); + net_ = net; + } + else { + parasitic_ = nullptr; + net_ = nullptr; + } + delete total_cap; +} + +void +SpefReader::dspfFinish() +{ + if (parasitic_) { + // Checking "should" be done by report_annotated_parasitics. + if (!quiet_) + parasitics_->check(parasitic_); + if (reduce_to_ != ReduceParasiticsTo::none) { + parasitics_->reduceTo(parasitic_, net_, reduce_to_, op_cond_, + corner_, cnst_min_max_, ap_); + if (delete_after_reduce_) + parasitics_->deleteParasiticNetwork(net_, ap_); + } + } + parasitic_ = nullptr; + net_ = nullptr; +} + +// Caller is only interested in nodes on net_. +ParasiticNode * +SpefReader::findParasiticNode(char *name) +{ + ParasiticNode *node; + Net *ext_net; + int ext_node_id; + Pin *ext_pin; + findParasiticNode(name, node, ext_net, ext_node_id, ext_pin); + if (node == nullptr + && (ext_net || ext_pin)) + warn("%s not connected to net %s.\n", name, network_->pathName(net_)); + return node; +} + +void +SpefReader::findParasiticNode(char *name, + ParasiticNode *&node, + Net *&ext_net, + int &ext_node_id, + Pin *&ext_pin) +{ + node = nullptr; + ext_net = nullptr; + ext_node_id = 0; + ext_pin = nullptr; + if (name) { + if (parasitic_) { + char *delim = strrchr(name, delimiter_); + if (delim) { + *delim = '\0'; + char *name2 = delim + 1; + name = nameMapLookup(name); + Instance *inst = findInstanceRelative(name); + if (inst) { + // : + Pin *pin = network_->findPin(inst, name2); + if (pin) { + if (network_->isConnected(net_, pin)) + node = parasitics_->ensureParasiticNode(parasitic_, pin); + else + ext_pin = pin; + } + else { + // Replace delimiter for error message. + *delim = delimiter_; + warn("pin %s not found.\n", name); + } + } + else { + Net *net = findNet(name); + // 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 (network_->isConnected(net, net_)) + node = parasitics_->ensureParasiticNode(parasitic_, net, id); + else { + ext_net = net; + ext_node_id = id; + } + } + else + warn("node %s not a pin or net:number\n", name); + } + } + } + else { + // + name = nameMapLookup(name); + Pin *pin = findPortPinRelative(name); + if (pin) { + if (network_->isConnected(net_, pin)) + node = parasitics_->ensureParasiticNode(parasitic_, pin); + else + ext_pin = pin; + } + else + warn("pin %s not found.\n", name); + } + } + } +} + +void +SpefReader::makeCapacitor(int, char *node_name, + SpefTriple *cap) +{ + ParasiticNode *node = findParasiticNode(node_name); + if (node) { + float cap1 = cap->value(triple_index_) * cap_scale_; + parasitics_->incrCap(node, cap1, ap_); + } + delete cap; + stringDelete(node_name); +} + +void +SpefReader::makeCapacitor(int id, + char *node_name1, + char *node_name2, + SpefTriple *cap) +{ + float cap1 = cap->value(triple_index_) * cap_scale_; + if (keep_coupling_caps_) + makeCouplingCap(id, node_name1, node_name2, cap1); + else { + ParasiticNode *node1, *node2; + Net *ext_net1, *ext_net2; + int ext_node_id1, ext_node_id2; + Pin *ext_pin1, *ext_pin2; + findParasiticNode(node_name1, node1, ext_net1, ext_node_id1, ext_pin1); + findParasiticNode(node_name2, node2, ext_net2, ext_node_id2, ext_pin2); + float scaled_cap = cap1 * ap_->couplingCapFactor(); + if (node1) + parasitics_->incrCap(node1, scaled_cap, ap_); + if (node2) + parasitics_->incrCap(node2, scaled_cap, ap_); + } + delete cap; + stringDelete(node_name1); + stringDelete(node_name2); +} + +void +SpefReader::makeCouplingCap(int id, + char *node_name1, + char *node_name2, + float cap) +{ + const char *name = nullptr; + const char *name_tmp = nullptr; + if (keep_device_names_) + // Prepend device type because OA uses one namespace for all devices. + name = name_tmp = stringPrint("C%d", id); + + ParasiticNode *node1, *node2; + Net *ext_net1, *ext_net2; + int ext_node_id1, ext_node_id2; + Pin *ext_pin1, *ext_pin2; + findParasiticNode(node_name1, node1, ext_net1, ext_node_id1, ext_pin1); + findParasiticNode(node_name2, node2, ext_net2, ext_node_id2, ext_pin2); + if (node1 && node2) + parasitics_->makeCouplingCap(name, node1, node2, cap, ap_); + if (node1 && node2 == nullptr) { + if (ext_net2) + parasitics_->makeCouplingCap(name, node1, ext_net2, ext_node_id2, + cap, ap_); + else if (ext_pin2) + parasitics_->makeCouplingCap(name, node1, ext_pin2, cap, ap_); + } + else if (node1 == nullptr && node2) { + if (ext_net1) + parasitics_->makeCouplingCap(name, node2, ext_net1, ext_node_id1, + cap, ap_); + else if (ext_pin1) + parasitics_->makeCouplingCap(name, node2, ext_pin1, cap, ap_); + } + stringDelete(name_tmp); +} + +void +SpefReader::makeResistor(int id, + char *node_name1, + char *node_name2, + SpefTriple *res) +{ + ParasiticNode *node1 = findParasiticNode(node_name1); + ParasiticNode *node2 = findParasiticNode(node_name2); + if (node1 && node2) { + float res1 = res->value(triple_index_) * res_scale_; + const char *name = nullptr; + const char *name_tmp = nullptr; + if (keep_device_names_) + // Prepend device type because OA uses one namespace for all devices. + name = name_tmp = stringPrint("R%d", id); + parasitics_->makeResistor(name, node1, node2, res1, ap_); + stringDelete(name_tmp); + } + delete res; + stringDelete(node_name1); + stringDelete(node_name2); +} + +//////////////////////////////////////////////////////////////// + +SpefRspfPi::SpefRspfPi(SpefTriple *c2, + SpefTriple *r1, + SpefTriple *c1) : + c2_(c2), + r1_(r1), + c1_(c1) +{ +} + +SpefRspfPi::~SpefRspfPi() +{ + delete c2_; + delete r1_; + delete c1_; +} + +//////////////////////////////////////////////////////////////// + +SpefTriple::SpefTriple(float value) : + is_triple_(false) +{ + values_[0] = value; +} + +SpefTriple::SpefTriple(float value1, + float value2, + float value3) : + is_triple_(true) +{ + values_[0] = value1; + values_[1] = value2; + values_[2] = value3; +} + +float +SpefTriple::value(int index) const +{ + if (is_triple_) + return values_[index]; + else + return values_[0]; +} + +} // namespace + +//////////////////////////////////////////////////////////////// +// Global namespace + +void spefFlushBuffer(); + +int +SpefParse_error(const char *msg) +{ + sta::spef_reader->warn("%s.\n", msg); + spefFlushBuffer(); + return 0; +} diff --git a/parasitics/SpefReader.hh b/parasitics/SpefReader.hh new file mode 100644 index 0000000..d208cfd --- /dev/null +++ b/parasitics/SpefReader.hh @@ -0,0 +1,54 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SPEF_READER_H +#define STA_SPEF_READER_H + +#include "Zlib.hh" + +namespace sta { + +class Network; +class Parasitics; +class ParasiticAnalysisPt; +class Instance; + +// 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. +// Return true if successful. +bool +readSpefFile(const char *filename, + Instance *instance, + ParasiticAnalysisPt *ap, + bool increment, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + ReduceParasiticsTo reduce_to, + bool delete_after_reduce, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + bool save, + bool quiet, + Report *report, + Network *network, + Parasitics *parasitics); + +} // namespace +#endif diff --git a/parasitics/SpefReaderPvt.hh b/parasitics/SpefReaderPvt.hh new file mode 100644 index 0000000..1ec304e --- /dev/null +++ b/parasitics/SpefReaderPvt.hh @@ -0,0 +1,210 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SPEF_READER_PVT_H +#define STA_SPEF_READER_PVT_H + +#include "Zlib.hh" +#include "Map.hh" +#include "StringSeq.hh" +#include "NetworkClass.hh" +#include "ParasiticsClass.hh" + +// Global namespace. +#define YY_INPUT(buf,result,max_size) \ + sta::spef_reader->getChars(buf, result, max_size) + +int +SpefParse_error(const char *msg); + +//////////////////////////////////////////////////////////////// + +namespace sta { + +class Report; +class OperatingConditions; +class MinMax; +class SpefRspfPi; +class SpefTriple; +class Corner; + +typedef Map > SpefNameMap; + +class SpefReader +{ +public: + SpefReader(const char *filename, + gzFile stream, + Instance *instance, + ParasiticAnalysisPt *ap, + bool increment, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + ReduceParasiticsTo reduce_to, + bool delete_after_reduce, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *cnst_min_max, + bool quiet, + Report *report, + Network *network, + Parasitics *parasitics); + virtual ~SpefReader(); + char divider() const { return divider_; } + void setDivider(char divider); + char delimiter() const { return delimiter_; } + void setDelimiter(char delimiter); + void incrLine(); + int line() const { return line_; } + const char *filename() const { return filename_; } + // flex YY_INPUT yy_n_chars arg changed definition from int to size_t, + // so provide both forms. + void getChars(char *buf, + int &result, + size_t max_size); + void getChars(char *buf, + size_t &result, + size_t max_size); + // Translate from spf/spef namespace to sta namespace. + char *translated(const char *token); + void warn(const char *fmt, ...) + __attribute__((format (printf, 2, 3))); + void setBusBrackets(char left, + char right); + void setTimeScale(float scale, + const char *units); + void setCapScale(float scale, + const char *units); + void setResScale(float scale, + const char *units); + void setInductScale(float scale, + const char *units); + void makeNameMapEntry(char *index, + char *name); + char *nameMapLookup(char *index); + void setDesignFlow(StringSeq *flow_keys); + Pin *findPin(char *name); + Net *findNet(char *name); + void rspfBegin(Net *net, + SpefTriple *total_cap); + void rspfFinish(); + void rspfDrvrBegin(Pin *drvr_pin, + SpefRspfPi *pi); + void rspfLoad(Pin *load_pin, + SpefTriple *rc); + void rspfDrvrFinish(); + void dspfBegin(Net *net, + 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); + +private: + Pin *findPinRelative(const char *name); + Pin *findPortPinRelative(const char *name); + Net *findNetRelative(const char *name); + Instance *findInstanceRelative(const char *name); + void makeCouplingCap(int id, + char *node_name1, + char *node_name2, + float cap); + ParasiticNode *findParasiticNode(char *name); + void findParasiticNode(char *name, + ParasiticNode *&node, + Net *&ext_net, + int &ext_node_id, + Pin *&ext_pin); + + const char *filename_; + Instance *instance_; + const ParasiticAnalysisPt *ap_; + bool increment_; + bool pin_cap_included_; + bool keep_coupling_caps_; + ReduceParasiticsTo reduce_to_; + bool delete_after_reduce_; + const OperatingConditions *op_cond_; + const Corner *corner_; + const MinMax *cnst_min_max_; + // Normally no need to keep device names. + bool keep_device_names_; + bool quiet_; + gzFile stream_; + int line_; + char divider_; + char delimiter_; + char bus_brkt_left_; + char bus_brkt_right_; + Net *net_; + Report *report_; + Network *network_; + Parasitics *parasitics_; + + int triple_index_; + float time_scale_; + float cap_scale_; + float res_scale_; + float induct_scale_; + SpefNameMap name_map_; + StringSeq *design_flow_; + Parasitic *parasitic_; +}; + +class SpefTriple +{ +public: + SpefTriple(float value); + SpefTriple(float value1, + float value2, + float value3); + float value(int index) const; + bool isTriple() const { return is_triple_; } + +private: + float values_[3]; + bool is_triple_; +}; + +class SpefRspfPi +{ +public: + SpefRspfPi(SpefTriple *c2, SpefTriple *r1, SpefTriple *c1); + ~SpefRspfPi(); + SpefTriple *c2() { return c2_; } + SpefTriple *r1() { return r1_; } + SpefTriple *c1() { return c1_; } + +private: + SpefTriple *c2_; + SpefTriple *r1_; + SpefTriple *c1_; +}; + +extern SpefReader *spef_reader; + +} // namespace +#endif diff --git a/sdc/Clock.cc b/sdc/Clock.cc new file mode 100644 index 0000000..a8ff6ad --- /dev/null +++ b/sdc/Clock.cc @@ -0,0 +1,713 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "Error.hh" +#include "StringUtil.hh" +#include "Transition.hh" +#include "TimingRole.hh" +#include "MinMax.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "Graph.hh" +#include "Clock.hh" + +namespace sta { + +static bool +isPowerOfTwo(int i); + +Clock::Clock(const char *name, + int index) : + name_(stringCopy(name)), + pins_(nullptr), + add_to_pins_(false), + vertex_pins_(nullptr), + pll_out_(nullptr), + pll_fdbk_(nullptr), + 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) +{ + makeClkEdges(); +} + +void +Clock::initClk(PinSet *pins, + bool add_to_pins, + float period, + FloatSeq *waveform, + const char *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; + setClkEdgeTimes(); + setComment(comment); +} + +bool +Clock::isVirtual() const +{ + return pins_ == nullptr || pins_->empty(); +} + +void +Clock::setPins(PinSet *pins, + const Network *network) +{ + delete pins_; + pins_ = pins; + makeVertexPins(network); +} + +void +Clock::makeVertexPins(const Network *network) +{ + if (pins_) { + if (vertex_pins_) + vertex_pins_->clear(); + else + vertex_pins_ = new PinSet; + PinSet::Iterator pin_iter(pins_); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + findVertexDriverPins(pin, network, vertex_pins_); + } + } +} + +void +Clock::setMasterClk(Clock *master) +{ + master_clk_ = master; + waveform_valid_ = false; +} + +void +Clock::makeClkEdges() +{ + clk_edges_ = new ClockEdge*[TransRiseFall::index_count]; + for (auto tr : TransRiseFall::range()) { + clk_edges_[tr->index()] = new ClockEdge(this, tr); + } +} + +Clock::~Clock() +{ + stringDelete(name_); + delete pins_; + delete vertex_pins_; + if (clk_edges_) { + delete clk_edges_[TransRiseFall::riseIndex()]; + delete clk_edges_[TransRiseFall::fallIndex()]; + delete [] clk_edges_; + } + delete waveform_; + delete edges_; + delete edge_shifts_; + delete uncertainties_; +} + +void +Clock::addPin(Pin *pin) +{ + if (pins_ == nullptr) + pins_ = new PinSet; + pins_->insert(pin); + if (vertex_pins_ == nullptr) + vertex_pins_ = new PinSet; + vertex_pins_->insert(pin); +} + +void +Clock::deletePin(Pin *pin) +{ + pins_->erase(pin); +} + +void +Clock::setAddToPins(bool add_to_pins) +{ + add_to_pins_ = add_to_pins; +} + +void +Clock::setClkEdgeTimes() +{ + setClkEdgeTime(TransRiseFall::rise()); + setClkEdgeTime(TransRiseFall::fall()); +} + +void +Clock::setClkEdgeTime(const TransRiseFall *tr) +{ + float time = (tr == TransRiseFall::rise()) ? (*waveform_)[0]:(*waveform_)[1]; + clk_edges_[tr->index()]->setTime(time); +} + +Pin * +Clock::defaultPin() const +{ + PinSet::Iterator pin_iter(vertex_pins_); + if (pin_iter.hasNext()) + return pin_iter.next(); + else + return nullptr; +} + +ClockEdge * +Clock::edge(const TransRiseFall *tr) const +{ + return clk_edges_[tr->index()]; +} + +void +Clock::setIsPropagated(bool propagated) +{ + is_propagated_ = propagated; +} + +void +Clock::slew(const TransRiseFall *tr, + const MinMax *min_max, + // Return values. + float &slew, + bool &exists) const +{ + slews_.value(tr, min_max, slew, exists); +} + +float +Clock::slew(const TransRiseFall *tr, + const MinMax *min_max) const +{ + float slew; + bool exists; + slews_.value(tr, min_max, slew, exists); + if (!exists) + slew = 0.0; + return slew; +} + +void +Clock::setSlew(const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float slew) +{ + slews_.setValue(tr, min_max, slew); +} + +void +Clock::setSlew(const TransRiseFall *tr, + const MinMax *min_max, + float slew) +{ + slews_.setValue(tr, min_max, slew); +} + +void +Clock::removeSlew() +{ + slews_.clear(); +} + +void +Clock::setSlewLimit(const TransRiseFallBoth *tr, + const PathClkOrData clk_data, + const MinMax *min_max, + float slew) +{ + slew_limits_[int(clk_data)].setValue(tr, min_max, slew); +} + +void +Clock::slewLimit(const TransRiseFall *tr, + const PathClkOrData clk_data, + const MinMax *min_max, + // Return values. + float &slew, + bool &exists) const +{ + slew_limits_[int(clk_data)].value(tr, min_max, slew, exists); +} + +void +Clock::uncertainty(const SetupHold *setup_hold, + // Return values. + float &uncertainty, + bool &exists) const +{ + if (uncertainties_) + uncertainties_->value(setup_hold, uncertainty, exists); + else { + uncertainty = 0.0F; + exists = false; + } +} + +void +Clock::setUncertainty(const SetupHoldAll *setup_hold, + float uncertainty) +{ + if (uncertainties_ == nullptr) + uncertainties_ = new ClockUncertainties; + uncertainties_->setValue(setup_hold, uncertainty); +} + +void +Clock::setUncertainty(const SetupHold *setup_hold, + float uncertainty) +{ + if (uncertainties_ == nullptr) + uncertainties_ = new ClockUncertainties; + 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; + } + } +} + +void +Clock::waveformInvalid() +{ + waveform_valid_ = false; +} + +//////////////////////////////////////////////////////////////// + +void +Clock::initGeneratedClk(PinSet *pins, + bool add_to_pins, + Pin *src_pin, + Clock *master_clk, + Pin *pll_out, + Pin *pll_fdbk, + 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) +{ + is_generated_ = true; + setPins(pins, network); + add_to_pins_ = add_to_pins; + src_pin_ = src_pin; + master_clk_ = master_clk; + master_clk_infered_ = false; + waveform_valid_ = false; + pll_out_= pll_out; + pll_fdbk_ = pll_fdbk; + divide_by_ = divide_by; + multiply_by_ = multiply_by; + duty_cycle_ = duty_cycle; + invert_ = invert; + combinational_ = combinational; + 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; +} + +void +Clock::setInferedMasterClk(Clock *master_clk) +{ + master_clk_ = master_clk; + master_clk_infered_ = true; + waveform_valid_ = false; +} + +bool +Clock::isGenerated() const +{ + return is_generated_; +} + +bool +Clock::isGeneratedWithPropagatedMaster() const +{ + return is_generated_ + && master_clk_ + // Insertion is zero if the master clock is ideal. + && master_clk_->isPropagated(); +} + +void +Clock::generate(const Clock *src_clk) +{ + if (waveform_ == nullptr) + waveform_ = new FloatSeq; + else + 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]); + } + 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); + } + else + generateScaledClk(src_clk, static_cast(divide_by_)); + } + else if (multiply_by_ >= 1) + generateScaledClk(src_clk, 1.0F / multiply_by_); + else if (edges_) + generateEdgesClk(src_clk); + + if (invert_) { + float first_time = (*waveform_)[0]; + float offset = (first_time >= period_) ? period_ : 0.0F; + 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_; + } + setClkEdgeTimes(); + waveform_valid_ = true; +} + +void +Clock::generateScaledClk(const Clock *src_clk, + 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); + } + else { + FloatSeq::ConstIterator wave_iter(src_clk->waveform()); + while (wave_iter.hasNext()) { + float time = wave_iter.next(); + waveform_->push_back(time * scale); + } + } +} + +void +Clock::generateEdgesClk(const Clock *src_clk) +{ + // The create_generated_clock tcl cmd and Sta::makeClock + // enforce this restriction. + if (edges_->size() == 3) { + const FloatSeq *src_wave = src_clk->waveform(); + size_t src_size = src_wave->size(); + float src_period = src_clk->period(); + + int edge0_1 = (*edges_)[0] - 1; + float rise = (*src_wave)[edge0_1 % src_size] + + (edge0_1 / src_size) * src_period; + if (edge_shifts_) + rise += (*edge_shifts_)[0]; + waveform_->push_back(rise); + + int edge1_1 = (*edges_)[1] - 1; + float fall = (*src_wave)[edge1_1 % src_size] + + (edge1_1 / src_size) * src_period; + if (edge_shifts_) + fall += (*edge_shifts_)[1]; + waveform_->push_back(fall); + + int edge2_1 = (*edges_)[2] - 1; + period_ = (*src_wave)[edge2_1 % src_size] + + (edge2_1 / src_size) * src_period - rise; + if (edge_shifts_) + period_ += (*edge_shifts_)[2]; + } + else + internalError("generated clock edges size is not three."); +} + +static bool +isPowerOfTwo(int i) +{ + return (i & (i - 1)) == 0; +} + +const TransRiseFall * +Clock::masterClkEdgeTr(const TransRiseFall *tr) const +{ + int edge_index = (tr == TransRiseFall::rise()) ? 0 : 1; + return ((*edges_)[edge_index] - 1) % 2 + ? TransRiseFall::fall() + : TransRiseFall::rise(); +} + +void +Clock::srcPinVertices(VertexSet &src_vertices, + const Network *network, + Graph *graph) +{ + if (network->isHierarchical(src_pin_)) { + // Use the clocks on a non-hierarchical pin on the same net. + PinSet vertex_pins; + findVertexDriverPins(src_pin_, network, &vertex_pins); + PinSet::Iterator pin_iter(vertex_pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + Vertex *vertex, *bidirect_drvr_vertex; + graph->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex) + src_vertices.insert(vertex); + if (bidirect_drvr_vertex) + src_vertices.insert(bidirect_drvr_vertex); + } + } + else { + Vertex *vertex = graph->pinDrvrVertex(src_pin_); + src_vertices.insert(vertex); + } +} + +bool +Clock::isDivideByOneCombinational() const +{ + return combinational_ + && divide_by_ == 1 + && multiply_by_ == 0 + && edge_shifts_ == 0; +} + +//////////////////////////////////////////////////////////////// + +ClockEdge::ClockEdge(Clock *clock, + TransRiseFall *tr) : + clock_(clock), + tr_(tr), + name_(stringPrint("%s %s", clock_->name(), tr_->asString())), + time_(0.0), + index_(clock_->index() * TransRiseFall::index_count + tr_->index()) +{ +} + +ClockEdge::~ClockEdge() +{ + stringDelete(name_); +} + +void +ClockEdge::setTime(float time) +{ + time_ = time; +} + +ClockEdge * +ClockEdge::opposite() const +{ + return clock_->edge(tr_->opposite()); +} + +float +ClockEdge::pulseWidth() const +{ + ClockEdge *opp_clk_edge = opposite(); + float width = opp_clk_edge->time() - time_; + if (width < 0.0) + width += clock_->period(); + return width; +} + +//////////////////////////////////////////////////////////////// + +int +clkCmp(const Clock *clk1, + const Clock *clk2) +{ + if (clk1 == nullptr && clk2) + return -1; + else if (clk1 == nullptr && clk2 == nullptr) + return 0; + else if (clk1 && clk2 == nullptr) + return 1; + else { + int index1 = clk1->index(); + int index2 = clk2->index(); + if (index1 < index2) + return -1; + else if (index1 == index2) + return 0; + else + return 1; + } +} + +int +clkEdgeCmp(ClockEdge *clk_edge1, + ClockEdge *clk_edge2) +{ + if (clk_edge1 == nullptr && clk_edge2) + return -1; + else if (clk_edge1 == nullptr && clk_edge2 == nullptr) + return 0; + else if (clk_edge1 && clk_edge2 == nullptr) + return 1; + else { + int index1 = clk_edge1->index(); + int index2 = clk_edge2->index(); + if (index1 < index2) + return -1; + else if (index1 == index2) + return 0; + else + return 1; + } +} + +bool +clkEdgeLess(ClockEdge *clk_edge1, + ClockEdge *clk_edge2) +{ + return clkEdgeCmp(clk_edge1, clk_edge2) < 0; +} + +//////////////////////////////////////////////////////////////// + +InterClockUncertainty::InterClockUncertainty(const Clock *src, + const Clock *target) : + src_(src), + target_(target) +{ +} + +bool +InterClockUncertainty::empty() const +{ + return uncertainties_[TransRiseFall::riseIndex()].empty() + && uncertainties_[TransRiseFall::fallIndex()].empty(); +} + +void +InterClockUncertainty::uncertainty(const TransRiseFall *src_tr, + const TransRiseFall *tgt_tr, + const SetupHold *setup_hold, + float &uncertainty, + bool &exists) const +{ + uncertainties_[src_tr->index()].value(tgt_tr, setup_hold, + uncertainty, exists); +} + +void +InterClockUncertainty::setUncertainty(const TransRiseFallBoth *src_tr, + const TransRiseFallBoth *tgt_tr, + const SetupHoldAll *setup_hold, + float uncertainty) +{ + for (auto src_tr_index : src_tr->rangeIndex()) + uncertainties_[src_tr_index].setValue(tgt_tr, setup_hold, uncertainty); +} + +void +InterClockUncertainty::removeUncertainty(const TransRiseFallBoth *src_tr, + const TransRiseFallBoth *tgt_tr, + const SetupHoldAll *setup_hold) +{ + for (auto src_tr_index : src_tr->rangeIndex()) + uncertainties_[src_tr_index].removeValue(tgt_tr, setup_hold); +} + +const RiseFallMinMax * +InterClockUncertainty::uncertainties(TransRiseFall *src_tr) const +{ + return &uncertainties_[src_tr->index()]; +} + +bool +InterClockUncertaintyLess::operator()(const InterClockUncertainty *inter1, + const InterClockUncertainty *inter2)const +{ + return inter1->src()->index() < inter2->src()->index() + || (inter1->src() == inter2->src() + && inter1->target()->index() < inter2->target()->index()); +} + +//////////////////////////////////////////////////////////////// + +bool +ClockNameLess::operator()(const Clock *clk1, + const Clock *clk2) +{ + return stringLess(clk1->name(), clk2->name()); +} + +void +sortClockSet(ClockSet *set, + ClockSeq &clks) +{ + ClockSet::Iterator clk_iter(set); + while (clk_iter.hasNext()) + clks.push_back(clk_iter.next()); + sort(clks, ClockNameLess()); +} + +} // namespace diff --git a/sdc/Clock.hh b/sdc/Clock.hh new file mode 100644 index 0000000..f66cc25 --- /dev/null +++ b/sdc/Clock.hh @@ -0,0 +1,316 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CLOCK_H +#define STA_CLOCK_H + +#include "DisallowCopyAssign.hh" +#include "MinMax.hh" +#include "RiseFallMinMax.hh" +#include "SdcClass.hh" +#include "SdcCmdComment.hh" +#include "GraphClass.hh" + +namespace sta { + +typedef Map ClkHpinEdgeMap; + +class Clock : public SdcCmdComment +{ +public: + ~Clock(); + const char *name() const { return name_; } + float period() const { return period_; } + // Virtual clocks have no pins. + bool isVirtual() const; + PinSet *pins() const { return pins_; } + PinSet *vertexPins() const { return vertex_pins_; } + // Clock pin used by input/output delay for propagated generated + // clock insertion delay. + 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_; } + ClockEdge *edge(const TransRiseFall *tr) const; + int index() const { return index_; } + bool isPropagated() const { return is_propagated_; } + void setIsPropagated(bool propagated); + // Ideal clock slew. + void slew(const TransRiseFall *tr, + const MinMax *min_max, + // Return values. + float &slew, + bool &exists) const; + // Return zero (default) if no slew exists. + float slew(const TransRiseFall *tr, + const MinMax *min_max) const; + void setSlew(const TransRiseFall *tr, + const MinMax *min_max, + float slew); + void setSlew(const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float slew); + void removeSlew(); + RiseFallMinMax *slews() { return &slews_; } + void setSlewLimit(const TransRiseFallBoth *tr, + const PathClkOrData clk_data, + const MinMax *min_max, + float slew); + void slewLimit(const TransRiseFall *tr, + const PathClkOrData clk_data, + const MinMax *min_max, + // Return values. + float &slew, + bool &exists) const; + ClockUncertainties *uncertainties() const { return uncertainties_; } + void uncertainty(const SetupHold *setup_hold, + // Return values. + float &uncertainty, + bool &exists) const; + void setUncertainty(const SetupHoldAll *setup_hold, + float uncertainty); + void setUncertainty(const SetupHold *setup_hold, + float uncertainty); + void removeUncertainty(const SetupHoldAll *setup_hold); + + void setPeriod(float period); + void setWaveform(FloatSeq *waveform); + + void addPin(Pin *pin); + void deletePin(Pin *pin); + void makeVertexPins(const Network *network); + + bool isGenerated() const; + bool isGeneratedWithPropagatedMaster() const; + void generate(const Clock *src_clk); + Pin *srcPin() const { return src_pin_; } + Clock *masterClk() const { return master_clk_; } + bool masterClkInfered() const { return master_clk_infered_; } + void setInferedMasterClk(Clock *master_clk); + Pin *pllOut() const { return pll_out_; } + Pin *pllFdbk() const { return pll_fdbk_; } + int divideBy() const { return divide_by_; } + 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 TransRiseFall *masterClkEdgeTr(const TransRiseFall *tr) const; + bool combinational() const { return combinational_; } + bool isDivideByOneCombinational() const; + bool generatedUpToDate() const; + void srcPinVertices(VertexSet &src_vertices, + 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 Constraints::makeClock. + Clock(const char *name, + int index); + 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, + Pin *pll_out, + Pin *pll_fdbk, + 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 setMasterClk(Clock *master); + void makeClkEdges(); + void setClkEdgeTimes(); + void setClkEdgeTime(const TransRiseFall *tr); + void generateScaledClk(const Clock *src_clk, + float scale); + void generateEdgesClk(const Clock *src_clk); + + const char *name_; + PinSet *pins_; + bool add_to_pins_; + // Hierarchical pins in pins_ become driver pins through the pin. + PinSet *vertex_pins_; + Pin *pll_out_; + Pin *pll_fdbk_; + float period_; + FloatSeq *waveform_; + bool waveform_valid_; + const int index_; + ClockEdge **clk_edges_; + bool is_propagated_; + RiseFallMinMax slews_; + RiseFallMinMax slew_limits_[path_clk_or_data_count]; + ClockUncertainties *uncertainties_; + bool is_generated_; + // Generated clock variables. + Pin *src_pin_; + Clock *master_clk_; + // 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_; + +private: + DISALLOW_COPY_AND_ASSIGN(Clock); + + friend class Sdc; +}; + +// A single rise/fall edge of a clock +class ClockEdge +{ +public: + Clock *clock() const { return clock_; } + ~ClockEdge(); + TransRiseFall *transition() const { return tr_; } + float time() const { return time_; } + const char *name() const { return name_; } + int index() const { return index_; } + ClockEdge *opposite() const; + // Pulse width if this is the leading edge of the pulse. + float pulseWidth() const; + + friend class Clock; // builder +private: + DISALLOW_COPY_AND_ASSIGN(ClockEdge); + ClockEdge(Clock *clock, TransRiseFall *tr); + void setTime(float time); + + Clock *clock_; + TransRiseFall *tr_; + const char *name_; + float time_; + int index_; +}; + +int +clkCmp(const Clock *clk1, + const Clock *clk2); +int +clkEdgeCmp(ClockEdge *clk_edge1, + ClockEdge *clk_edge2); +bool +clkEdgeLess(ClockEdge *clk_edge1, + ClockEdge *clk_edge2); + +class ClockNameLess +{ +public: + bool operator()(const Clock *clk1, const Clock *clk2); +}; + +//////////////////////////////////////////////////////////////// + +class InterClockUncertainty +{ +public: + InterClockUncertainty(const Clock *src, + const Clock *target); + const Clock *src() const { return src_; } + const Clock *target() const { return target_; } + void uncertainty(const TransRiseFall *src_tr, + const TransRiseFall *tgt_tr, + const SetupHold *setup_hold, + // Return values. + float &uncertainty, + bool &exists) const; + void setUncertainty(const TransRiseFallBoth *src_tr, + const TransRiseFallBoth *tgt_tr, + const SetupHoldAll *setup_hold, + float uncertainty); + void removeUncertainty(const TransRiseFallBoth *src_tr, + const TransRiseFallBoth *tgt_tr, + const SetupHoldAll *setup_hold); + const RiseFallMinMax *uncertainties(TransRiseFall *src_tr) const; + bool empty() const; + +private: + DISALLOW_COPY_AND_ASSIGN(InterClockUncertainty); + + const Clock *src_; + const Clock *target_; + RiseFallMinMax uncertainties_[TransRiseFall::index_count]; +}; + +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()); + } +}; + +void +sortClockSet(ClockSet * set, + ClockSeq &clks); + +// Clock source pins. +class ClockPinIterator : public PinSet::Iterator +{ +public: + ClockPinIterator(Clock *clk) : + PinSet::Iterator(clk->pins()) + { + } +}; + +// The clock source pin's graph vertex pins. +// If the source pin is hierarchical, the vertex pins are: +// hierarchical input - load pins inside the hierarchical instance +// hierarchical output - load pins outside the hierarchical instance +class ClockVertexPinIterator : public PinSet::Iterator +{ +public: + ClockVertexPinIterator(Clock *clk) : + PinSet::Iterator(clk->vertexPins()) + { + } +}; + +} // namespace +#endif diff --git a/sdc/ClockGatingCheck.cc b/sdc/ClockGatingCheck.cc new file mode 100644 index 0000000..182aa5f --- /dev/null +++ b/sdc/ClockGatingCheck.cc @@ -0,0 +1,33 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "ClockGatingCheck.hh" + +namespace sta { + +ClockGatingCheck::ClockGatingCheck() : + active_value_(LogicValue::unknown) +{ +} + +void +ClockGatingCheck::setActiveValue(LogicValue value) +{ + active_value_ = value; +} + +} // namespace diff --git a/sdc/ClockGatingCheck.hh b/sdc/ClockGatingCheck.hh new file mode 100644 index 0000000..de3e1fc --- /dev/null +++ b/sdc/ClockGatingCheck.hh @@ -0,0 +1,42 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CLOCK_GATING_CHECK_H +#define STA_CLOCK_GATING_CHECK_H + +#include "DisallowCopyAssign.hh" +#include "SdcClass.hh" +#include "RiseFallMinMax.hh" + +namespace sta { + +class ClockGatingCheck +{ +public: + ClockGatingCheck(); + RiseFallMinMax *margins() { return &margins_; } + void setActiveValue(LogicValue value); + LogicValue activeValue() const { return active_value_; } + +private: + DISALLOW_COPY_AND_ASSIGN(ClockGatingCheck); + + RiseFallMinMax margins_; + LogicValue active_value_; +}; + +} // namespace +#endif diff --git a/sdc/ClockGroups.cc b/sdc/ClockGroups.cc new file mode 100644 index 0000000..f3157d2 --- /dev/null +++ b/sdc/ClockGroups.cc @@ -0,0 +1,85 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "StringUtil.hh" +#include "ClockGroups.hh" + +namespace sta { + +ClockGroup::ClockGroup(ClockSet *clks) : + clks_(clks) +{ +} + +ClockGroup::~ClockGroup() +{ + delete clks_; +} + +bool +ClockGroup::isMember(const Clock *clk) +{ + return clks_->hasKey(const_cast(clk)); +} + +//////////////////////////////////////////////////////////////// + +ClockGroups::ClockGroups(const char *name, + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + const char *comment) : + SdcCmdComment(comment), + name_(stringCopy(name)), + logically_exclusive_(logically_exclusive), + physically_exclusive_(physically_exclusive), + asynchronous_(asynchronous), + allow_paths_(allow_paths) +{ +} + +ClockGroups::~ClockGroups() +{ + stringDelete(name_); + groups_.deleteContentsClear(); +} + +ClockGroup * +ClockGroups::makeClockGroup(ClockSet *clks) +{ + ClockGroup *group = new ClockGroup(clks); + groups_.insert(group); + return group; +} + +void +ClockGroups::removeClock(Clock *clk) +{ + ClockGroupSet::Iterator group_iter(groups_); + while (group_iter.hasNext()) { + ClockGroup *group = group_iter.next(); + ClockSet *clks = group->clks(); + clks->erase(clk); + if (clks->empty()) { + groups_.erase(group); + delete group; + } + } +} + +} // namespace diff --git a/sdc/ClockGroups.hh b/sdc/ClockGroups.hh new file mode 100644 index 0000000..9954d95 --- /dev/null +++ b/sdc/ClockGroups.hh @@ -0,0 +1,71 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CLOCK_GROUPS_H +#define STA_CLOCK_GROUPS_H + +#include "DisallowCopyAssign.hh" +#include "SdcCmdComment.hh" +#include "SdcClass.hh" + +namespace sta { + +class ClockGroup +{ +public: + ClockGroup(ClockSet *clks); + ~ClockGroup(); + bool isMember(const Clock *clk); + ClockSet *clks() const { return clks_; } + +private: + DISALLOW_COPY_AND_ASSIGN(ClockGroup); + + ClockSet *clks_; +}; + +class ClockGroups : public SdcCmdComment +{ +public: + ClockGroups(const char *name, + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + const char *comment); + ~ClockGroups(); + ClockGroup *makeClockGroup(ClockSet *clks); + const char *name() const { return name_; } + ClockGroupSet *groups() { return &groups_; } + bool logicallyExclusive() const { return logically_exclusive_; } + bool physicallyExclusive() const { return physically_exclusive_; } + bool asynchronous() const { return asynchronous_; } + bool allowPaths() const { return allow_paths_; } + void removeClock(Clock *clk); + +private: + const char *name_; + bool logically_exclusive_; + bool physically_exclusive_; + bool asynchronous_; + bool allow_paths_; + ClockGroupSet groups_; + + DISALLOW_COPY_AND_ASSIGN(ClockGroups); +}; + +} // namespace +#endif diff --git a/sdc/ClockInsertion.cc b/sdc/ClockInsertion.cc new file mode 100644 index 0000000..b68a64f --- /dev/null +++ b/sdc/ClockInsertion.cc @@ -0,0 +1,89 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "ClockInsertion.hh" + +namespace sta { + +ClockInsertion::ClockInsertion(const Clock *clk, + const Pin *pin) : + clk_(clk), + pin_(pin) +{ +} + +void +ClockInsertion::setDelay(const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay) +{ + for (auto el_index : early_late->rangeIndex()) + delays_[el_index].setValue(tr, min_max, delay); +} + +float +ClockInsertion::delay(const TransRiseFall *tr, + const MinMax *min_max, + const EarlyLate *early_late) +{ + float insertion; + bool exists; + delays_[early_late->index()].value(tr, min_max, insertion, exists); + if (exists) + return insertion; + else + return 0.0; +} + +void +ClockInsertion::delay(const TransRiseFall *tr, + const MinMax *min_max, + const EarlyLate *early_late, + // Return values. + float &insertion, + bool &exists) + +{ + delays_[early_late->index()].value(tr, min_max, insertion, exists); + if (!exists) + insertion = 0.0; +} + +void +ClockInsertion::setDelay(const TransRiseFall *tr, + const MinMax *min_max, + const EarlyLate *early_late, + float delay) +{ + delays_[early_late->index()].setValue(tr, min_max, delay); +} + +void +ClockInsertion::setDelays(RiseFallMinMax *delays) +{ + for (auto el_index : EarlyLate::rangeIndex()) + delays_[el_index].setValues(delays); +} + +RiseFallMinMax * +ClockInsertion::delays(const EarlyLate *early_late) +{ + return &delays_[early_late->index()]; +} + +} // namespace diff --git a/sdc/ClockInsertion.hh b/sdc/ClockInsertion.hh new file mode 100644 index 0000000..196b857 --- /dev/null +++ b/sdc/ClockInsertion.hh @@ -0,0 +1,57 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CLOCK_INSERTION_H +#define STA_CLOCK_INSERTION_H + +#include "DisallowCopyAssign.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" +#include "MinMax.hh" +#include "RiseFallMinMax.hh" +#include "Transition.hh" + +namespace sta { + +class ClockInsertion +{ +public: + ClockInsertion(const Clock *clk, const Pin *pin); + const Clock *clock() const { return clk_; } + const Pin *pin() const { return pin_; } + float delay(const TransRiseFall *tr, const MinMax *min_max, + const EarlyLate *early_late); + void delay(const TransRiseFall *tr, const MinMax *min_max, + const EarlyLate *early_late, + // Return values. + float &insertion, bool &exists); + RiseFallMinMax *delays(const EarlyLate *early_late); + void setDelay(const TransRiseFall *tr, const MinMax *min_max, + const EarlyLate *early_late, float delay); + void setDelay(const TransRiseFallBoth *tr, const MinMaxAll *min_max, + const EarlyLateAll *early_late, float delay); + void setDelays(RiseFallMinMax *delays); + +private: + DISALLOW_COPY_AND_ASSIGN(ClockInsertion); + + const Clock *clk_; + const Pin *pin_; + RiseFallMinMax delays_[EarlyLate::index_count]; +}; + +} // namespace +#endif diff --git a/sdc/ClockLatency.cc b/sdc/ClockLatency.cc new file mode 100644 index 0000000..873b93c --- /dev/null +++ b/sdc/ClockLatency.cc @@ -0,0 +1,83 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "ClockLatency.hh" + +namespace sta { + +ClockLatency::ClockLatency(const Clock *clk, + const Pin *pin) : + clk_(clk), + pin_(pin) +{ +} + +void +ClockLatency::setDelay(const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float delay) +{ + delays_.setValue(tr, min_max, delay); +} + +float +ClockLatency::delay(const TransRiseFall *tr, + const MinMax *min_max) +{ + float latency; + bool exists; + delays_.value(tr, min_max, latency, exists); + if (exists) + return latency; + else + return 0.0; +} + +void +ClockLatency::delay(const TransRiseFall *tr, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) + +{ + delays_.value(tr, min_max, latency, exists); + if (!exists) + latency = 0.0; +} + +void +ClockLatency::setDelay(const TransRiseFall *tr, + const MinMax *min_max, + float delay) +{ + delays_.setValue(tr, min_max, delay); +} + +void +ClockLatency::setDelays(RiseFallMinMax *delays) +{ + delays_.setValues(delays); +} + +RiseFallMinMax * +ClockLatency::delays() +{ + return &delays_; +} + +} // namespace diff --git a/sdc/ClockLatency.hh b/sdc/ClockLatency.hh new file mode 100644 index 0000000..53f2181 --- /dev/null +++ b/sdc/ClockLatency.hh @@ -0,0 +1,61 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CLOCK_LATENCY_H +#define STA_CLOCK_LATENCY_H + +#include "DisallowCopyAssign.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" +#include "MinMax.hh" +#include "RiseFallMinMax.hh" +#include "Transition.hh" + +namespace sta { + +class ClockLatency +{ +public: + ClockLatency(const Clock *clk, + const Pin *pin); + const Clock *clock() const { return clk_; } + const Pin *pin() const { return pin_; } + float delay(const TransRiseFall *tr, + const MinMax *min_max); + void delay(const TransRiseFall *tr, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists); + RiseFallMinMax *delays(); + void setDelay(const TransRiseFall *tr, + const MinMax *min_max, + float delay); + void setDelay(const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float delay); + void setDelays(RiseFallMinMax *delays); + +private: + DISALLOW_COPY_AND_ASSIGN(ClockLatency); + + const Clock *clk_; + const Pin *pin_; + RiseFallMinMax delays_; +}; + +} // namespace +#endif diff --git a/sdc/CycleAccting.cc b/sdc/CycleAccting.cc new file mode 100644 index 0000000..66a2e4b --- /dev/null +++ b/sdc/CycleAccting.cc @@ -0,0 +1,340 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include // ceil +#include // max +#include "Machine.hh" +#include "Debug.hh" +#include "Fuzzy.hh" +#include "Units.hh" +#include "TimingRole.hh" +#include "Clock.hh" +#include "CycleAccting.hh" + +namespace sta { + +CycleAccting::CycleAccting(const ClockEdge *src, + const ClockEdge *tgt) : + src_(src), + tgt_(tgt), + max_cycles_exceeded_(false) +{ + for (int i = 0; i <= TimingRole::index_max; i++) { + delay_[i] = MinMax::min()->initValue(); + required_[i] = 0; + src_cycle_[i] = 0; + tgt_cycle_[i] = 0; + } +} + +void +CycleAccting::findDelays(StaState *sta) +{ + Debug *debug = sta->debug(); + const Unit *time_unit = sta->units()->timeUnit(); + debugPrint2(debug, "cycle_acct", 1, "%s -> %s\n", + src_->name(), + tgt_->name()); + const int setup_index = TimingRole::setup()->index(); + const int latch_setup_index = TimingRole::latchSetup()->index(); + const int data_check_setup_index = TimingRole::dataCheckSetup()->index(); + const int hold_index = TimingRole::hold()->index(); + const int gclk_hold_index = TimingRole::gatedClockHold()->index(); + Clock *src_clk = src_->clock(); + Clock *tgt_clk = tgt_->clock(); + double tgt_opp_time1 = tgt_->opposite()->time(); + double tgt_period = tgt_clk->period(); + double src_period = src_clk->period(); + if (tgt_period > 0.0 && src_period > 0.0) { + // If the clocks are related (ie, generated clock and its source) allow + // allow enough cycles to match up the common period. + int tgt_max_cycle; + if (tgt_period > src_period) + tgt_max_cycle = 100; + else { + int ratio = std::ceil(src_period / tgt_period); + tgt_max_cycle = std::max(ratio, 1000); + } + bool tgt_past_src = false; + bool src_past_tgt = false; + int tgt_cycle, src_cycle; + for (tgt_cycle = (tgt_->time() < tgt_period) ? 0 : -1; + tgt_cycle <= tgt_max_cycle; + tgt_cycle++) { + double tgt_cycle_start = tgt_cycle * tgt_period; + double tgt_time = tgt_cycle_start + tgt_->time(); + double tgt_opp_time = tgt_cycle_start + tgt_opp_time1; + for (src_cycle = (src_->time() < src_period) ? 0 : -1; + ; + 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)) { + debugPrint2(debug, "cycle_acct", 1, " setup = %s, required = %s\n", + time_unit->asString(delay_[setup_index]), + time_unit->asString(required_[setup_index])); + debugPrint2(debug, "cycle_acct", 1, " hold = %s, required = %s\n", + time_unit->asString(delay_[hold_index]), + time_unit->asString(required_[hold_index])); + debugPrint2(debug, "cycle_acct", 1, + " converged at src cycles = %d tgt cycles = %d\n", + src_cycle, tgt_cycle); + return; + } + + if (fuzzyGreater(src_cycle_start, tgt_cycle_start + tgt_period) + && src_past_tgt) + break; + debugPrint5(debug, "cycle_acct", 2, " %s src cycle %d %s + %s = %s\n", + src_->name(), + src_cycle, + time_unit->asString(src_cycle_start), + time_unit->asString(src_->time()), + time_unit->asString(src_time)); + debugPrint5(debug, "cycle_acct", 2, " %s tgt cycle %d %s + %s = %s\n", + 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); + debugPrint2(debug, "cycle_acct", 2, + " setup min delay = %s, required = %s\n", + 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); + debugPrint2(debug, "cycle_acct", 2, + " latch setup min delay = %s, required = %s\n", + 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); + debugPrint2(debug, "cycle_acct", 2, + " hold min delay = %s, required = %s\n", + 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); + debugPrint2(debug, "cycle_acct", 2, + " gated clk hold min delay = %s, required = %s\n", + time_unit->asString(delay_[gclk_hold_index]), + time_unit->asString(required_[gclk_hold_index])); + } + } + } + } + max_cycles_exceeded_ = true; + debugPrint2(debug, "cycle_acct", 1, + " max cycles exceeded after %d src cycles, %d tgt_cycles\n", + src_cycle, tgt_cycle); + } + else if (tgt_period > 0.0) + findDefaultArrivalSrcDelays(); +} + +void +CycleAccting::setSetupAccting(int src_cycle, + 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); + setAccting(TimingRole::gatedClockSetup(), src_cycle, tgt_cycle, delay, req); + setAccting(TimingRole::recovery(), src_cycle, tgt_cycle, delay, req); +} + +void +CycleAccting::setHoldAccting(int src_cycle, + 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); + setAccting(TimingRole::removal(), src_cycle, tgt_cycle, delay, req); + setAccting(TimingRole::latchHold(), src_cycle, tgt_cycle, delay, req); +} + +void +CycleAccting::setAccting(TimingRole *role, + int src_cycle, + int tgt_cycle, + float delay, + float req) +{ + int index = role->index(); + src_cycle_[index] = src_cycle; + tgt_cycle_[index] = tgt_cycle; + delay_[index] = delay; + required_[index] = req; +} + +void +CycleAccting::findDefaultArrivalSrcDelays() +{ + const Clock *tgt_clk = tgt_->clock(); + float tgt_time = tgt_->time(); + float tgt_period = tgt_clk->period(); + // Unclocked arrival setup check is in cycle zero. + if (tgt_time > tgt_period) + setDefaultSetupAccting(0, 0, tgt_time - tgt_period, tgt_time - tgt_period); + else if (tgt_time > 0.0) + setDefaultSetupAccting(0, 0, tgt_time, tgt_time); + else + setDefaultSetupAccting(0, 1, tgt_period, tgt_period); + setDefaultHoldAccting(0, 0, 0.0, tgt_time); +} + +void +CycleAccting::setDefaultSetupAccting(int src_cycle, + int tgt_cycle, + float delay, + float req) +{ + setSetupAccting(src_cycle, tgt_cycle, delay, req); + setAccting(TimingRole::latchSetup(), src_cycle, tgt_cycle, delay, req); + setAccting(TimingRole::dataCheckSetup(), src_cycle, tgt_cycle, delay, req); +} + +void +CycleAccting::setDefaultHoldAccting(int src_cycle, + int tgt_cycle, + float delay, + float req) +{ + setHoldAccting(src_cycle, tgt_cycle, delay, req); + setAccting(TimingRole::dataCheckHold(), src_cycle, tgt_cycle, delay, req); +} + +float +CycleAccting::requiredTime(const TimingRole *check_role) +{ + return required_[check_role->index()]; +} + +float +CycleAccting::sourceTimeOffset(const TimingRole *check_role) +{ + return sourceCycle(check_role) * src_->clock()->period(); +} + +int +CycleAccting::sourceCycle(const TimingRole *check_role) +{ + return src_cycle_[check_role->index()]; +} + +int +CycleAccting::targetCycle(const TimingRole *check_role) +{ + return tgt_cycle_[check_role->index()]; +} + +float +CycleAccting::targetTimeOffset(const TimingRole *check_role) +{ + return targetCycle(check_role) * tgt_->clock()->period(); +} + +//////////////////////////////////////////////////////////////// + +bool +CycleAcctingLess::operator()(const CycleAccting *acct1, + 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()); +} + +size_t +CycleAcctingHash::operator()(const CycleAccting *acct) const +{ + return hashSum(acct->src()->index(), acct->target()->index()); +} + +bool +CycleAcctingEqual::operator()(const CycleAccting *acct1, + const CycleAccting *acct2) const +{ + return acct1->src() == acct2->src() + && acct1->target() == acct2->target(); +} + +} // namespace diff --git a/sdc/CycleAccting.hh b/sdc/CycleAccting.hh new file mode 100644 index 0000000..ea6dd25 --- /dev/null +++ b/sdc/CycleAccting.hh @@ -0,0 +1,105 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CYCLE_ACCTING_H +#define STA_CYCLE_ACCTING_H + +#include "DisallowCopyAssign.hh" +#include "MinMax.hh" +#include "StaState.hh" +#include "SdcClass.hh" +#include "TimingRole.hh" + +namespace sta { + +class CycleAccting +{ +public: + CycleAccting(const ClockEdge *src, + const ClockEdge *tgt); + // Fill in required times. + void findDelays(StaState *sta); + // Find delays when source clk edge is the default arrival clock edge + // (from unclocked set_input_delay). + void findDefaultArrivalSrcDelays(); + const ClockEdge *src() const { return src_; } + const ClockEdge *target() const { return tgt_; } + float requiredTime(const TimingRole *check_role); + int sourceCycle(const TimingRole *check_role); + int targetCycle(const TimingRole *check_role); + float sourceTimeOffset(const TimingRole *check_role); + float targetTimeOffset(const TimingRole *check_role); + bool maxCyclesExceeded() const { return max_cycles_exceeded_; } + +private: + DISALLOW_COPY_AND_ASSIGN(CycleAccting); + void setHoldAccting(int src_cycle, + int tgt_cycle, + float delay, + float req); + void setAccting(TimingRole *role, + int src_cycle, + int tgt_cycle, + float delay, + float req); + void setSetupAccting(int src_cycle, + int tgt_cycle, + float delay, + float req); + void setDefaultSetupAccting(int src_cycle, + int tgt_cycle, + float delay, + float req); + void setDefaultHoldAccting(int src_cycle, + int tgt_cycle, + float delay, + float req); + + const ClockEdge *src_; + const ClockEdge *tgt_; + // Setup/hold delay from source to target. + float delay_[TimingRole::index_max + 1]; + // Delay from beginning of src_cycle_'th cycle to target edge. + float required_[TimingRole::index_max + 1]; + // Source clock cycle offset. + int src_cycle_[TimingRole::index_max + 1]; + // Target clock cycle offset. + int tgt_cycle_[TimingRole::index_max + 1]; + bool max_cycles_exceeded_; +}; + +class CycleAcctingLess +{ +public: + bool operator()(const CycleAccting *acct1, + const CycleAccting *acct2) const; +}; + +class CycleAcctingHash +{ +public: + size_t operator()(const CycleAccting *acct) const; +}; + +class CycleAcctingEqual +{ +public: + bool operator()(const CycleAccting *acct1, + const CycleAccting *acct2) const; +}; + +} // namespace +#endif diff --git a/sdc/DataCheck.cc b/sdc/DataCheck.cc new file mode 100644 index 0000000..70edf31 --- /dev/null +++ b/sdc/DataCheck.cc @@ -0,0 +1,115 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Clock.hh" +#include "DataCheck.hh" + +namespace sta { + +DataCheck::DataCheck(Pin *from, + Pin *to, + Clock *clk) : + from_(from), + to_(to), + clk_(clk) +{ +} + +void +DataCheck::margin(const TransRiseFall *from_tr, + const TransRiseFall *to_tr, + const SetupHold *setup_hold, + // Return values. + float &margin, + bool &exists) const +{ + return margins_[from_tr->index()].value(to_tr, setup_hold, + margin, exists); +} + +void +DataCheck::setMargin(const TransRiseFallBoth *from_tr, + const TransRiseFallBoth *to_tr, + const SetupHoldAll *setup_hold, + float margin) +{ + for (auto from_tr_index : from_tr->rangeIndex()) + margins_[from_tr_index].setValue(to_tr, setup_hold, margin); +} + +void +DataCheck::removeMargin(const TransRiseFallBoth *from_tr, + const TransRiseFallBoth *to_tr, + const SetupHoldAll *setup_hold) +{ + for (auto from_tr_index : from_tr->rangeIndex()) + margins_[from_tr_index].removeValue(to_tr, setup_hold); +} + +bool +DataCheck::empty() const +{ + for (auto tr_index : TransRiseFall::rangeIndex()) { + if (!margins_[tr_index].empty()) + return false; + } + return true; +} + +void +DataCheck::marginIsOneValue(SetupHold *setup_hold, + // Return values. + float &value, + bool &one_value) const +{ + float value1, value2; + if (margins_[TransRiseFall::riseIndex()].isOneValue(setup_hold, value1) + && margins_[TransRiseFall::fallIndex()].isOneValue(setup_hold, value2) + && value1 == value2) { + value = value1; + one_value = true; + } + else + one_value = false; +} + +//////////////////////////////////////////////////////////////// + +DataCheckLess::DataCheckLess(const Network *network) : + pin_less_(network) +{ +} + +bool +DataCheckLess::operator()(const DataCheck *check1, + const DataCheck *check2) const +{ + const Pin *from1 = check1->from(); + const Pin *from2 = check2->from(); + const Pin *to1 = check1->to(); + const Pin *to2 = check2->to(); + const Clock *clk1 = check1->clk(); + const Clock *clk2 = check2->clk(); + int clk_cmp = clkCmp(clk1, clk2); + return pin_less_(from1, from2) + || (from1 == from2 + && (pin_less_(to1, to2) + || (to1 == to2 + && clk_cmp < 0))); +} + +} // namespace diff --git a/sdc/DataCheck.hh b/sdc/DataCheck.hh new file mode 100644 index 0000000..2a1574f --- /dev/null +++ b/sdc/DataCheck.hh @@ -0,0 +1,81 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_DATA_CHECK_H +#define STA_DATA_CHECK_H + +#include "DisallowCopyAssign.hh" +#include "MinMax.hh" +#include "RiseFallMinMax.hh" +#include "LibertyClass.hh" +#include "NetworkClass.hh" +#include "NetworkCmp.hh" +#include "SdcClass.hh" + +namespace sta { + +class PinPathNameLess; + +class DataCheck +{ +public: + DataCheck(Pin *from, + Pin *to, + Clock *clk); + Pin *from() const { return from_; } + Pin *to() const { return to_; } + Clock *clk() const { return clk_; } + void margin(const TransRiseFall *from_tr, + const TransRiseFall *to_tr, + const SetupHold *setup_hold, + // Return values. + float &margin, + bool &exists) const; + void setMargin(const TransRiseFallBoth *from_tr, + const TransRiseFallBoth *to_tr, + const SetupHoldAll *setup_hold, + float margin); + void removeMargin(const TransRiseFallBoth *from_tr, + const TransRiseFallBoth *to_tr, + const SetupHoldAll *setup_hold); + bool empty() const; + void marginIsOneValue(SetupHold *setup_hold, + // Return values. + float &value, + bool &one_value) const; + +private: + DISALLOW_COPY_AND_ASSIGN(DataCheck); + + Pin *from_; + Pin *to_; + Clock *clk_; + RiseFallMinMax margins_[TransRiseFall::index_count]; +}; + +class DataCheckLess +{ +public: + explicit DataCheckLess(const Network *network); + bool operator()(const DataCheck *check1, + const DataCheck *check2) const; + +private: + PinPathNameLess pin_less_; +}; + +} // namespace +#endif diff --git a/sdc/DeratingFactors.cc b/sdc/DeratingFactors.cc new file mode 100644 index 0000000..a9259c9 --- /dev/null +++ b/sdc/DeratingFactors.cc @@ -0,0 +1,196 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "DeratingFactors.hh" + +namespace sta { + +inline int +TimingDerateIndex(TimingDerateType type) +{ + return int(type); +} + +DeratingFactors::DeratingFactors() +{ + clear(); +} + +void +DeratingFactors::setFactor(PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float factor) +{ + for (auto tr1 : tr->range()) + factors_[int(clk_data)].setValue(tr1, early_late, factor); +} + +void +DeratingFactors::factor(PathClkOrData clk_data, + const TransRiseFall *tr, + const EarlyLate *early_late, + float &factor, + bool &exists) const +{ + factors_[int(clk_data)].value(tr, 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(); +} + +void +DeratingFactors::isOneValue(const EarlyLate *early_late, + bool &is_one_value, + float &value) const +{ + bool is_one_value0, is_one_value1; + float value0, value1; + is_one_value0 = factors_[0].isOneValue(early_late, value0); + is_one_value1 = factors_[1].isOneValue(early_late, value1); + is_one_value = is_one_value0 + && is_one_value1 + && value0 == value1; + value = value1; +} + +void +DeratingFactors::isOneValue(PathClkOrData clk_data, + const EarlyLate *early_late, + bool &is_one_value, + float &value) const +{ + is_one_value = factors_[int(clk_data)].isOneValue(early_late, value); +} + +bool +DeratingFactors::hasValue() const +{ + return factors_[0].hasValue() + || factors_[1].hasValue(); +} + +//////////////////////////////////////////////////////////////// + +DeratingFactorsGlobal::DeratingFactorsGlobal() +{ + clear(); +} + +void +DeratingFactorsGlobal::setFactor(TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float factor) +{ + factors_[TimingDerateIndex(type)].setFactor(clk_data, tr, early_late, factor); +} + +void +DeratingFactorsGlobal::factor(TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFall *tr, + const EarlyLate *early_late, + float &factor, + bool &exists) const +{ + factors_[TimingDerateIndex(type)].factor(clk_data, tr, early_late, factor, exists); +} + +void +DeratingFactorsGlobal::clear() +{ + for (int type = 0; type < timing_derate_type_count; type++) + factors_[type].clear(); +} + +DeratingFactors * +DeratingFactorsGlobal::factors(TimingDerateType type) +{ + return &factors_[TimingDerateIndex(type)]; +} + +//////////////////////////////////////////////////////////////// + +DeratingFactorsCell::DeratingFactorsCell() +{ + clear(); +} + +void +DeratingFactorsCell::setFactor(TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float factor) +{ + factors_[TimingDerateIndex(type)].setFactor(clk_data, tr, early_late, factor); +} + +void +DeratingFactorsCell::factor(TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFall *tr, + const EarlyLate *early_late, + float &factor, + bool &exists) const +{ + factors_[TimingDerateIndex(type)].factor(clk_data, tr, early_late, factor, exists); +} + +void +DeratingFactorsCell::clear() +{ + for (int type = 0; type < timing_derate_cell_type_count; type++) + factors_[type].clear(); +} + +DeratingFactors * +DeratingFactorsCell::factors(TimingDerateType type) +{ + return &factors_[TimingDerateIndex(type)]; +} + +void +DeratingFactorsCell::isOneValue(const EarlyLate *early_late, + bool &is_one_value, + float &value) const +{ + bool is_one_value1, is_one_value2; + float value1, value2; + factors_[TimingDerateIndex(TimingDerateType::cell_delay)] + .isOneValue(early_late, is_one_value1, value1); + factors_[TimingDerateIndex(TimingDerateType::cell_check)] + .isOneValue(early_late, is_one_value2, value2); + is_one_value = is_one_value1 + && is_one_value2 + && value1 == value2; + value = value1; +} + +//////////////////////////////////////////////////////////////// + +DeratingFactorsNet::DeratingFactorsNet() +{ +} + +} // namespace diff --git a/sdc/DeratingFactors.hh b/sdc/DeratingFactors.hh new file mode 100644 index 0000000..16f2703 --- /dev/null +++ b/sdc/DeratingFactors.hh @@ -0,0 +1,118 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_DERATING_FACTORS_H +#define STA_DERATING_FACTORS_H + +#include "DisallowCopyAssign.hh" +#include "LibertyClass.hh" +#include "SdcClass.hh" +#include "MinMax.hh" +#include "RiseFallMinMax.hh" + +namespace sta { + +class DeratingFactors +{ +public: + DeratingFactors(); + void setFactor(PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float factor); + void factor(PathClkOrData clk_data, + const TransRiseFall *tr, + const EarlyLate *early_late, + float &factor, + bool &exists) const; + void clear(); + void isOneValue(const EarlyLate *early_late, + bool &is_one_value, + float &value) const; + void isOneValue(PathClkOrData clk_data, + const EarlyLate *early_late, + bool &is_one_value, + float &value) const; + bool hasValue() const; + +private: + DISALLOW_COPY_AND_ASSIGN(DeratingFactors); + + RiseFallMinMax factors_[path_clk_or_data_count]; +}; + +class DeratingFactorsGlobal +{ +public: + DeratingFactorsGlobal(); + void setFactor(TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float factor); + void factor(TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFall *tr, + const EarlyLate *early_late, + float &factor, + bool &exists) const; + DeratingFactors *factors(TimingDerateType type); + void clear(); + +private: + DISALLOW_COPY_AND_ASSIGN(DeratingFactorsGlobal); + + DeratingFactors factors_[timing_derate_type_count]; +}; + +class DeratingFactorsCell +{ +public: + DeratingFactorsCell(); + void setFactor(TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float factor); + void factor(TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFall *tr, + const EarlyLate *early_late, + float &factor, + bool &exists) const; + DeratingFactors *factors(TimingDerateType type); + void clear(); + void isOneValue(const EarlyLate *early_late, + bool &is_one_value, + float &value) const; + +private: + DISALLOW_COPY_AND_ASSIGN(DeratingFactorsCell); + + DeratingFactors factors_[timing_derate_cell_type_count]; +}; + +class DeratingFactorsNet : public DeratingFactors +{ +public: + DeratingFactorsNet(); + +private: + DISALLOW_COPY_AND_ASSIGN(DeratingFactorsNet); +}; + +} // namespace +#endif diff --git a/sdc/DisabledPorts.cc b/sdc/DisabledPorts.cc new file mode 100644 index 0000000..4092226 --- /dev/null +++ b/sdc/DisabledPorts.cc @@ -0,0 +1,265 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "StringUtil.hh" +#include "TimingRole.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "DisabledPorts.hh" + +namespace sta { + +DisabledPorts::DisabledPorts() : + all_(false), + from_(nullptr), + to_(nullptr), + from_to_(nullptr) +{ +} + +DisabledPorts::~DisabledPorts() +{ + delete from_; + delete to_; + if (from_to_) { + from_to_->deleteContents(); + delete from_to_; + } +} + +void +DisabledPorts::setDisabledAll() +{ + all_ = true; +} + +void +DisabledPorts::removeDisabledAll() +{ + all_ = false; +} + +void +DisabledPorts::setDisabledFrom(LibertyPort *port) +{ + if (from_ == nullptr) + from_ = new LibertyPortSet; + from_->insert(port); +} + +void +DisabledPorts::removeDisabledFrom(LibertyPort *port) +{ + if (from_) + from_->erase(port); +} + +void +DisabledPorts::setDisabledTo(LibertyPort *port) +{ + if (to_ == nullptr) + to_ = new LibertyPortSet; + to_->insert(port); +} + +void +DisabledPorts::removeDisabledTo(LibertyPort *port) +{ + if (to_) + to_->erase(port); +} + +void +DisabledPorts::setDisabledFromTo(LibertyPort *from, LibertyPort *to) +{ + if (from_to_ == nullptr) + from_to_ = new LibertyPortPairSet; + LibertyPortPair pair(from, to); + if (!from_to_->hasKey(&pair)) { + LibertyPortPair *pair = new LibertyPortPair(from, to); + from_to_->insert(pair); + } +} + +void +DisabledPorts::removeDisabledFromTo(LibertyPort *from, LibertyPort *to) +{ + if (from_to_) { + LibertyPortPair probe(from, to); + LibertyPortPair *pair = from_to_->findKey(&probe); + if (pair) { + from_to_->erase(pair); + delete pair; + } + } +} + +bool +DisabledPorts::isDisabled(LibertyPort *from, LibertyPort *to, + const TimingRole *role) +{ + LibertyPortPair pair(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(&pair)); +} + +//////////////////////////////////////////////////////////////// + +DisabledCellPorts::DisabledCellPorts(LibertyCell *cell) : + DisabledPorts(), + cell_(cell), + arc_sets_(nullptr) +{ +} + + +DisabledCellPorts::~DisabledCellPorts() +{ + delete arc_sets_; +} + +void +DisabledCellPorts::setDisabled(TimingArcSet *arc_set) +{ + if (arc_sets_ == nullptr) + arc_sets_ = new TimingArcSetSet; + arc_sets_->insert(arc_set); +} + +void +DisabledCellPorts::removeDisabled(TimingArcSet *arc_set) +{ + if (arc_sets_) + arc_sets_->erase(arc_set); +} + +bool +DisabledCellPorts::isDisabled(TimingArcSet *arc_set) const +{ + return arc_sets_ + && arc_sets_->hasKey(arc_set); +} + +class DisabledCellPortsLess +{ +public: + DisabledCellPortsLess(); + bool operator()(const DisabledCellPorts *disable1, + const DisabledCellPorts *disable2); +}; + +DisabledCellPortsLess::DisabledCellPortsLess() +{ +} + +bool +DisabledCellPortsLess::operator()(const DisabledCellPorts *disable1, + const DisabledCellPorts *disable2) +{ + return stringLess(disable1->cell()->name(), + disable2->cell()->name()); +} + +void +sortDisabledCellPortsMap(DisabledCellPortsMap *cell_map, + DisabledCellPortsSeq &disables) +{ + DisabledCellPortsMap::Iterator disabled_iter(cell_map); + while (disabled_iter.hasNext()) + disables.push_back(disabled_iter.next()); + sort(disables, DisabledCellPortsLess()); +} + +//////////////////////////////////////////////////////////////// + +DisabledInstancePorts::DisabledInstancePorts(Instance *inst) : + DisabledPorts(), + inst_(inst) +{ +} + +class DisabledInstPortsLess +{ +public: + explicit DisabledInstPortsLess(Network *network); + bool operator()(const DisabledInstancePorts *disable1, + const DisabledInstancePorts *disable2); + +private: + Network *network_; +}; + +DisabledInstPortsLess::DisabledInstPortsLess(Network *network) : + network_(network) +{ +} + +bool +DisabledInstPortsLess::operator()(const DisabledInstancePorts *disable1, + const DisabledInstancePorts *disable2) +{ + return stringLess(network_->pathName(disable1->instance()), + network_->pathName(disable2->instance())); +} + +void +sortDisabledInstancePortsMap(DisabledInstancePortsMap *inst_map, + Network *network, + DisabledInstancePortsSeq &disables) +{ + DisabledInstancePortsMap::Iterator disabled_iter(inst_map); + while (disabled_iter.hasNext()) + disables.push_back(disabled_iter.next()); + sort(disables, DisabledInstPortsLess(network)); +} + +//////////////////////////////////////////////////////////////// + +class LibertyPortPairNameLess +{ +public: + bool operator()(const LibertyPortPair *pair1, + const LibertyPortPair *pair2); +}; + +bool +LibertyPortPairNameLess::operator()(const LibertyPortPair *pair1, + 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)); +} + +void +sortLibertyPortPairSet(LibertyPortPairSet *sets, + LibertyPortPairSeq &pairs) +{ + LibertyPortPairSet::Iterator pair_iter(sets); + while (pair_iter.hasNext()) + pairs.push_back(pair_iter.next()); + sort(pairs, LibertyPortPairNameLess()); +} + +} diff --git a/sdc/DisabledPorts.hh b/sdc/DisabledPorts.hh new file mode 100644 index 0000000..c1b7734 --- /dev/null +++ b/sdc/DisabledPorts.hh @@ -0,0 +1,112 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_DISABLED_PORTS_H +#define STA_DISABLED_PORTS_H + +#include "DisallowCopyAssign.hh" +#include "Map.hh" +#include "NetworkClass.hh" +#include "LibertyClass.hh" +#include "SdcClass.hh" + +namespace sta { + +class TimingRole; +class DisabledCellPorts; +class DisabledInstancePorts; + +typedef Vector DisabledInstancePortsSeq; +typedef Vector DisabledCellPortsSeq; +typedef Vector LibertyPortPairSeq; +typedef Set TimingArcSetSet; + +// Base class for disabled cell and instance ports. +class DisabledPorts +{ +public: + DisabledPorts(); + ~DisabledPorts(); + void setDisabledAll(); + void removeDisabledAll(); + void setDisabledFrom(LibertyPort *port); + void removeDisabledFrom(LibertyPort *port); + void setDisabledTo(LibertyPort *port); + void removeDisabledTo(LibertyPort *port); + void setDisabledFromTo(LibertyPort *from, LibertyPort *to); + void removeDisabledFromTo(LibertyPort *from, LibertyPort *to); + 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_; } + +private: + DISALLOW_COPY_AND_ASSIGN(DisabledPorts); + + bool all_; + LibertyPortSet *from_; + LibertyPortSet *to_; + LibertyPortPairSet *from_to_; +}; + +// set_disable_timing cell [-from] [-to] +class DisabledCellPorts : public DisabledPorts +{ +public: + DisabledCellPorts(LibertyCell *cell); + ~DisabledCellPorts(); + LibertyCell *cell() const { return cell_; } + void setDisabled(TimingArcSet *arc_set); + void removeDisabled(TimingArcSet *arc_set); + bool isDisabled(TimingArcSet *arc_set) const; + TimingArcSetSet *timingArcSets() const { return arc_sets_; } + + using DisabledPorts::isDisabled; + +private: + DISALLOW_COPY_AND_ASSIGN(DisabledCellPorts); + + LibertyCell *cell_; + TimingArcSetSet *arc_sets_; +}; + +// set_disable_timing instance [-from] [-to] +class DisabledInstancePorts : public DisabledPorts +{ +public: + DisabledInstancePorts(Instance *inst); + Instance *instance() const { return inst_; } + +private: + DISALLOW_COPY_AND_ASSIGN(DisabledInstancePorts); + + Instance *inst_; +}; + +void +sortDisabledCellPortsMap(DisabledCellPortsMap *cell_map, + DisabledCellPortsSeq &disables); +void +sortDisabledInstancePortsMap(DisabledInstancePortsMap *inst_map, + Network *network, + DisabledInstancePortsSeq &disables); +void +sortLibertyPortPairSet(LibertyPortPairSet *sets, + LibertyPortPairSeq &pairs); + +} // namespace +#endif diff --git a/sdc/ExceptionPath.cc b/sdc/ExceptionPath.cc new file mode 100644 index 0000000..8703744 --- /dev/null +++ b/sdc/ExceptionPath.cc @@ -0,0 +1,2534 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "Units.hh" +#include "Transition.hh" +#include "PortDirection.hh" +#include "TimingRole.hh" +#include "MinMax.hh" +#include "Network.hh" +#include "NetworkCmp.hh" +#include "Clock.hh" +#include "ExceptionPath.hh" + +namespace sta { + +static bool +thrusIntersectPts(ExceptionThruSeq *thrus1, + ExceptionThruSeq *thrus2); +static void +insertPinPairsThruHierPin(const Pin *hpin, + const Network *network, + PinPairSet *pairs); +static void +insertPinPairsThruNet(Net *net, + const Network *network, + PinPairSet *pairs); +static void +deletePinPairsThruHierPin(const Pin *hpin, + const Network *network, + PinPairSet *pairs); +static int +setNameCmp(PinSet *set1, + 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()) { + Pin *pin1 = iter1.next(); + Pin *pin2 = iter2.next(); + const char *name1 = network->pathName(pin1); + const char *name2 = network->pathName(pin2); + int cmp = strcmp(name1, name2); + if (cmp != 0) + return cmp; + } + // Sets are equal. + return 0; + } + else + return (size1 > size2) ? 1 : -1; +} + +static int +setNameCmp(ClockSet *set1, + 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 cmp = clk1->index() - clk2->index(); + if (cmp != 0) + return cmp; + } + // Sets are equal. + return 0; + } + else + return (size1 > size2) ? 1 : -1; +} + +static int +setNameCmp(InstanceSet *set1, + 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()) { + Instance *inst1 = iter1.next(); + Instance *inst2 = iter2.next(); + const char *name1 = network->pathName(inst1); + const char *name2 = network->pathName(inst2); + int cmp = strcmp(name1, name2); + if (cmp != 0) + return cmp; + } + // Sets are equal. + return 0; + } + else + return (size1 > size2) ? 1 : -1; +} + +static int +setNameCmp(NetSet *set1, + 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()) { + Net *net1 = iter1.next(); + Net *net2 = iter2.next(); + const char *name1 = network->pathName(net1); + const char *name2 = network->pathName(net2); + int cmp = strcmp(name1, name2); + if (cmp != 0) + return cmp; + } + // Sets are equal. + return 0; + } + else + return (size1 > size2) ? 1 : -1; +} + +//////////////////////////////////////////////////////////////// + +const char * +EmptyExpceptionPt::what() const throw() +{ + return "empty exception from/through/to."; +} + +void +checkFromThrusTo(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to) +{ + bool found_empty = ((from && !from->hasObjects()) + || (to + && (!to->hasObjects() + && to->transition() + == TransRiseFallBoth::riseFall() + && (to->endTransition() + == TransRiseFallBoth::riseFall())))); + if (thrus) { + ExceptionThruSeq::Iterator thru_iter(thrus); + while (thru_iter.hasNext()) { + ExceptionThru *thru = thru_iter.next(); + if (!thru->hasObjects()) + found_empty = true; + } + } + if (found_empty) + throw EmptyExpceptionPt(); +} + +ExceptionPath::ExceptionPath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + int priority, + const char *comment) : + SdcCmdComment(comment), + from_(from), + thrus_(thrus), + to_(to), + min_max_(min_max), + own_pts_(own_pts), + priority_(priority) +{ + makeStates(); +} + +ExceptionPath::~ExceptionPath() +{ + if (own_pts_) { + delete from_; + delete to_; + if (thrus_) { + thrus_->deleteContents(); + delete thrus_; + } + } + ExceptionState *state, *next_state; + for (state = states_; state; state = next_state) { + next_state = state->nextState(); + delete state; + } +} + +const char * +ExceptionPath::asString(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; +} + +ExceptionPt * +ExceptionPath::firstPt() +{ + if (from_) + return from_; + else if (thrus_ && !thrus_->empty()) + return (*thrus_)[0]; + else if (to_) + return to_; + else + return nullptr; +} + +bool +ExceptionPath::matchesFirstPt(const TransRiseFall *to_tr, + const MinMax *min_max) +{ + ExceptionPt *first_pt = firstPt(); + return first_pt->transition()->matches(to_tr) + && matches(min_max, false); +} + +bool +ExceptionPath::matches(const MinMax *min_max, + bool) const +{ + return min_max_->matches(min_max); +} + +void +ExceptionPath::setPriority(int priority) +{ + priority_ = priority; +} + +//////////////////////////////////////////////////////////////// +// Exception precedence relative to from/thru/to pins/clocks: +// Priority order: +// 1) -from pin/instance/port +// 2) -to pin/instance/port +// 3) -through pin +// 4) -from clock +// 5) -to clock +// +// Foreach priority level (from 1 to 5) +// If the exception has this type of qualifier, it takes +// priority over an exception without this type of qualifier. +int +ExceptionPath::fromThruToPriority(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to) +{ + int priority = 0; + if (from && (from->hasPins() || from->hasInstances())) + priority |= (1 << 6); + if (to && (to->hasPins() || to->hasInstances())) + priority |= (1 << 5); + if (thrus && !thrus->empty()) + priority |= (1 << 4); + if (from && from->hasClocks()) + priority |= (1 << 3); + if (to && to->hasClocks()) + priority |= (1 << 2); + // Leave room for minMaxPriority() which uses bits 0 and 1. + return priority; +} + +size_t +ExceptionPath::hash() const +{ + return hash(nullptr); +} + +size_t +ExceptionPath::hash(ExceptionPt *missing_pt) const +{ + size_t hash = typePriority(); + int pot = 32; + ExceptionPtIterator pt_iter(this); + while (pt_iter.hasNext()) { + ExceptionPt *pt = pt_iter.next(); + if (pt != missing_pt) + hash += pt->hash() * (pot - 1); + pot *= 2; + } + return hash; +} + +bool +ExceptionPath::mergeablePts(ExceptionPath *exception) const +{ + ExceptionPt *ignore; + return mergeablePts(exception, nullptr, ignore); +} + +bool +ExceptionPath::mergeablePts(ExceptionPath *exception2, + 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)))) + || (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)))) + 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_ && to2 == nullptr) + || (to_ == nullptr && to2)) + return false; + if (to2 == missing_pt2) + missing_pt = to_; + return true; +} + +bool +ExceptionPath::intersectsPts(ExceptionPath *exception) const +{ + ExceptionFrom *from2 = exception->from(); + ExceptionThruSeq *thrus2 = exception->thrus(); + ExceptionTo *to2 = exception->to(); + if (((from_ == nullptr && from2 == nullptr) + || (from_ && from2 && from_->intersectsPts(from2))) + && ((thrus_ == nullptr && thrus2 == nullptr) + || (thrus_ && thrus2 && thrus_->size() == thrus2->size())) + && ((to_ == nullptr && to2 == nullptr) + || (to_ && to2 && to_->intersectsPts(to2)))) { + 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)) + return false; + } + return true; + } + return false; +} + +const char * +ExceptionPath::fromThruToString(const Network *network) const +{ + string str; + if (min_max_ != MinMaxAll::all()) { + str += " -"; + str += min_max_->asString(); + } + + if (from_) + str += from_->asString(network); + + if (thrus_) { + str += " -thru"; + bool first_thru = true; + ExceptionThruSeq::Iterator thru_iter(thrus_); + while (thru_iter.hasNext()) { + ExceptionThru *thru = thru_iter.next(); + if (!first_thru) + str += " &&"; + str += " {"; + str += thru->asString(network); + str += "}"; + first_thru = false; + } + } + + if (to_) + str += to_->asString(network); + + char *result = makeTmpString(str.size() + 1); + strcpy(result, str.c_str()); + return result; +} + +ExceptionState * +ExceptionPath::firstState() +{ + return states_; +} + +void +ExceptionPath::makeStates() +{ + if (thrus_) { + ExceptionState *prev_state = nullptr; + ExceptionThruSeq::Iterator thru_iter(thrus_); + bool first = true; + int index = 0; + while (thru_iter.hasNext()) { + ExceptionThru *thru = thru_iter.next(); + // No state for first -thru if no -from,since it kicks off the exception. + if (!(from_ == nullptr && first)) { + ExceptionState *state = new ExceptionState(this, thru, index); + if (prev_state) + prev_state->setNextState(state); + else + states_ = state; + prev_state = state; + } + first = false; + index++; + } + // Last state indicates all the thrus have been traversed. + ExceptionState *state = new ExceptionState(this, nullptr, index); + if (prev_state) + prev_state->setNextState(state); + else + states_ = state; + } + else + states_ = new ExceptionState(this, nullptr, 0); +} + +bool +ExceptionPath::resetMatch(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max) +{ + // Only the reset expception points need to match. + // For example, if the reset is -from, it matches any + // exceptions that match the -from even if they are more specific. + // -from + return ((from && from_ + && thrus == nullptr + && to == nullptr + && from_->intersectsPts(from)) + // -thru + || (from == nullptr + && thrus && thrus_ + && to == nullptr + && thrusIntersectPts(thrus_, thrus)) + // -to + || (from == nullptr + && thrus == nullptr + && to && to_ + && to_->intersectsPts(to)) + // -from -thru + || (from && from_ + && thrus && thrus_ + && to == nullptr + && from_->intersectsPts(from) + && thrusIntersectPts(thrus_, thrus)) + // -from -to + || (from && from_ + && thrus == nullptr + && to && to_ + && from_->intersectsPts(from) + && to_->intersectsPts(to)) + // -thru -to + || (from == nullptr + && thrus && thrus_ + && to && to_ + && thrusIntersectPts(thrus_, thrus) + && to_->intersectsPts(to)) + // -from -thru -to + || (from && from_ + && thrus && thrus_ + && to && to_ + && from_->intersectsPts(from) + && thrusIntersectPts(thrus_, thrus) + && to_->intersectsPts(to))) + && (min_max == MinMaxAll::all() + || min_max_ == min_max); +} + +static bool +thrusIntersectPts(ExceptionThruSeq *thrus1, + ExceptionThruSeq *thrus2) +{ + 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(); + if (!thru1->intersectsPts(thru2)) + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////// + +PathDelay::PathDelay(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, + float delay, + bool own_pts, + const char *comment) : + ExceptionPath(from, thrus, to, min_max->asMinMaxAll(), own_pts, + pathDelayPriority() + fromThruToPriority(from, thrus, to), + comment), + ignore_clk_latency_(ignore_clk_latency), + delay_(delay) +{ +} + +ExceptionPath * +PathDelay::clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) +{ + return new PathDelay(from, thrus, to, min_max_->asMinMax(), + ignore_clk_latency_, delay_, own_pts, + comment_); +} + +int +PathDelay::typePriority() const +{ + return pathDelayPriority(); +} + +bool +PathDelay::tighterThan(ExceptionPath *exception) const +{ + if (min_max_->asMinMax() == MinMax::min()) + return delay_ > exception->delay(); + else + return delay_ < exception->delay(); +} + +const char * +PathDelay::asString(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; +} + +const char * +PathDelay::typeString() const +{ + return "Path"; +} + +bool +PathDelay::mergeable(ExceptionPath *exception) const +{ + return overrides(exception) + && exception->ignoreClkLatency() == ignore_clk_latency_ + && exception->delay() == delay_; +} + +bool +PathDelay::overrides(ExceptionPath *exception) const +{ + return exception->isPathDelay() + && exception->priority() == priority_ + && exception->minMax() == min_max_; +} + +//////////////////////////////////////////////////////////////// + +FalsePath::FalsePath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + const char *comment) : + ExceptionPath(from, thrus, to, min_max, own_pts, + 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) : + ExceptionPath(from, thrus, to, min_max, own_pts, priority, comment) +{ +} + +ExceptionPath * +FalsePath::clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) +{ + return new FalsePath(from, thrus, to, min_max_, own_pts, comment_); +} + +int +FalsePath::typePriority() const +{ + return falsePathPriority(); +} + +bool +FalsePath::tighterThan(ExceptionPath *) const +{ + return false; +} + +const char * +FalsePath::typeString() const +{ + return "False"; +} + +bool +FalsePath::mergeable(ExceptionPath *exception) const +{ + return exception->isFalse() + && exception->priority() == priority() + && exception->minMax() == min_max_; +} + +bool +FalsePath::overrides(ExceptionPath *exception) const +{ + return mergeable(exception); +} + +//////////////////////////////////////////////////////////////// + +LoopPath::LoopPath(ExceptionThruSeq *thrus, bool own_pts) : + FalsePath(nullptr, thrus, nullptr, MinMaxAll::all(), own_pts, + falsePathPriority() + fromThruToPriority(nullptr, thrus, nullptr), + nullptr) +{ +} + +const char * +LoopPath::typeString() const +{ + return "Loop"; +} + +bool +LoopPath::mergeable(ExceptionPath *) const +{ + return false; +} + +//////////////////////////////////////////////////////////////// + +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) : + ExceptionPath(from, thrus, to, min_max, own_pts, + multiCyclePathPriority() + fromThruToPriority(from, thrus, to), + comment), + use_end_clk_(use_end_clk), + path_multiplier_(path_multiplier) +{ +} + +ExceptionPath * +MultiCyclePath::clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) +{ + return new MultiCyclePath(from, thrus, to, min_max_, use_end_clk_, + path_multiplier_, own_pts, comment_); +} + +int +MultiCyclePath::pathMultiplier(const MinMax *min_max) const +{ + if (min_max_ == MinMaxAll::all() + && min_max == MinMax::min()) + // Path multiplier is zero if no -setup/-hold is specified. + return 0; + else + return path_multiplier_; +} + +int +MultiCyclePath::priority(const MinMax *min_max) const +{ + if (min_max_ == MinMaxAll::all()) + return priority_ + 1; + else if (min_max_->asMinMax() == min_max) + return priority_ + 2; + else + return priority_; +} + +int +MultiCyclePath::typePriority() const +{ + return multiCyclePathPriority(); +} + +bool +MultiCyclePath::tighterThan(ExceptionPath *exception) const +{ + return path_multiplier_ < exception->pathMultiplier(); +} + +bool +MultiCyclePath::matches(const MinMax *min_max, + bool exactly) const +{ + return min_max_->matches(min_max) + // set_multicycle_path -setup determines hold check accounting, + // so they must be propagated for min (hold) paths. + || (!exactly && min_max == MinMax::min()); +} + +const char * +MultiCyclePath::asString(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; +} + +const char * +MultiCyclePath::typeString() const +{ + return "Multicycle"; +} + +bool +MultiCyclePath::mergeable(ExceptionPath *exception) const +{ + return overrides(exception) + && exception->pathMultiplier() == path_multiplier_; +} + +bool +MultiCyclePath::overrides(ExceptionPath *exception) const +{ + return exception->isMultiCycle() + && exception->priority() == priority() + && exception->minMax() == min_max_; +} + +//////////////////////////////////////////////////////////////// + +FilterPath::FilterPath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) : + ExceptionPath(from, thrus, to, MinMaxAll::all(), own_pts, + filterPathPriority() + fromThruToPriority(from, thrus, to), + nullptr) +{ +} + +const char * +FilterPath::typeString() const +{ + return "Filter"; +} + +ExceptionPath * +FilterPath::clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) +{ + return new FilterPath(from, thrus, to, own_pts); +} + +int +FilterPath::typePriority() const +{ + return filterPathPriority(); +} + +bool +FilterPath::tighterThan(ExceptionPath *) const +{ + return false; +} + +// Filter paths are used for report -from/-thru/-to as well as +// generated clock insertion delays so do not let them merge. +bool +FilterPath::mergeable(ExceptionPath *) const +{ + return false; +} + +bool +FilterPath::overrides(ExceptionPath *) const +{ + return false; +} + +bool +FilterPath::resetMatch(ExceptionFrom *, + ExceptionThruSeq *, + ExceptionTo *, + const MinMaxAll *) +{ + return false; +} + +//////////////////////////////////////////////////////////////// + +GroupPath::GroupPath(const char *name, + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts, + const char *comment) : + ExceptionPath(from, thrus, to, MinMaxAll::all(), own_pts, + groupPathPriority() + fromThruToPriority(from, thrus, to), + comment), + name_(stringCopy(name)), + is_default_(is_default) +{ +} + +GroupPath::~GroupPath() +{ + stringDelete(name_); +} + +const char * +GroupPath::typeString() const +{ + return "Group"; +} + +ExceptionPath * +GroupPath::clone(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool own_pts) +{ + return new GroupPath(name_, is_default_, from, thrus, to, own_pts, + comment_); +} + +int +GroupPath::typePriority() const +{ + return groupPathPriority(); +} + +bool +GroupPath::tighterThan(ExceptionPath *) const +{ + return false; +} + +bool +GroupPath::mergeable(ExceptionPath *exception) const +{ + return exception->isGroupPath() + && is_default_ == exception->isDefault() + && (name_ && exception->name() && stringEq(name_, exception->name())); +} + +bool +GroupPath::overrides(ExceptionPath *exception) const +{ + return mergeable(exception); +} + +//////////////////////////////////////////////////////////////// + +const int ExceptionPt::as_string_max_objects_ = 20; + +ExceptionPt::ExceptionPt(const TransRiseFallBoth *tr, + bool own_pts) : + tr_(tr), + own_pts_(own_pts), + hash_(0) +{ +} + +// ExceptionPt initialization functions set hash_ and incrementally +// maintain the value. +size_t +ExceptionPt::hash() const +{ + return hash_; +} + +ExceptionFromTo::ExceptionFromTo(PinSet *pins, + ClockSet *clks, + InstanceSet *insts, + const TransRiseFallBoth *tr, + bool own_pts) : + ExceptionPt(tr, own_pts), + pins_(pins), + clks_(clks), + insts_(insts) +{ + // Delete empty sets. + if (pins_ && pins_->empty()) { + if (own_pts) + delete pins_; + pins_ = nullptr; + } + if (clks_ && clks_->empty()) { + if (own_pts) + delete clks_; + clks_ = nullptr; + } + if (insts_ && insts_->empty()) { + if (own_pts) + delete insts_; + insts_ = nullptr; + } + findHash(); +} + +ExceptionFromTo::~ExceptionFromTo() +{ + if (own_pts_) { + delete pins_; + delete clks_; + delete insts_; + } +} + +bool +ExceptionFromTo::hasPins() const +{ + return pins_ != nullptr && !pins_->empty(); +} + +bool +ExceptionFromTo::hasClocks() const +{ + return clks_ != nullptr && !clks_->empty(); +} + +bool +ExceptionFromTo::hasInstances() const +{ + return insts_ != nullptr && !insts_->empty(); +} + +bool +ExceptionFromTo::hasObjects() const +{ + return hasPins() || hasClocks() || hasInstances(); +} + +void +ExceptionFromTo::allPins(const Network *network, + PinSet *pins) +{ + if (pins_) { + PinSet::Iterator pin_iter(pins_); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + pins->insert(pin); + } + } + if (insts_) { + InstanceSet::Iterator inst_iter(insts_); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + pins->insert(pin); + } + delete pin_iter; + } + } +} + +void +ExceptionFromTo::findHash() +{ + hash_ = 0; + if (pins_) { + size_t hash = 0; + PinSet::Iterator pin_iter(pins_); + while (pin_iter.hasNext()) { + const Pin *pin = pin_iter.next(); + hash += hashPtr(pin); + } + hash_ += hash * hash_pin; + } + if (clks_) { + size_t hash = 0; + ClockSet::Iterator clk_iter(clks_); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + hash += clk->index(); + } + hash_ += hash * hash_clk; + } + if (insts_) { + size_t hash = 0; + InstanceSet::Iterator inst_iter(insts_); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + hash += hashPtr(inst); + } + hash_ += hash * hash_inst; + } +} + +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_) + && from_to->transition() == tr_; +} + +int +ExceptionFromTo::nameCmp(ExceptionPt *pt2, + const Network *network) const +{ + int priority_cmp = typePriority() - pt2->typePriority(); + if (priority_cmp == 0) { + int pin_cmp = setNameCmp(pins_, pt2->pins(), network); + if (pin_cmp == 0) { + int clk_cmp = setNameCmp(clks_, pt2->clks()); + if (clk_cmp == 0) { + int inst_cmp = setNameCmp(insts_, pt2->instances(), network); + if (inst_cmp == 0) + return tr_->index() - pt2->transition()->index(); + else + return inst_cmp; + } + else + return clk_cmp; + } + else + return pin_cmp; + } + else + return priority_cmp; +} + +void +ExceptionFromTo::mergeInto(ExceptionPt *pt) +{ + if (pins_) { + PinSet::Iterator pin_iter(pins_); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + pt->addPin(pin); + } + } + if (clks_) { + ClockSet::Iterator clk_iter(clks_); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + pt->addClock(clk); + } + } + if (insts_) { + InstanceSet::Iterator inst_iter(insts_); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + pt->addInstance(inst); + } + } +} + +void +ExceptionFromTo::deleteObjects(ExceptionFromTo *pt) +{ + PinSet *pins = pt->pins(); + if (pins && pins_) { + PinSet::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + deletePin(pin); + } + } + ClockSet *clks = pt->clks(); + if (clks && clks_) { + ClockSet::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + deleteClock(clk); + } + } + InstanceSet *insts = pt->instances(); + if (insts && insts_) { + InstanceSet::Iterator inst_iter(insts); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + deleteInstance(inst); + } + } +} + +void +ExceptionFromTo::addPin(Pin *pin) +{ + if (pins_ == nullptr) + pins_ = new PinSet; + if (!pins_->hasKey(pin)) { + pins_->insert(pin); + // Incrementally update hash. + hash_ += hashPtr(pin) * hash_pin; + } +} + +void +ExceptionFromTo::addClock(Clock *clk) +{ + if (clks_ == nullptr) + clks_ = new ClockSet; + if (!clks_->hasKey(clk)) { + clks_->insert(clk); + // Incrementally update hash. + hash_ += clk->index() * hash_clk; + } +} + +void +ExceptionFromTo::addInstance(Instance *inst) +{ + if (insts_ == nullptr) + insts_ = new InstanceSet; + if (!insts_->hasKey(inst)) { + insts_->insert(inst); + // Incrementally update hash. + hash_ += hashPtr(inst) * hash_inst; + } +} + +void +ExceptionFromTo::deletePin(Pin *pin) +{ + if (pins_) { + pins_->erase(pin); + // Incrementally update hash. + hash_ -= hashPtr(pin) * hash_pin; + } +} + +void +ExceptionFromTo::deleteClock(Clock *clk) +{ + if (clks_) { + clks_->erase(clk); + // Incrementally update hash. + hash_ -= clk->index() * hash_clk; + } +} + +void +ExceptionFromTo::deleteInstance(Instance *inst) +{ + if (insts_) { + insts_->erase(inst); + // Incrementally update hash. + hash_ -= hashPtr(inst) * hash_inst; + } +} + +const char * +ExceptionFromTo::asString(const Network *network) const +{ + string str; + str += " "; + str += cmdKeyword(); + str += " {"; + + int obj_count = 0; + bool first = true; + if (pins_) { + PinSeq pins; + sortPinSet(pins_, network, pins); + PinSeq::Iterator pin_iter(pins); + while (pin_iter.hasNext() + && obj_count < as_string_max_objects_) { + const Pin *pin = pin_iter.next(); + if (!first) + str += ", "; + str += network->pathName(pin); + first = false; + obj_count++; + } + } + if (clks_) { + ClockSeq clks; + sortClockSet(clks_, clks); + ClockSeq::Iterator clk_iter(clks); + while (clk_iter.hasNext() + && obj_count < as_string_max_objects_) { + Clock *clk = clk_iter.next(); + if (!first) + str += ", "; + str += clk->name(); + first = false; + obj_count++; + } + } + if (insts_) { + InstanceSeq insts; + sortInstanceSet(insts_, network, insts); + InstanceSeq::Iterator inst_iter(insts); + while (inst_iter.hasNext() + && obj_count < as_string_max_objects_) { + if (!first) + str += ", "; + Instance *inst = inst_iter.next(); + str += network->pathName(inst); + first = false; + obj_count++; + } + } + if (obj_count == as_string_max_objects_) + str += ", ..."; + + str += "}"; + + char *result = makeTmpString(str.size() + 1); + strcpy(result, str.c_str()); + return result; +} + +size_t +ExceptionFromTo::objectCount() const +{ + size_t count = 0; + if (pins_) + count += pins_->size(); + if (clks_) + count += clks_->size(); + if (insts_) + count += insts_->size(); + return count; +} + +//////////////////////////////////////////////////////////////// + +ExceptionFrom::ExceptionFrom(PinSet *pins, + ClockSet *clks, + InstanceSet *insts, + const TransRiseFallBoth *tr, + bool own_pts) : + ExceptionFromTo(pins, clks, insts, tr, own_pts) +{ +} + +void +ExceptionFrom::findHash() +{ + ExceptionFromTo::findHash(); + hash_ += tr_->index() * 31 + 29; +} + +ExceptionFrom * +ExceptionFrom::clone() +{ + PinSet *pins = nullptr; + if (pins_) + pins = new PinSet(*pins_); + ClockSet *clks = nullptr; + if (clks_) + clks = new ClockSet(*clks_); + InstanceSet *insts = nullptr; + if (insts_) + insts = new InstanceSet(*insts_); + return new ExceptionFrom(pins, clks, insts, tr_, true); +} + +bool +ExceptionFrom::intersectsPts(ExceptionFrom *from) const +{ + return from->transition() == tr_ + && ((pins_ && PinSet::intersects(pins_, from->pins())) + || (clks_ && ClockSet::intersects(clks_, from->clks())) + || (insts_ && InstanceSet::intersects(insts_, from->instances()))); +} + +const char * +ExceptionFrom::cmdKeyword() const +{ + if (tr_ == TransRiseFallBoth::rise()) + return "-rise_from"; + else if (tr_ == TransRiseFallBoth::fall()) + return "-fall_from"; + else + return "-from"; +} + +//////////////////////////////////////////////////////////////// + +ExceptionTo::ExceptionTo(PinSet *pins, + ClockSet *clks, + InstanceSet *insts, + const TransRiseFallBoth *tr, + const TransRiseFallBoth *end_tr, + bool own_pts) : + ExceptionFromTo(pins, clks, insts, tr, own_pts), + end_tr_(end_tr) +{ +} + +ExceptionTo * +ExceptionTo::clone() +{ + PinSet *pins = nullptr; + if (pins_) + pins = new PinSet(*pins_); + ClockSet *clks = nullptr; + if (clks_) + clks = new ClockSet(*clks_); + InstanceSet *insts = nullptr; + if (insts_) + insts = new InstanceSet(*insts_); + return new ExceptionTo(pins, clks, insts, tr_, end_tr_, true); +} + +const char * +ExceptionTo::asString(const Network *network) const +{ + string str; + if (hasObjects()) + str += ExceptionFromTo::asString(network); + + if (end_tr_ != TransRiseFallBoth::riseFall()) + str += (end_tr_ == TransRiseFallBoth::rise()) ? " -rise" : " -fall"; + + char *result = makeTmpString(str.size() + 1); + strcpy(result, str.c_str()); + return result; +} + +bool +ExceptionTo::intersectsPts(ExceptionTo *to) const +{ + return to->transition() == tr_ + && to->endTransition() == end_tr_ + && ((pins_ && PinSet::intersects(pins_, to->pins())) + || (clks_ && ClockSet::intersects(clks_, to->clks())) + || (insts_ && InstanceSet::intersects(insts_, to->instances()))); +} + +bool +ExceptionTo::matchesFilter(const Pin *pin, + const ClockEdge *clk_edge, + const TransRiseFall *end_tr, + const Network *network) const +{ + // "report -to reg" does match clock pins. + return matches(pin, clk_edge, end_tr, true, network); +} + +bool +ExceptionTo::matches(const Pin *pin, + const ClockEdge *clk_edge, + const TransRiseFall *end_tr, + const Network *network) const +{ + // "exception -to reg" does not match reg clock pins. + return matches(pin, clk_edge, end_tr, false, network); +} + +bool +ExceptionTo::matches(const Pin *pin, + const ClockEdge *clk_edge, + const TransRiseFall *end_tr, + bool inst_matches_reg_clk_pin, + const Network *network) const + +{ + return (pins_ + && pins_->hasKey(const_cast(pin)) + && tr_->matches(end_tr) + && end_tr_->matches(end_tr)) + || (clk_edge + && clks_ + && clks_->hasKey(const_cast(clk_edge->clock())) + && tr_->matches(clk_edge->transition()) + && end_tr_->matches(end_tr)) + || (insts_ + && (inst_matches_reg_clk_pin + || !network->isRegClkPin(pin)) + && insts_->hasKey(network->instance(pin)) + && network->direction(pin)->isAnyInput() + && tr_->matches(end_tr) + && end_tr_->matches(end_tr)) + || (pins_ == nullptr + && clks_ == nullptr + && insts_ == nullptr + && end_tr_->matches(end_tr)); +} + +bool +ExceptionTo::matches(const Pin *pin, + const TransRiseFall *end_tr) const +{ + return (pins_ + && pins_->hasKey(const_cast(pin)) + && tr_->matches(end_tr) + && end_tr_->matches(end_tr)) + || (pins_ == nullptr + && clks_ == nullptr + && insts_ == nullptr + && end_tr_->matches(end_tr)); +} + +bool +ExceptionTo::matches(const Clock *clk) const +{ + return clks_ + && clks_->hasKey(const_cast(clk)); +} + +const char * +ExceptionTo::cmdKeyword() const +{ + if (tr_ == TransRiseFallBoth::rise()) + return "-rise_to"; + else if (tr_ == TransRiseFallBoth::fall()) + return "-fall_to"; + else + return "-to"; +} + +int +ExceptionTo::nameCmp(ExceptionPt *pt2, + const Network *network) const +{ + ExceptionTo *to2 = dynamic_cast(pt2); + int cmp = ExceptionFromTo::nameCmp(pt2, network); + if (cmp == 0) + return end_tr_->index() - to2->endTransition()->index(); + else + return cmp; +} + +//////////////////////////////////////////////////////////////// + +ExceptionThru::ExceptionThru(PinSet *pins, + NetSet *nets, + InstanceSet *insts, + const TransRiseFallBoth *tr, + bool own_pts, + const Network *network) : + ExceptionPt(tr, own_pts), + pins_(pins), + edges_(nullptr), + nets_(nets), + insts_(insts) +{ + // Delete empty sets. + if (pins_ && pins_->empty()) { + if (own_pts) + delete pins_; + pins_ = nullptr; + } + if (nets_ && nets_->empty()) { + if (own_pts) + delete nets_; + nets_ = nullptr; + } + if (insts_ && insts_->empty()) { + if (own_pts) + delete insts_; + insts_ = nullptr; + } + makeAllEdges(network); + findHash(); +} + +void +ExceptionThru::makeAllEdges(const Network *network) +{ + if (pins_) + makePinEdges(network); + if (nets_) + makeNetEdges(network); + if (insts_) + makeInstEdges(network); +} + +void +ExceptionThru::makePinEdges(const Network *network) +{ + PinSet::Iterator pin_iter(pins_); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + if (network->isHierarchical(pin)) + makeHpinEdges(pin, network); + } +} + +// Call after the pin has been deleted from pins_, +// but before the pin has been deleted from the netlist. +void +ExceptionThru::deletePinEdges(Pin *pin, + Network *network) +{ + // Incrementally delete only edges through (hier) or from/to (leaf) the pin. + if (edges_ && network->net(pin)) { + if (network->isHierarchical(pin)) { + // Use driver lookup to minimize potentially expensive calls to + // 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. + EdgePinsSet::Iterator edge_iter(edges_); + while (edge_iter.hasNext()) { + EdgePins *edge_pins = edge_iter.next(); + Pin *p_first = edge_pins->first; + if (drvrs->hasKey(p_first)) { + deletePinPairsThruHierPin(pin, network, edges_); + break; + } + } + } + } + else { + EdgePinsSet::Iterator edge_iter(edges_); + while (edge_iter.hasNext()) { + EdgePins *edge_pins = edge_iter.next(); + if (edge_pins->first == pin + || edge_pins->second == pin) { + edges_->erase(edge_pins); + delete edge_pins; + } + } + } + } +} + +void +ExceptionThru::makeHpinEdges(const Pin *pin, + const Network *network) +{ + if (edges_ == nullptr) + edges_ = new EdgePinsSet; + // Add edges thru pin to edges_. + insertPinPairsThruHierPin(pin, network, edges_); +} + +void +ExceptionThru::makeNetEdges(const Network *network) +{ + NetSet::Iterator net_iter(nets_); + while (net_iter.hasNext()) { + Net *net = net_iter.next(); + if (edges_ == nullptr) + edges_ = new EdgePinsSet; + // Add edges thru pin to edges_. + insertPinPairsThruNet(net, network, edges_); + } +} + +void +ExceptionThru::makeNetEdges(Net *net, + const Network *network) +{ + if (edges_ == nullptr) + edges_ = new EdgePinsSet; + // Add edges thru pin to edges_. + insertPinPairsThruNet(net, network, edges_); +} + +void +ExceptionThru::makeInstEdges(const Network *network) +{ + InstanceSet::Iterator inst_iter(insts_); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + if (network->isHierarchical(inst)) { + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + makeHpinEdges(pin, network); + } + delete pin_iter; + } + } +} + +void +ExceptionThru::makeInstEdges(Instance *inst, + Network *network) +{ + if (network->isHierarchical(inst)) { + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + makeHpinEdges(pin, network); + } + delete pin_iter; + } +} + +// Call after the inst has been deleted from insts_, +// but before the inst has been deleted from the netlist. +void +ExceptionThru::deleteInstEdges(Instance *inst, + Network *network) +{ + // Incrementally delete edges through each hier pin. + if (edges_) { + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + deletePinEdges(pin, network); + } + delete pin_iter; + } +} + +ExceptionThru::~ExceptionThru() +{ + if (own_pts_) { + delete pins_; + delete nets_; + delete insts_; + if (edges_) { + edges_->deleteContents(); + delete edges_; + } + } +} + +const char * +ExceptionThru::asString(const Network *network) const +{ + string str; + bool first = true; + int obj_count = 0; + if (pins_) { + PinSeq pins; + sortPinSet(pins_, network, pins); + PinSeq::Iterator pin_iter(pins); + while (pin_iter.hasNext() + && obj_count < as_string_max_objects_) { + const Pin *pin = pin_iter.next(); + if (!first) + str += ", "; + str += network->pathName(pin); + first = false; + obj_count++; + } + } + if (nets_) { + NetSeq nets; + sortNetSet(nets_, network, nets); + NetSeq::Iterator net_iter(nets); + while (net_iter.hasNext() + && obj_count < as_string_max_objects_) { + const Net *net = net_iter.next(); + if (!first) + str += ", "; + str += network->pathName(net); + first = false; + obj_count++; + } + } + if (insts_) { + InstanceSeq insts; + sortInstanceSet(insts_, network, insts); + InstanceSeq::Iterator inst_iter(insts); + while (inst_iter.hasNext() + && obj_count < as_string_max_objects_) { + if (!first) + str += ", "; + Instance *inst = inst_iter.next(); + str += network->pathName(inst); + first = false; + obj_count++; + } + } + if (obj_count == as_string_max_objects_) + str += ", ..."; + if (tr_ == TransRiseFallBoth::rise()) + str += " rise"; + else if (tr_ == TransRiseFallBoth::fall()) + str += " fall"; + + char *result = makeTmpString(str.size() + 1); + strcpy(result, str.c_str()); + return result; +} + +ExceptionThruSeq * +exceptionThrusClone(ExceptionThruSeq *thrus, + const Network *network) +{ + if (thrus) { + ExceptionThruSeq *thrus_cpy = new ExceptionThruSeq; + ExceptionThruSeq::Iterator thru_iter(thrus); + while (thru_iter.hasNext()) { + ExceptionThru *thru = thru_iter.next(); + ExceptionThru *thru_cpy = thru->clone(network); + thrus_cpy->push_back(thru_cpy); + } + return thrus_cpy; + } + else + return nullptr; +} + +ExceptionThru * +ExceptionThru::clone(const Network *network) +{ + PinSet *pins = nullptr; + if (pins_) + pins = new PinSet(*pins_); + NetSet *nets = nullptr; + if (nets_) + nets = new NetSet(*nets_); + InstanceSet *insts = nullptr; + if (insts_) + insts = new InstanceSet(*insts_); + return new ExceptionThru(pins, nets, insts, tr_, true, network); +} + +bool +ExceptionThru::hasObjects() const +{ + return (pins_ != nullptr && !pins_->empty()) + || (nets_ != nullptr && !nets_->empty()) + || (insts_ != nullptr && !insts_->empty()); +} + + +void +ExceptionThru::addPin(Pin *pin) +{ + if (pins_ == nullptr) + pins_ = new PinSet; + if (!pins_->hasKey(pin)) { + pins_->insert(pin); + // Incrementally update hash. + hash_ += hashPtr(pin) * hash_pin; + } +} + +void +ExceptionThru::addNet(Net *net) +{ + if (nets_ == nullptr) + nets_ = new NetSet; + if (!nets_->hasKey(net)) { + nets_->insert(net); + // Incrementally update hash. + hash_ += hashPtr(net) * hash_net; + } +} + +void +ExceptionThru::addInstance(Instance *inst) +{ + if (insts_ == nullptr) + insts_ = new InstanceSet; + if (!insts_->hasKey(inst)) { + insts_->insert(inst); + // Incrementally update hash. + hash_ += hashPtr(inst) * hash_inst; + } +} + +void +ExceptionThru::addEdge(EdgePins *edge) +{ + if (edges_ == nullptr) + edges_ = new EdgePinsSet; + edges_->insert(edge); + // Hash is unchanged because edges are derived from hierarchical pins. +} + +void +ExceptionThru::deletePin(Pin *pin) +{ + if (pins_) { + pins_->erase(pin); + // Incrementally update hash. + hash_ -= hashPtr(pin) * hash_pin; + } +} + +void +ExceptionThru::deleteNet(Net *net) +{ + if (nets_) { + nets_->erase(net); + // Incrementally update hash. + hash_ -= hashPtr(net) * hash_net; + } +} + +void +ExceptionThru::deleteInstance(Instance *inst) +{ + if (insts_) { + insts_->erase(inst); + // Incrementally update hash. + hash_ -= hashPtr(inst) * hash_inst; + } +} + +void +ExceptionThru::deleteEdge(EdgePins *edge) +{ + if (edges_) { + edges_->erase(edge); + // Hash is unchanged because edges are derived from hierarchical pins. + } +} + +void +ExceptionThru::allPins(const Network *network, + PinSet *pins) +{ + if (pins_) { + PinSet::Iterator pin_iter(pins_); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + pins->insert(pin); + } + } + if (insts_) { + InstanceSet::Iterator inst_iter(insts_); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + pins->insert(pin); + } + delete pin_iter; + } + } + if (nets_) { + NetSet::Iterator net_iter(nets_); + while (net_iter.hasNext()) { + Net *net = net_iter.next(); + NetConnectedPinIterator *pin_iter = network->connectedPinIterator(net); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + pins->insert(pin); + } + delete pin_iter; + } + } +} + +bool +ExceptionThru::matches(const Pin *from_pin, + const Pin *to_pin, + const TransRiseFall *to_tr, + const Network *network) +{ + EdgePins edge_pins(const_cast(from_pin), const_cast(to_pin)); + return ((pins_ && pins_->hasKey(const_cast(to_pin))) + || (edges_ && edges_->hasKey(&edge_pins)) + || (nets_ && nets_->hasKey(network->net(to_pin))) + || (insts_ && insts_->hasKey(network->instance(to_pin)))) + && tr_->matches(to_tr); +} + +void +ExceptionThru::findHash() +{ + hash_ = 0; + if (pins_) { + size_t hash = 0; + PinSet::Iterator pin_iter(pins_); + while (pin_iter.hasNext()) { + const Pin *pin = pin_iter.next(); + hash += hashPtr(pin); + } + hash_ += hash * hash_pin; + } + if (nets_) { + size_t hash = 0; + NetSet::Iterator net_iter(nets_); + while (net_iter.hasNext()) { + Net *net = net_iter.next(); + hash += hashPtr(net); + } + hash_ += hash * hash_net; + } + if (insts_) { + size_t hash = 0; + InstanceSet::Iterator inst_iter(insts_); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + hash += hashPtr(inst); + } + hash_ += hash * hash_inst; + } + hash_ += tr_->index() * 13; +} + +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_) + && tr_ == thru->tr_; +} + +int +ExceptionThru::nameCmp(ExceptionPt *pt2, + const Network *network) const +{ + int priority_cmp = typePriority() - pt2->typePriority(); + if (priority_cmp == 0) { + int pin_cmp = setNameCmp(pins_, pt2->pins(), network); + if (pin_cmp == 0) { + int net_cmp = setNameCmp(nets_, pt2->nets(), network); + if (net_cmp == 0) { + int inst_cmp = setNameCmp(insts_, pt2->instances(), network); + if (inst_cmp == 0) + return tr_->index() - pt2->transition()->index(); + else + return inst_cmp; + } + else + return net_cmp; + } + else + return pin_cmp; + } + else + return priority_cmp; +} + +void +ExceptionThru::mergeInto(ExceptionPt *pt) +{ + if (pins_) { + PinSet::Iterator pin_iter(pins_); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + pt->addPin(pin); + } + } + if (edges_) { + EdgePinsSet::Iterator edge_iter(edges_); + while (edge_iter.hasNext()) { + EdgePins *edge = edge_iter.next(); + pt->addEdge(edge); + } + // EdgePins are now owned by acquirer. + edges_->clear(); + } + if (nets_) { + NetSet::Iterator net_iter(nets_); + while (net_iter.hasNext()) { + Net *net = net_iter.next(); + pt->addNet(net); + } + } + if (insts_) { + InstanceSet::Iterator inst_iter(insts_); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + pt->addInstance(inst); + } + } +} + +void +ExceptionThru::deleteObjects(ExceptionThru *pt) +{ + PinSet *pins = pt->pins(); + if (pins && pins_) { + PinSet::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + deletePin(pin); + } + } + EdgePinsSet *edges = pt->edges(); + if (edges && edges_) { + EdgePinsSet::Iterator edge_iter(edges); + while (edge_iter.hasNext()) { + EdgePins *edge = edge_iter.next(); + deleteEdge(edge); + } + } + NetSet *nets = pt->nets(); + if (nets && nets_) { + NetSet::Iterator net_iter(nets); + while (net_iter.hasNext()) { + Net *net = net_iter.next(); + deleteNet(net); + } + } + InstanceSet *insts = pt->instances(); + if (insts && insts_) { + InstanceSet::Iterator inst_iter(insts); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + deleteInstance(inst); + } + } +} + +bool +ExceptionThru::intersectsPts(ExceptionThru *thru) const +{ + return thru->transition() == tr_ + && ((pins_ && PinSet::intersects(pins_, thru->pins())) + || (nets_ && NetSet::intersects(nets_, thru->nets())) + || (insts_ && InstanceSet::intersects(insts_, thru->instances()))); +} + +size_t +ExceptionThru::objectCount() const +{ + size_t count = 0; + if (pins_) + count += pins_->size(); + if (nets_) + count += nets_->size(); + if (insts_) + count += insts_->size(); + return count; +} + +void +ExceptionThru::connectPinAfter(PinSet *drvrs, + Network *network) +{ + // - Tricky to detect exactly what needs to be updated. In theory, + // at most, only edges starting/ending (pin is leaf) or spanning + // (pin is hier) the pin may need to be added. Trick is avoiding + // adding edges through the newly connected pin that don't belong. + // - some examples: + // a. leaf driver connected, with downstream hnet in nets_, only + // the edges from pin through hier_net should be added. + // b. hpin connected, but only some other hpin/hnet along the overall + // net resides in pins_/nets_, only add edges through those other + // hpin/hnets. + // c. hier inst resides in insts_, it gets a new pin added/connected, so + // should add new edges through that pin. + + // Use driver lookups to minimize potentially expensive calls that + // traverse hier pins. + + // No enabled edges if no driver. + if (drvrs && !drvrs->empty()) { + PinSet::Iterator pin_iter(pins_); + while (pin_iter.hasNext()) { + Pin *thru_pin = pin_iter.next(); + if (network->isHierarchical(thru_pin)) { + PinSet *thru_pin_drvrs = network->drivers(thru_pin); + if (PinSet::intersects(drvrs, thru_pin_drvrs)) + makePinEdges(thru_pin, network); + } + } + InstanceSet::Iterator inst_iter(insts_); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + if (network->isHierarchical(inst)) { + InstancePinIterator *inst_pin_iter = network->pinIterator(inst); + 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)) + makePinEdges(inst_pin, network); + } + delete inst_pin_iter; + } + } + NetSet::Iterator net_iter(nets_); + while (net_iter.hasNext()) { + Net *net = net_iter.next(); + PinSet *net_drvrs = network->drivers(net); + if (PinSet::intersects(drvrs, net_drvrs)) + makeNetEdges(net, network); + } + } +} + +void +ExceptionThru::makePinEdges(Pin *pin, + const Network *network) +{ + if (network->isHierarchical(pin)) + makeHpinEdges(pin, network); +} + +void +ExceptionThru::disconnectPinBefore(Pin *pin, + Network *network) +{ + // Remove edges from/to leaf pin and through hier pin. + deletePinEdges(pin, network); +} + +//////////////////////////////////////////////////////////////// + +ExceptionPtIterator::ExceptionPtIterator(const ExceptionPath *exception) : + exception_(exception), + from_done_(false), + to_done_(false) +{ + if (exception->thrus()) + thru_iter_.init(exception->thrus()); +} + +bool +ExceptionPtIterator::hasNext() +{ + return (!from_done_ && exception_->from()) + || thru_iter_.hasNext() + || (!to_done_ && exception_->to()); +} + + +ExceptionPt * +ExceptionPtIterator::next() +{ + if (!from_done_ && exception_->from()) { + from_done_ = true; + return exception_->from(); + } + else if (thru_iter_.hasNext()) + return thru_iter_.next(); + else { + to_done_ = true; + return exception_->to(); + } +} + +//////////////////////////////////////////////////////////////// + +ExpandedExceptionVisitor::ExpandedExceptionVisitor(ExceptionPath *exception, + const Network *network) : + exception_(exception), + network_(network) +{ +} + +void +ExpandedExceptionVisitor::visitExpansions() +{ + ExceptionFrom *from = exception_->from(); + if (from) { + const TransRiseFallBoth *tr = from->transition(); + PinSet::Iterator pin_iter(from->pins()); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + PinSet pins; + pins.insert(pin); + ExceptionFrom expanded_from(&pins, nullptr, nullptr, tr, false); + expandThrus(&expanded_from); + } + ClockSet::Iterator clk_iter(from->clks()); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + ClockSet clks; + clks.insert(clk); + ExceptionFrom expanded_from(nullptr, &clks, nullptr, tr, false); + expandThrus(&expanded_from); + } + InstanceSet::Iterator inst_iter(from->instances()); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + InstanceSet insts; + insts.insert(inst); + ExceptionFrom expanded_from(nullptr, nullptr, &insts, tr, false); + expandThrus(&expanded_from); + } + } + else + expandThrus(0); +} + +void +ExpandedExceptionVisitor::expandThrus(ExceptionFrom *expanded_from) +{ + ExceptionThruSeq *thrus = exception_->thrus(); + if (thrus) { + // Use tail recursion to expand the exception points in the thrus. + ExceptionThruSeq::Iterator thru_iter(thrus); + ExceptionThruSeq expanded_thrus; + expandThru(expanded_from, thru_iter, &expanded_thrus); + } + else + expandTo(expanded_from, nullptr); +} + +void +ExpandedExceptionVisitor::expandThru(ExceptionFrom *expanded_from, + ExceptionThruSeq::Iterator &thru_iter, + ExceptionThruSeq *expanded_thrus) +{ + if (thru_iter.hasNext()) { + ExceptionThru *thru = thru_iter.next(); + const TransRiseFallBoth *tr = thru->transition(); + PinSet::Iterator pin_iter(thru->pins()); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + PinSet pins; + pins.insert(pin); + ExceptionThru expanded_thru(&pins, nullptr, nullptr, tr, false, network_); + expanded_thrus->push_back(&expanded_thru); + expandThru(expanded_from, thru_iter, expanded_thrus); + expanded_thrus->pop_back(); + } + NetSet::Iterator net_iter(thru->nets()); + while (net_iter.hasNext()) { + Net *net = net_iter.next(); + NetSet nets; + nets.insert(net); + ExceptionThru expanded_thru(nullptr, &nets, nullptr, tr, false, network_); + expanded_thrus->push_back(&expanded_thru); + expandThru(expanded_from, thru_iter, expanded_thrus); + expanded_thrus->pop_back(); + } + InstanceSet::Iterator inst_iter(thru->instances()); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + InstanceSet insts; + insts.insert(inst); + ExceptionThru expanded_thru(nullptr, nullptr, &insts, tr, false, network_); + expanded_thrus->push_back(&expanded_thru); + expandThru(expanded_from, thru_iter, expanded_thrus); + expanded_thrus->pop_back(); + } + } + else + // End of thrus tail recursion. + expandTo(expanded_from, expanded_thrus); +} + +void +ExpandedExceptionVisitor::expandTo(ExceptionFrom *expanded_from, + ExceptionThruSeq *expanded_thrus) +{ + ExceptionTo *to = exception_->to(); + if (to) { + const TransRiseFallBoth *tr = to->transition(); + const TransRiseFallBoth *end_tr = to->endTransition(); + PinSet::Iterator pin_iter(to->pins()); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + PinSet pins; + pins.insert(pin); + ExceptionTo expanded_to(&pins, nullptr, nullptr, tr, end_tr, false); + visit(expanded_from, expanded_thrus, &expanded_to); + } + ClockSet::Iterator clk_iter(to->clks()); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + ClockSet clks; + clks.insert(clk); + ExceptionTo expanded_to(nullptr, &clks, nullptr, tr, end_tr, false); + visit(expanded_from, expanded_thrus, &expanded_to); + } + InstanceSet::Iterator inst_iter(to->instances()); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + InstanceSet insts; + insts.insert(inst); + ExceptionTo expanded_to(nullptr, nullptr, &insts, tr, end_tr, false); + visit(expanded_from, expanded_thrus, &expanded_to); + } + } + else + visit(expanded_from, expanded_thrus, nullptr); +} + +//////////////////////////////////////////////////////////////// + +ExceptionState::ExceptionState(ExceptionPath *exception, + ExceptionThru *next_thru, + int index) : + exception_(exception), + next_thru_(next_thru), + next_state_(nullptr), + index_(index) +{ +} + +void +ExceptionState::setNextState(ExceptionState *next_state) +{ + next_state_ = next_state; +} + +bool +ExceptionState::matchesNextThru(const Pin *from_pin, + const Pin *to_pin, + const TransRiseFall *to_tr, + const MinMax *min_max, + const Network *network) const +{ + // Don't advance the state if the exception is complete (no next_thru_). + return next_thru_ + && exception_->matches(min_max, false) + && next_thru_->matches(from_pin, to_pin, to_tr, network); +} + +bool +ExceptionState::isComplete() const +{ + return next_thru_ == nullptr + && exception_->to() == nullptr; +} + +size_t +ExceptionState::hash() const +{ + return hashSum(exception_->hash(), index_); +} + +//////////////////////////////////////////////////////////////// + +class ExceptionLess +{ +public: + explicit ExceptionLess(const Network *network); + bool operator()(ExceptionPath *except1, + ExceptionPath *except2); + +private: + const Network *network_; +}; + +ExceptionLess::ExceptionLess(const Network *network) : + network_(network) +{ +} + +bool +ExceptionLess::operator()(ExceptionPath *except1, + ExceptionPath *except2) +{ + int priority1 = except1->typePriority() + except1->minMax()->index(); + int priority2 = except2->typePriority() + except2->minMax()->index(); + if (priority1 == priority2) { + ExceptionPtIterator pt_iter1(except1); + ExceptionPtIterator pt_iter2(except2); + while (pt_iter1.hasNext() && pt_iter2.hasNext()) { + ExceptionPt *pt1 = pt_iter1.next(); + ExceptionPt *pt2 = pt_iter2.next(); + int cmp = pt1->nameCmp(pt2, network_); + if (cmp != 0) + return cmp < 0; + } + // Lesser has fewer exception pts. + return !pt_iter1.hasNext() && pt_iter2.hasNext(); + } + else + return (priority1 < priority2); +} + +void +sortExceptions(ExceptionPathSet *set, + ExceptionPathSeq &exceptions, + Network *network) +{ + ExceptionPathSet::Iterator except_iter(set); + while (except_iter.hasNext()) { + ExceptionPath *exception = except_iter.next(); + exceptions.push_back(exception); + } + sort(exceptions, ExceptionLess(network)); +} + +//////////////////////////////////////////////////////////////// + +class InsertPinPairsThru : public HierPinThruVisitor +{ +public: + InsertPinPairsThru(PinPairSet *pairs, + const Network *network); + +protected: + virtual void visit(Pin *drvr, + Pin *load); + + PinPairSet *pairs_; + const Network *network_; + +private: + DISALLOW_COPY_AND_ASSIGN(InsertPinPairsThru); +}; + +InsertPinPairsThru::InsertPinPairsThru(PinPairSet *pairs, + const Network *network) : + HierPinThruVisitor(), + pairs_(pairs), + network_(network) +{ +} + +void +InsertPinPairsThru::visit(Pin *drvr, + Pin *load) +{ + PinPair probe(drvr, load); + if (!pairs_->hasKey(&probe)) { + PinPair *pair = new PinPair(drvr, load); + pairs_->insert(pair); + } +} + +static void +insertPinPairsThruHierPin(const Pin *hpin, + const Network *network, + PinPairSet *pairs) +{ + InsertPinPairsThru visitor(pairs, network); + visitDrvrLoadsThruHierPin(hpin, network, &visitor); +} + +static void +insertPinPairsThruNet(Net *net, + const Network *network, + PinPairSet *pairs) +{ + InsertPinPairsThru visitor(pairs, network); + visitDrvrLoadsThruNet(net, network, &visitor); +} + +class DeletePinPairsThru : public HierPinThruVisitor +{ +public: + DeletePinPairsThru(PinPairSet *pairs, + const Network *network); + +protected: + virtual void visit(Pin *drvr, + Pin *load); + + PinPairSet *pairs_; + const Network *network_; + +private: + DISALLOW_COPY_AND_ASSIGN(DeletePinPairsThru); +}; + +DeletePinPairsThru::DeletePinPairsThru(PinPairSet *pairs, + const Network *network) : + HierPinThruVisitor(), + pairs_(pairs), + network_(network) +{ +} + +void +DeletePinPairsThru::visit(Pin *drvr, + Pin *load) +{ + PinPair probe(drvr, load); + pairs_->erase(&probe); +} + +static void +deletePinPairsThruHierPin(const Pin *hpin, + const Network *network, + PinPairSet *pairs) +{ + DeletePinPairsThru visitor(pairs, network); + visitDrvrLoadsThruHierPin(hpin, network, &visitor); +} + +} // namespace diff --git a/sdc/ExceptionPath.hh b/sdc/ExceptionPath.hh new file mode 100644 index 0000000..5a5961b --- /dev/null +++ b/sdc/ExceptionPath.hh @@ -0,0 +1,684 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_EXCEPTION_PATH_H +#define STA_EXCEPTION_PATH_H + +#include "DisallowCopyAssign.hh" +#include "Error.hh" +#include "Set.hh" +#include "SdcCmdComment.hh" +#include "SdcClass.hh" + +namespace sta { + +class TransRiseFall; +class TransRiseFallBoth; +class MinMaxAll; +class Network; +class Pin; +class Clock; + +class ExceptionPt; +class ExceptionFrom; +class ExceptionThru; +class ExceptionTo; +class ExceptionState; + +typedef Vector ExceptionPathSeq; + +class ExceptionPath : public SdcCmdComment +{ +public: + ExceptionPath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + int priority, + const char *comment); + virtual ~ExceptionPath(); + virtual bool isFalse() const { return false; } + virtual bool isLoop() const { return false; } + virtual bool isMultiCycle() const { return false; } + virtual bool isPathDelay() const { return false; } + 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; + ExceptionFrom *from() const { return from_; } + ExceptionThruSeq *thrus() const { return thrus_; } + ExceptionTo *to() const { return to_; } + ExceptionPt *firstPt(); + bool intersectsPts(ExceptionPath *exception) const; + const MinMaxAll *minMax() const { return min_max_; } + virtual bool matches(const MinMax *min_max, + bool exact) const; + bool matchesFirstPt(const TransRiseFall *to_tr, const MinMax *min_max); + ExceptionState *firstState(); + virtual bool resetMatch(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max); + // The priority remains the same even though pin/clock/net/inst objects + // are added to the exceptions points during exception merging because + // only exceptions with the same priority are merged. + virtual int priority(const MinMax *) const { return priority_; } + int priority() const { return priority_; } + void setPriority(int priority); + virtual int typePriority() const = 0; + // Exception type priorities are spaced to accomodate + // fromThruToPriority from 0 thru 127. + static int falsePathPriority() { return 4000; } + static int pathDelayPriority() { return 3000; } + static int multiCyclePathPriority() { return 2000; } + static int filterPathPriority() { return 1000; } + static int groupPathPriority() { return 0; } + // Compare the value (path delay or cycle count) to another exception + // of the same priority. Because the exception "values" are floats, + // they cannot be coded into the priority. + virtual bool tighterThan(ExceptionPath *exception) const = 0; + static int fromThruToPriority(ExceptionFrom *from, + 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 = 0; + bool mergeablePts(ExceptionPath *exception) const; + bool mergeablePts(ExceptionPath *exception2, + 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; + // Default handlers. + 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 bool isDefault() const { return false; } + virtual bool ignoreClkLatency() { return false; } + +protected: + virtual const char *typeString() const = 0; + const char *fromThruToString(const Network *network) const; + void makeStates(); + + ExceptionFrom *from_; + ExceptionThruSeq *thrus_; + ExceptionTo *to_; + const MinMaxAll *min_max_; + bool own_pts_; + int priority_; + ExceptionState *states_; + +private: + DISALLOW_COPY_AND_ASSIGN(ExceptionPath); +}; + +// set_false_path +class FalsePath : public ExceptionPath +{ +public: + FalsePath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool own_pts, + const char *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; + +private: + DISALLOW_COPY_AND_ASSIGN(FalsePath); +}; + +// Loop paths are false paths used to disable paths around +// combinational loops when dynamic loop breaking is enabled. +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; + +private: + DISALLOW_COPY_AND_ASSIGN(LoopPath); +}; + +// set_max_delay/set_min_delay +class PathDelay : public ExceptionPath +{ +public: + PathDelay(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, + 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() { return ignore_clk_latency_; } + +protected: + bool ignore_clk_latency_; + float delay_; + +private: + DISALLOW_COPY_AND_ASSIGN(PathDelay); +}; + +// set_multicycle_path +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; + + using ExceptionPath::priority; + +protected: + bool use_end_clk_; + int path_multiplier_; + +private: + DISALLOW_COPY_AND_ASSIGN(MultiCyclePath); +}; + +// Filter used restrict path reporting -from/-thru nets/pins. +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); + virtual int typePriority() const; + virtual bool tighterThan(ExceptionPath *exception) const; + +private: + DISALLOW_COPY_AND_ASSIGN(FilterPath); +}; + +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_; } + +protected: + const char *name_; + bool is_default_; + +private: + DISALLOW_COPY_AND_ASSIGN(GroupPath); +}; + +// Base class for Exception from/thru/to. +class ExceptionPt +{ +public: + ExceptionPt(const TransRiseFallBoth *tr, + bool own_pts); + virtual ~ExceptionPt() {}; + virtual bool isFrom() const { return false; } + virtual bool isThru() const { return false; } + virtual bool isTo() const { return false; } + const TransRiseFallBoth *transition() const { return tr_; } + virtual PinSet *pins() = 0; + virtual ClockSet *clks() = 0; + virtual InstanceSet *instances() = 0; + virtual NetSet *nets() = 0; + virtual EdgePinsSet *edges() = 0; + size_t hash() const; + virtual int nameCmp(ExceptionPt *pt, const Network *network) const = 0; + virtual void mergeInto(ExceptionPt *pt) = 0; + // All pins and instance/net pins. + virtual void allPins(const Network *network, + PinSet *pins) = 0; + virtual int typePriority() const = 0; + virtual const char *asString(const Network *network) const = 0; + virtual size_t objectCount() const = 0; + virtual void addPin(Pin *pin) = 0; + virtual void addClock(Clock *clk) = 0; + virtual void addInstance(Instance *inst) = 0; + virtual void addNet(Net *net) = 0; + virtual void addEdge(EdgePins *edge) = 0; + virtual void connectPinAfter(PinSet *, + Network *network) = 0; + virtual void disconnectPinBefore(Pin *pin, + Network *network) = 0; + +protected: + const TransRiseFallBoth *tr_; + // True when the pin/net/inst/edge sets are owned by the exception point. + bool own_pts_; + // Hash is cached because there may be many objects to speed up + // exception merging. + size_t hash_; + + // Maximum number of objects for asString() to show. + static const int as_string_max_objects_; + static const size_t hash_clk = 3; + static const size_t hash_pin = 5; + static const size_t hash_net = 7; + static const size_t hash_inst = 11; + +private: + DISALLOW_COPY_AND_ASSIGN(ExceptionPt); +}; + +class ExceptionFromTo : public ExceptionPt +{ +public: + ExceptionFromTo(PinSet *pins, ClockSet *clks, + InstanceSet *insts, + const TransRiseFallBoth *tr, + bool own_pts); + ~ExceptionFromTo(); + virtual PinSet *pins() { return pins_; } + bool hasPins() const; + ClockSet *clks() { return clks_; } + bool hasClocks() const; + InstanceSet *instances() { return insts_; } + bool hasInstances() const; + virtual NetSet *nets() { return nullptr; } + virtual EdgePinsSet *edges() { return nullptr; } + bool hasObjects() const; + void deleteObjects(ExceptionFromTo *pt); + virtual void allPins(const Network *network, + PinSet *pins); + bool equal(ExceptionFromTo *from_to) const; + virtual int nameCmp(ExceptionPt *pt, + const Network *network) const; + virtual void mergeInto(ExceptionPt *pt); + virtual const char *asString(const Network *network) const; + virtual size_t objectCount() const; + void deleteClock(Clock *clk); + virtual void addPin(Pin *pin); + virtual void addClock(Clock *clk); + virtual void addInstance(Instance *inst); + virtual void addNet(Net *) {} + virtual void addEdge(EdgePins *) {} + virtual void connectPinAfter(PinSet *, + Network *) {} + virtual void disconnectPinBefore(Pin *, + Network *) {} + +protected: + virtual void findHash(); + + void deletePin(Pin *pin); + void deleteInstance(Instance *inst); + virtual const char *cmdKeyword() const = 0; + + PinSet *pins_; + ClockSet *clks_; + InstanceSet *insts_; + +private: + DISALLOW_COPY_AND_ASSIGN(ExceptionFromTo); +}; + +class ExceptionFrom : public ExceptionFromTo +{ +public: + ExceptionFrom(PinSet *pins, + ClockSet *clks, + InstanceSet *insts, + const TransRiseFallBoth *tr, + bool own_pts); + ExceptionFrom *clone(); + virtual bool isFrom() const { return true; } + bool intersectsPts(ExceptionFrom *from) const; + virtual int typePriority() const { return 0; } + +protected: + virtual const char *cmdKeyword() const; + virtual void findHash(); + +private: + DISALLOW_COPY_AND_ASSIGN(ExceptionFrom); +}; + +class ExceptionTo : public ExceptionFromTo +{ +public: + ExceptionTo(PinSet *pins, + ClockSet *clks, + InstanceSet *insts, + // -to|-rise_to|-fall_to + const TransRiseFallBoth *tr, + // -rise|-fall endpoint transition. + const TransRiseFallBoth *end_tr, + bool own_pts); + ExceptionTo *clone(); + virtual bool isTo() const { return true; } + const char *asString(const Network *network) const; + const TransRiseFallBoth *endTransition() { return end_tr_; } + bool intersectsPts(ExceptionTo *to) const; + virtual int typePriority() const { return 1; } + bool matches(const Pin *pin, + const ClockEdge *clk_edge, + const TransRiseFall *end_tr, + const Network *network) const; + bool matches(const Pin *pin, + const TransRiseFall *end_tr) const; + bool matches(const Clock *clk) const; + bool matchesFilter(const Pin *pin, + const ClockEdge *clk_edge, + const TransRiseFall *end_tr, + const Network *network) const; + virtual int nameCmp(ExceptionPt *pt, const Network *network) const; + +protected: + bool matches(const Pin *pin, + const ClockEdge *clk_edge, + const TransRiseFall *end_tr, + bool inst_matches_reg_clk_pin, + const Network *network) const; + virtual const char *cmdKeyword() const; + + // -rise|-fall endpoint transition. + const TransRiseFallBoth *end_tr_; + +private: + DISALLOW_COPY_AND_ASSIGN(ExceptionTo); +}; + +class ExceptionThru : public ExceptionPt +{ +public: + ExceptionThru(PinSet *pins, + NetSet *nets, + InstanceSet *insts, + const TransRiseFallBoth *tr, + bool own_pts, + const Network *network); + ~ExceptionThru(); + 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; } + bool hasObjects() const; + void deleteObjects(ExceptionThru *pt); + virtual void allPins(const Network *network, + PinSet *pins); + bool matches(const Pin *from_pin, + const Pin *to_pin, + const TransRiseFall *to_tr, + const Network *network); + bool equal(ExceptionThru *thru) const; + virtual int nameCmp(ExceptionPt *pt, + const Network *network) const; + virtual void mergeInto(ExceptionPt *pt); + bool intersectsPts(ExceptionThru *thru) const; + virtual int typePriority() const { return 2; } + virtual size_t objectCount() const; + virtual void connectPinAfter(PinSet *drvrs, + Network *network); + virtual void disconnectPinBefore(Pin *pin, + Network *network); + +protected: + void findHash(); + virtual void addPin(Pin *pin); + virtual void addEdge(EdgePins *edge); + virtual void addNet(Net *net); + virtual void addInstance(Instance *inst); + virtual void addClock(Clock *) {} + void deletePin(Pin *pin); + void deleteEdge(EdgePins *edge); + void deleteNet(Net *net); + void deleteInstance(Instance *inst); + void makeAllEdges(const Network *network); + void makePinEdges(const Network *network); + void makeNetEdges(const Network *network); + void makeInstEdges(const Network *network); + void makeHpinEdges(const Pin *pin, + const Network *network); + void makePinEdges(Pin *pin, + const Network *network); + void makeNetEdges(Net *net, + const Network *network); + void makeInstEdges(Instance *inst, + Network *network); + void deletePinEdges(Pin *pin, + Network *network); + void deleteNetEdges(Net *net, + const Network *network); + void deleteInstEdges(Instance *inst, + Network *network); + + // Leaf/port pins. + PinSet *pins_; + // Graph edges that traverse thru hierarchical pins. + EdgePinsSet *edges_; + NetSet *nets_; + InstanceSet *insts_; + +private: + DISALLOW_COPY_AND_ASSIGN(ExceptionThru); +}; + +ExceptionThruSeq * +exceptionThrusClone(ExceptionThruSeq *thrus, + const Network *network); + +// Iterate uniformly across exception from/thru/to's. +class ExceptionPtIterator +{ +public: + explicit ExceptionPtIterator(const ExceptionPath *exception); + bool hasNext(); + ExceptionPt *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(ExceptionPtIterator); + + const ExceptionPath *exception_; + bool from_done_; + ExceptionThruSeq::Iterator thru_iter_; + bool to_done_; +}; + +// Visitor for exception point sets expanded into single object paths. +// For example: +// -from {A B} -to {C D} +// expands into +// -from A -to C +// -from A -to D +// -from B -to C +// -from B -to D +class ExpandedExceptionVisitor +{ +public: + ExpandedExceptionVisitor(ExceptionPath *exception, + const Network *network); + virtual ~ExpandedExceptionVisitor() {} + void visitExpansions(); + // From/thrus/to have a single exception point (pin/instance/net/clock). + virtual void visit(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to) = 0; + +protected: + ExceptionPath *exception_; + const Network *network_; + +private: + DISALLOW_COPY_AND_ASSIGN(ExpandedExceptionVisitor); + void expandFrom(); + void expandThrus(ExceptionFrom *expanded_from); + void expandThru(ExceptionFrom *expanded_from, + ExceptionThruSeq::Iterator &thru_iter, + ExceptionThruSeq *expanded_thrus); + void expandTo(ExceptionFrom *expanded_from, + ExceptionThruSeq *expanded_thrus); +}; + +// States used by tags to know what exception points have been seen +// so far in a path. +class ExceptionState +{ +public: + ExceptionState(ExceptionPath *exception, + ExceptionThru *next_thru, + int index); + ExceptionPath *exception() { return exception_; } + bool matchesNextThru(const Pin *from_pin, + const Pin *to_pin, + const TransRiseFall *to_tr, + const MinMax *min_max, + const Network *network) const; + bool isComplete() const; + ExceptionThru *nextThru() const { return next_thru_; } + ExceptionState *nextState() const { return next_state_; } + void setNextState(ExceptionState *next_state); + size_t hash() const; + +private: + DISALLOW_COPY_AND_ASSIGN(ExceptionState); + + ExceptionPath *exception_; + ExceptionThru *next_thru_; + ExceptionState *next_state_; + int index_; +}; + +// Exception thrown by check. +class EmptyExpceptionPt : public StaException +{ +public: + virtual const char *what() const throw(); +}; + +// Throws EmptyExpceptionPt it finds an empty exception point. +void +checkFromThrusTo(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to); + +void +sortExceptions(ExceptionPathSet *set, + ExceptionPathSeq &exceptions, + Network *network); + +bool +intersects(PinSet *set1, + PinSet *set2); +bool +intersects(ClockSet *set1, + ClockSet *set2); + +} // namespace +#endif diff --git a/sdc/InputDrive.cc b/sdc/InputDrive.cc new file mode 100644 index 0000000..e516291 --- /dev/null +++ b/sdc/InputDrive.cc @@ -0,0 +1,225 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "InputDrive.hh" + +namespace sta { + +InputDrive::InputDrive() +{ + for (auto tr_index : TransRiseFall::rangeIndex()) { + for (auto mm_index : MinMax::rangeIndex()) + drive_cells_[tr_index][mm_index] = nullptr; + } +} + +InputDrive::~InputDrive() +{ + for (auto tr_index : TransRiseFall::rangeIndex()) { + for (auto mm_index : MinMax::rangeIndex()) { + InputDriveCell *drive_cell = drive_cells_[tr_index][mm_index]; + delete drive_cell; + } + } +} + +void +InputDrive::setSlew(const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float slew) +{ + slews_.setValue(tr, min_max, slew); +} + +void +InputDrive::setDriveResistance(const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float res) +{ + drive_resistances_.setValue(tr, min_max, res); +} + +void +InputDrive::driveResistance(const TransRiseFall *tr, + const MinMax *min_max, + float &res, + bool &exists) +{ + drive_resistances_.value(tr, min_max, res, exists); +} + +bool +InputDrive::hasDriveResistance(const TransRiseFall *tr, const MinMax *min_max) +{ + return drive_resistances_.hasValue(tr, min_max); +} + +bool +InputDrive::driveResistanceMinMaxEqual(const TransRiseFall *tr) +{ + float min_res, max_res; + bool min_exists, max_exists; + drive_resistances_.value(tr, MinMax::min(), min_res, min_exists); + drive_resistances_.value(tr, MinMax::max(), max_res, max_exists); + return min_exists && max_exists && min_res == max_res; +} + +void +InputDrive::setDriveCell(LibertyLibrary *library, + LibertyCell *cell, + LibertyPort *from_port, + float *from_slews, + LibertyPort *to_port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max) +{ + for (auto tr_index : tr->rangeIndex()) { + for (auto mm_index : min_max->rangeIndex()) { + InputDriveCell *drive = drive_cells_[tr_index][mm_index]; + if (drive) { + 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_[tr_index][mm_index] = drive; + } + } + } +} + +void +InputDrive::driveCell(const TransRiseFall *tr, + const MinMax *min_max, + LibertyCell *&cell, + LibertyPort *&from_port, + float *&from_slews, + LibertyPort *&to_port) +{ + InputDriveCell *drive = drive_cells_[tr->index()][min_max->index()]; + if (drive) { + cell = drive->cell(); + from_port = drive->fromPort(); + from_slews = drive->fromSlews(); + to_port = drive->toPort(); + } + else + cell = nullptr; +} + +InputDriveCell * +InputDrive::driveCell(const TransRiseFall *tr, + const MinMax *min_max) +{ + return drive_cells_[tr->index()][min_max->index()]; +} + +bool +InputDrive::hasDriveCell(const TransRiseFall *tr, + const MinMax *min_max) +{ + return drive_cells_[tr->index()][min_max->index()] != nullptr; +} + +bool +InputDrive::driveCellsEqual() +{ + int rise_index = TransRiseFall::riseIndex(); + int fall_index = TransRiseFall::fallIndex(); + int min_index = MinMax::minIndex(); + int max_index = MinMax::maxIndex(); + InputDriveCell *drive1 = drive_cells_[rise_index][min_index]; + InputDriveCell *drive2 = drive_cells_[rise_index][max_index]; + InputDriveCell *drive3 = drive_cells_[fall_index][min_index]; + InputDriveCell *drive4 = drive_cells_[fall_index][max_index]; + return drive1->equal(drive2) + && drive1->equal(drive3) + && drive1->equal(drive4); +} + +void +InputDrive::slew(const TransRiseFall *tr, + const MinMax *min_max, + float &slew, + bool &exists) +{ + slews_.value(tr, min_max, slew, exists); +} + +//////////////////////////////////////////////////////////////// + +InputDriveCell::InputDriveCell(LibertyLibrary *library, + LibertyCell *cell, + LibertyPort *from_port, + float *from_slews, + LibertyPort *to_port) : + library_(library), + cell_(cell), + from_port_(from_port), + to_port_(to_port) +{ + setFromSlews(from_slews); +} + +void +InputDriveCell::setLibrary(LibertyLibrary *library) +{ + library_ = library; +} + +void +InputDriveCell::setCell(LibertyCell *cell) +{ + cell_ = cell; +} + +void +InputDriveCell::setFromPort(LibertyPort *from_port) +{ + from_port_ = from_port; +} + +void +InputDriveCell::setToPort(LibertyPort *to_port) +{ + to_port_ = to_port; +} + +void +InputDriveCell::setFromSlews(float *from_slews) +{ + for (auto tr_index : TransRiseFall::rangeIndex()) + from_slews_[tr_index] = from_slews[tr_index]; +} + +bool +InputDriveCell::equal(InputDriveCell *drive) const +{ + int rise_index = TransRiseFall::riseIndex(); + int fall_index = TransRiseFall::fallIndex(); + return cell_ == drive->cell_ + && from_port_ == drive->from_port_ + && from_slews_[rise_index] == drive->from_slews_[rise_index] + && from_slews_[fall_index] == drive->from_slews_[fall_index] + && to_port_ == drive->to_port_; +} + +} // namespace diff --git a/sdc/InputDrive.hh b/sdc/InputDrive.hh new file mode 100644 index 0000000..8861fb3 --- /dev/null +++ b/sdc/InputDrive.hh @@ -0,0 +1,117 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_INPUT_DRIVE_H +#define STA_INPUT_DRIVE_H + +#include "DisallowCopyAssign.hh" +#include "LibertyClass.hh" +#include "NetworkClass.hh" +#include "MinMax.hh" +#include "RiseFallMinMax.hh" + +namespace sta { + +class InputDriveCell; + +// Input drive description from +// set_driving_cell +// set_drive +// set_input_transition +class InputDrive +{ +public: + explicit InputDrive(); + ~InputDrive(); + void setSlew(const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float slew); + void setDriveResistance(const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float res); + void driveResistance(const TransRiseFall *tr, + const MinMax *min_max, + float &res, + bool &exists); + bool hasDriveResistance(const TransRiseFall *tr, + const MinMax *min_max); + bool driveResistanceMinMaxEqual(const TransRiseFall *tr); + void setDriveCell(LibertyLibrary *library, + LibertyCell *cell, + LibertyPort *from_port, + float *from_slews, + LibertyPort *to_port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max); + void driveCell(const TransRiseFall *tr, + const MinMax *min_max, + LibertyCell *&cell, + LibertyPort *&from_port, + float *&from_slews, + LibertyPort *&to_port); + InputDriveCell *driveCell(const TransRiseFall *tr, + const MinMax *min_max); + bool hasDriveCell(const TransRiseFall *tr, + const MinMax *min_max); + // True if rise/fall/min/max drive cells are equal. + bool driveCellsEqual(); + void slew(const TransRiseFall *tr, + const MinMax *min_max, + float &slew, + bool &exists); + RiseFallMinMax *slews() { return &slews_; } + +private: + DISALLOW_COPY_AND_ASSIGN(InputDrive); + + RiseFallMinMax slews_; + RiseFallMinMax drive_resistances_; + // Separate rise/fall/min/max drive cells. + InputDriveCell *drive_cells_[TransRiseFall::index_count][MinMax::index_count]; +}; + +class InputDriveCell +{ +public: + InputDriveCell(LibertyLibrary *library, + LibertyCell *cell, + LibertyPort *from_port, + float *from_slews, + LibertyPort *to_port); + LibertyLibrary *library() const { return library_; } + void setLibrary(LibertyLibrary *library); + LibertyCell *cell() const { return cell_; } + void setCell(LibertyCell *cell); + LibertyPort *fromPort() const { return from_port_; } + void setFromPort(LibertyPort *from_port); + float *fromSlews() { return from_slews_; } + void setFromSlews(float *from_slews); + LibertyPort *toPort() const { return to_port_; } + void setToPort(LibertyPort *to_port); + bool equal(InputDriveCell *drive) const; + +private: + DISALLOW_COPY_AND_ASSIGN(InputDriveCell); + + LibertyLibrary *library_; + LibertyCell *cell_; + LibertyPort *from_port_; + float from_slews_[TransRiseFall::index_count]; + LibertyPort *to_port_; +}; + +} // namespace +#endif diff --git a/sdc/Makefile.am b/sdc/Makefile.am new file mode 100644 index 0000000..ed61ad2 --- /dev/null +++ b/sdc/Makefile.am @@ -0,0 +1,67 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +lib_LTLIBRARIES = libsdc.la + +include_HEADERS = \ + Clock.hh \ + ClockGatingCheck.hh \ + ClockGroups.hh \ + ClockInsertion.hh \ + ClockLatency.hh \ + CycleAccting.hh \ + DataCheck.hh \ + DeratingFactors.hh \ + DisabledPorts.hh \ + ExceptionPath.hh \ + InputDrive.hh \ + MinMaxValues.hh \ + PinPair.hh \ + PortDelay.hh \ + PortExtCap.hh \ + RiseFallMinMax.hh \ + RiseFallValues.hh \ + Sdc.hh \ + SdcClass.hh \ + SdcCmdComment.hh \ + WriteSdc.hh \ + WriteSdcPvt.hh + +libsdc_la_SOURCES = \ + Clock.cc \ + ClockGatingCheck.cc \ + ClockGroups.cc \ + ClockInsertion.cc \ + ClockLatency.cc \ + CycleAccting.cc \ + DataCheck.cc \ + DeratingFactors.cc \ + DisabledPorts.cc \ + ExceptionPath.cc \ + InputDrive.cc \ + PinPair.cc \ + PortDelay.cc \ + PortExtCap.cc \ + RiseFallMinMax.cc \ + RiseFallValues.cc \ + Sdc.cc \ + SdcCmdComment.cc \ + WriteSdc.cc + +libs: $(lib_LTLIBRARIES) + +xtags: $(SOURCES) $(HEADERS) + etags -a -o ../TAGS $(SOURCES) $(HEADERS) diff --git a/sdc/MinMaxValues.hh b/sdc/MinMaxValues.hh new file mode 100644 index 0000000..015adf3 --- /dev/null +++ b/sdc/MinMaxValues.hh @@ -0,0 +1,169 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_MIN_MAX_VALUES_H +#define STA_MIN_MAX_VALUES_H + +#include "MinMax.hh" +#include "Error.hh" + +namespace sta { + +template +class MinMaxValues +{ +public: + MinMaxValues() + { + clear(); + } + + MinMaxValues(TYPE init_value) + { + int mm_index; + + mm_index = MinMax::minIndex(); + values_[mm_index] = init_value; + exists_[mm_index] = true; + + mm_index = MinMax::maxIndex(); + values_[mm_index] = init_value; + exists_[mm_index] = true; + } + + void + clear() + { + exists_[MinMax::minIndex()] = false; + exists_[MinMax::maxIndex()] = false; + } + + void + clear(const MinMaxAll *min_max) + { + exists_[min_max->index()] = false; + } + + bool + empty() + { + return !exists_[MinMax::minIndex()] + && !exists_[MinMax::maxIndex()]; + } + + void + setValue(TYPE value) + { + for (auto mm_index : MinMax::rangeIndex()) { + values_[mm_index] = value; + exists_[mm_index] = true; + } + } + + void + setValue(const MinMaxAll *min_max, + TYPE value) + { + for (auto mm_index : min_max->rangeIndex()) { + values_[mm_index] = value; + exists_[mm_index] = true; + } + } + + void + setValue(const MinMax *min_max, + TYPE value) + { + int mm_index = min_max->index(); + values_[mm_index] = value; + exists_[mm_index] = true; + } + + void + mergeValue(const MinMax *min_max, + TYPE value) + { + int mm_index = min_max->index(); + if (!exists_[mm_index] + || min_max->compare(value, values_[mm_index])) { + values_[mm_index] = value; + exists_[mm_index] = true; + } + } + + TYPE + value(const MinMax *min_max) const + { + int mm_index = min_max->index(); + bool exists = exists_[mm_index]; + if (exists) + return values_[mm_index]; + else + internalError("uninitialized value reference"); + } + + void + value(const MinMax *min_max, + // Return values. + TYPE &value, + bool &exists) const + { + int mm_index = min_max->index(); + exists = exists_[mm_index]; + value = values_[mm_index]; + } + + bool + hasValue(const MinMax *min_max) const + { + int mm_index = min_max->index(); + return exists_[mm_index]; + } + + void + removeValue(const MinMaxAll *min_max) + { + for (auto mm_index : min_max->rangeIndex()) + exists_[mm_index] = false; + } + + static bool equal(MinMaxValues *values1, + 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()])) + && ((!values1->exists_[MinMax::maxIndex()] + && !values2->exists_[MinMax::maxIndex()]) + || (values1->exists_[MinMax::maxIndex()] + && values2->exists_[MinMax::maxIndex()] + && values1->values_[MinMax::maxIndex()] + == values2->values_[MinMax::maxIndex()])); + } + +private: + TYPE values_[MinMax::index_count]; + bool exists_[MinMax::index_count]; +}; + +typedef MinMaxValues MinMaxFloatValues; +typedef MinMaxValues MinMaxIntValues; + +} // namespace +#endif diff --git a/sdc/PinPair.cc b/sdc/PinPair.cc new file mode 100644 index 0000000..adf0952 --- /dev/null +++ b/sdc/PinPair.cc @@ -0,0 +1,48 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "PinPair.hh" + +namespace sta { + +bool +PinPairLess::operator()(const PinPair *pair1, + const PinPair *pair2) const +{ + return pair1->first < pair2->first + || (pair1->first == pair2->first + && pair1->second < pair2->second); +} + +bool +PinPairEqual::operator()(const PinPair *pair1, + const PinPair *pair2) const +{ + return pair1->first == pair2->first + && pair1->second == pair2->second; +} + +size_t +PinPairHash::operator()(const PinPair *pair) const +{ + size_t hash = hash_init_value; + hashIncr(hash, hashPtr(pair->first)); + hashIncr(hash, hashPtr(pair->second)); + return hash; +} + +} // namespace diff --git a/sdc/PinPair.hh b/sdc/PinPair.hh new file mode 100644 index 0000000..d14f512 --- /dev/null +++ b/sdc/PinPair.hh @@ -0,0 +1,51 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PIN_PAIR_H +#define STA_PIN_PAIR_H + +#include "Hash.hh" +#include "Set.hh" +#include "NetworkClass.hh" + +namespace sta { + +typedef std::pair PinPair; + +class PinPairLess +{ +public: + bool operator()(const PinPair *pair1, + const PinPair *pair2) const; +}; + +typedef Set PinPairSet; + +class PinPairHash +{ +public: + size_t operator()(const PinPair *pair) const; +}; + +class PinPairEqual +{ +public: + bool operator()(const PinPair *pair1, + const PinPair *pair2) const; +}; + +} // namespace +#endif diff --git a/sdc/PortDelay.cc b/sdc/PortDelay.cc new file mode 100644 index 0000000..14cc3cf --- /dev/null +++ b/sdc/PortDelay.cc @@ -0,0 +1,196 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Sdc.hh" +#include "Network.hh" +#include "PortDelay.hh" + +namespace sta { + +PortDelay::PortDelay(Pin *pin, + ClockEdge *clk_edge, + Pin *ref_pin) : + pin_(pin), + clk_edge_(clk_edge), + source_latency_included_(false), + network_latency_included_(false), + ref_pin_(ref_pin), + delays_() +{ +} + +Clock * +PortDelay::clock() const +{ + if (clk_edge_) + return clk_edge_->clock(); + else + return nullptr; +} + +bool +PortDelay::sourceLatencyIncluded() const +{ + return source_latency_included_; +} + +void +PortDelay::setSourceLatencyIncluded(bool included) +{ + source_latency_included_ = included; +} + +bool +PortDelay::networkLatencyIncluded() const +{ + return network_latency_included_; +} + +void +PortDelay::setNetworkLatencyIncluded(bool included) +{ + network_latency_included_ = included; +} + +TransRiseFall * +PortDelay::refTransition() const +{ + // Reference pin transition is the clock transition. + if (clk_edge_) + return clk_edge_->transition(); + else + return TransRiseFall::rise(); +} + +InputDelay::InputDelay(Pin *pin, + ClockEdge *clk_edge, + Pin *ref_pin, + int index, + Network *network) : + PortDelay(pin, clk_edge, ref_pin), + next_(nullptr), + index_(index) +{ + findVertexLoadPins(pin, network, &vertex_pins_); +} + +void +InputDelay::setNext(InputDelay *next) +{ + next_ = next; +} + +OutputDelay::OutputDelay(Pin *pin, + ClockEdge *clk_edge, + Pin *ref_pin, + Network *network) : + PortDelay(pin, clk_edge, ref_pin), + next_(nullptr) +{ + if (network) + findVertexDriverPins(pin, network, &vertex_pins_); +} + +void +OutputDelay::setNext(OutputDelay *next) +{ + next_ = next; +} + +//////////////////////////////////////////////////////////////// + +PinInputDelayIterator::PinInputDelayIterator(const Pin *pin, + const Sdc *sdc) +{ + next_ = sdc->input_delay_map_.findKey(pin); +} + +bool +PinInputDelayIterator::hasNext() +{ + return next_ != nullptr; +} + +InputDelay * +PinInputDelayIterator::next() +{ + InputDelay *next = next_; + if (next) + next_ = next_->next(); + return next; +} + +VertexPinInputDelayIterator::VertexPinInputDelayIterator(const Pin *vertex_pin, + const Sdc *sdc) : + PinInputDelayIterator() +{ + next_ = sdc->input_delay_vertex_map_.findKey(vertex_pin); +} + +//////////////////////////////////////////////////////////////// + +PinOutputDelayIterator::PinOutputDelayIterator(const Pin *pin, + const Sdc *sdc) +{ + next_ = sdc->output_delay_map_.findKey(pin); +} + +bool +PinOutputDelayIterator::hasNext() +{ + return next_ != nullptr; +} + +OutputDelay * +PinOutputDelayIterator::next() +{ + OutputDelay *next = next_; + if (next) + next_ = next_->next(); + return next; +} + +VertexPinOutputDelayIterator::VertexPinOutputDelayIterator(const Pin *vertex_pin, + const Sdc *sdc) : + PinOutputDelayIterator() +{ + next_ = sdc->output_delay_vertex_map_.findKey(vertex_pin); +} + +//////////////////////////////////////////////////////////////// + +PortDelayLess::PortDelayLess(const Network *network) : + network_(network) +{ +} + +bool +PortDelayLess::operator() (const PortDelay *delay1, + const PortDelay *delay2) const +{ + Pin *pin1 = delay1->pin(); + Pin *pin2 = delay2->pin(); + int pin_cmp = network_->pathNameCmp(pin1, pin2); + if (pin_cmp < 0) + return true; + else if (pin_cmp > 0) + return false; + else + return clkEdgeLess(delay1->clkEdge(), delay2->clkEdge()); +} + +} // namespace diff --git a/sdc/PortDelay.hh b/sdc/PortDelay.hh new file mode 100644 index 0000000..bbd0fd5 --- /dev/null +++ b/sdc/PortDelay.hh @@ -0,0 +1,194 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PORT_DELAY_H +#define STA_PORT_DELAY_H + +#include "DisallowCopyAssign.hh" +#include "RiseFallMinMax.hh" +#include "SdcClass.hh" + +namespace sta { + +class PortDelay; + +typedef Vector PortDelaySeq; + +// set_input_delay arrival, set_output_delay departure +class PortDelay +{ +public: + RiseFallMinMax *delays() { return &delays_; } + Pin *pin() const { return pin_; } + PinSet &vertexPins() { return vertex_pins_; } + Clock *clock() const; + ClockEdge *clkEdge() const { return clk_edge_; } + bool sourceLatencyIncluded() const; + void setSourceLatencyIncluded(bool included); + bool networkLatencyIncluded() const; + void setNetworkLatencyIncluded(bool included); + Pin *refPin() const { return ref_pin_; } + TransRiseFall *refTransition() const; + +protected: + PortDelay(Pin *pin, ClockEdge *clk_edge, Pin *ref_pin); + + Pin *pin_; + ClockEdge *clk_edge_; + bool source_latency_included_; + bool network_latency_included_; + Pin *ref_pin_; + RiseFallMinMax delays_; + PinSet vertex_pins_; + +private: + DISALLOW_COPY_AND_ASSIGN(PortDelay); +}; + +class InputDelay : public PortDelay +{ +public: + int index() const { return index_; } + +protected: + InputDelay(Pin *pin, + ClockEdge *clk_edge, + Pin *ref_pin, + int index, + Network *network); + InputDelay *next() { return next_; } + + // Linked list of port delays on the same pin wrt different clocks. + InputDelay *next_; + void setNext(InputDelay *next); + +private: + DISALLOW_COPY_AND_ASSIGN(InputDelay); + + int index_; + + friend class Sdc; + friend class PinInputDelayIterator; + friend class InputDelayIterator; +}; + +class InputDelayVertexPinIterator : public PinSet::Iterator +{ +public: + InputDelayVertexPinIterator(InputDelay *input_delay) : + PinSet::Iterator(input_delay->vertexPins()) + { + } +}; + +class OutputDelay : public PortDelay +{ +public: + +protected: + OutputDelay(Pin *pin, + ClockEdge *clk_edge, + Pin *ref_pin, + Network *network); + OutputDelay *next() { return next_; } + void setNext(OutputDelay *next); + + // Linked list of port delays on the same port wrt different clocks. + OutputDelay *next_; + +private: + DISALLOW_COPY_AND_ASSIGN(OutputDelay); + + friend class Sdc; + friend class PinOutputDelayIterator; + friend class OutputDelayIterator; +}; + +class OutputDelayVertexPinIterator : public PinSet::Iterator +{ +public: + OutputDelayVertexPinIterator(OutputDelay *output_delay) : + PinSet::Iterator(output_delay->vertexPins()) + { + } +}; + +class PinInputDelayIterator : public Iterator +{ +public: + PinInputDelayIterator(const Pin *pin, + const Sdc *sdc); + virtual bool hasNext(); + virtual InputDelay *next(); + +protected: + PinInputDelayIterator() {} + + InputDelay *next_; + + DISALLOW_COPY_AND_ASSIGN(PinInputDelayIterator); +}; + +class VertexPinInputDelayIterator : public PinInputDelayIterator +{ +public: + VertexPinInputDelayIterator(const Pin *vertex_pin, + const Sdc *sdc); + +private: + DISALLOW_COPY_AND_ASSIGN(VertexPinInputDelayIterator); +}; + +class PinOutputDelayIterator : public Iterator +{ +public: + PinOutputDelayIterator(const Pin *pin, + const Sdc *sdc); + virtual bool hasNext(); + virtual OutputDelay *next(); + +protected: + PinOutputDelayIterator() {} + + OutputDelay *next_; + + DISALLOW_COPY_AND_ASSIGN(PinOutputDelayIterator); +}; + +class VertexPinOutputDelayIterator : public PinOutputDelayIterator +{ +public: + VertexPinOutputDelayIterator(const Pin *vertex_pin, + const Sdc *sdc); + +private: + DISALLOW_COPY_AND_ASSIGN(VertexPinOutputDelayIterator); +}; + +// Prediate used to sort port delays. +class PortDelayLess +{ +public: + explicit PortDelayLess(const Network *network); + bool operator()(const PortDelay *delay1, + const PortDelay *delay2) const; + +private: + const Network *network_; +}; + +} // namespace +#endif diff --git a/sdc/PortExtCap.cc b/sdc/PortExtCap.cc new file mode 100644 index 0000000..a9d8b93 --- /dev/null +++ b/sdc/PortExtCap.cc @@ -0,0 +1,80 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "PortExtCap.hh" + +namespace sta { + +PortExtCap::PortExtCap(Port *port) : + port_(port) +{ +} + +void +PortExtCap::pinCap(const TransRiseFall *tr, + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) +{ + pin_cap_.value(tr, min_max, cap, exists); +} + +void +PortExtCap::setPinCap(float cap, + const TransRiseFall *tr, + const MinMax *min_max) +{ + pin_cap_.setValue(tr, min_max, cap); +} + +void +PortExtCap::wireCap(const TransRiseFall *tr, + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) +{ + wire_cap_.value(tr, min_max, cap, exists); +} + +void +PortExtCap::setWireCap(float cap, + const TransRiseFall *tr, + const MinMax *min_max) +{ + wire_cap_.setValue(tr, min_max, cap); +} + +void +PortExtCap::setFanout(int fanout, + const MinMax *min_max) +{ + fanout_.setValue(min_max, fanout); +} + + +void +PortExtCap::fanout(const MinMax *min_max, + // Return values. + int &fanout, + bool &exists) +{ + fanout_.value(min_max, fanout, exists); +} + +} // namespace diff --git a/sdc/PortExtCap.hh b/sdc/PortExtCap.hh new file mode 100644 index 0000000..06385c0 --- /dev/null +++ b/sdc/PortExtCap.hh @@ -0,0 +1,71 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PORT_EXT_CAP_H +#define STA_PORT_EXT_CAP_H + +#include "DisallowCopyAssign.hh" +#include "MinMax.hh" +#include "Transition.hh" +#include "RiseFallMinMax.hh" +#include "MinMaxValues.hh" +#include "NetworkClass.hh" + +namespace sta { + +typedef MinMaxIntValues FanoutValues; + +// Port external pin and wire capacitance (set_load -pin_load -wire_load). +class PortExtCap +{ +public: + explicit PortExtCap(Port *port); + Port *port() { return port_; } + void pinCap(const TransRiseFall *tr, + const MinMax *min_max, + // Return values. + float &cap, bool &exists); + RiseFallMinMax *pinCap() { return &pin_cap_; } + void setPinCap(float cap, + const TransRiseFall *tr, + const MinMax *min_max); + void wireCap(const TransRiseFall *tr, + const MinMax *min_max, + // Return values. + float &cap, bool &exists); + RiseFallMinMax *wireCap() { return &wire_cap_; } + void setWireCap(float cap, + const TransRiseFall *tr, + const MinMax *min_max); + void setFanout(int fanout, + const MinMax *min_max); + void fanout(const MinMax *min_max, + // Return values. + int &fanout, + bool &exists); + FanoutValues *fanout() { return &fanout_; } + +private: + DISALLOW_COPY_AND_ASSIGN(PortExtCap); + + Port *port_; + RiseFallMinMax pin_cap_; + RiseFallMinMax wire_cap_; + FanoutValues fanout_; +}; + +} // namespace +#endif diff --git a/sdc/RiseFallMinMax.cc b/sdc/RiseFallMinMax.cc new file mode 100644 index 0000000..949654e --- /dev/null +++ b/sdc/RiseFallMinMax.cc @@ -0,0 +1,267 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "RiseFallMinMax.hh" + +namespace sta { + +RiseFallMinMax::RiseFallMinMax() +{ + clear(); +} + +void +RiseFallMinMax::clear() +{ + for (int tr_index=0; tr_indexvalues_[tr_index][mm_index]; + exists_[tr_index][mm_index] = rfmm->exists_[tr_index][mm_index]; + } + } +} + +void +RiseFallMinMax::setValue(float value) +{ + setValue(TransRiseFallBoth::riseFall(), MinMaxAll::all(), value); +} + +void +RiseFallMinMax::setValue(const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float value) +{ + for (auto tr_index : tr->rangeIndex()) { + for (auto mm_index : min_max->rangeIndex()) { + values_[tr_index][mm_index] = value; + exists_[tr_index][mm_index] = true; + } + } +} + +void +RiseFallMinMax::removeValue(const TransRiseFallBoth *tr, + const MinMax *min_max) +{ + int mm_index = min_max->index(); + for (auto tr_index : tr->rangeIndex()) + exists_[tr_index][mm_index] = false; +} + +void +RiseFallMinMax::removeValue(const TransRiseFallBoth *tr, + const MinMaxAll *min_max) +{ + for (auto mm : min_max->range()) + removeValue(tr, mm); +} + +void +RiseFallMinMax::mergeValue(const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float value) +{ + for (auto tr_index : tr->rangeIndex()) { + for (auto mm : min_max->range()) { + int mm_index = mm->index(); + if (!exists_[tr_index][mm_index] + || mm->compare(value, values_[tr_index][mm_index])) { + values_[tr_index][mm_index] = value; + exists_[tr_index][mm_index] = true; + } + } + } +} + +void +RiseFallMinMax::setValue(const TransRiseFallBoth *tr, + const MinMax *min_max, + float value) +{ + int mm_index = min_max->index(); + for (auto tr_index : tr->rangeIndex()) { + values_[tr_index][mm_index] = value; + exists_[tr_index][mm_index] = true; + } +} + +void +RiseFallMinMax::setValue(const TransRiseFall *tr, + const MinMax *min_max, + float value) +{ + int tr_index = tr->index(); + int mm_index = min_max->index(); + values_[tr_index][mm_index] = value; + exists_[tr_index][mm_index] = true; +} + +void +RiseFallMinMax::setValues(RiseFallMinMax *values) +{ + for (int tr_index=0;tr_indexvalues_[tr_index][mm_index]; + exists_[tr_index][mm_index] = values->exists_[tr_index][mm_index]; + } + } +} + +void +RiseFallMinMax::value(const TransRiseFall *tr, + const MinMax *min_max, + float &value, bool &exists) const +{ + exists = exists_[tr->index()][min_max->index()]; + if (exists) + value = values_[tr->index()][min_max->index()]; +} + +float +RiseFallMinMax::value(const TransRiseFall *tr, + const MinMax *min_max) const +{ + return values_[tr->index()][min_max->index()]; +} + +bool +RiseFallMinMax::hasValue() const +{ + return !empty(); +} + +bool +RiseFallMinMax::empty() const +{ + for (int tr_index=0;tr_indexindex()][min_max->index()]; +} + +void +RiseFallMinMax::mergeWith(RiseFallMinMax *rfmm) +{ + for (auto min_max : MinMax::range()) { + int mm_index = min_max->index(); + for (auto tr_index : TransRiseFall::rangeIndex()) { + bool exists1 = exists_[tr_index][mm_index]; + bool exists2 = rfmm->exists_[tr_index][mm_index]; + if (exists1 && exists2) { + float rfmm_value = rfmm->values_[tr_index][mm_index]; + if (min_max->compare(rfmm_value, values_[tr_index][mm_index])) + values_[tr_index][mm_index] = rfmm_value; + } + else if (!exists1 && exists2) { + values_[tr_index][mm_index] = rfmm->values_[tr_index][mm_index]; + exists_[tr_index][mm_index] = true; + } + } + } +} + +bool +RiseFallMinMax::equal(const RiseFallMinMax *values) const +{ + for (int tr_index=0;tr_indexexists_[tr_index][mm_index]; + if (exists1 != exists2) + return false; + if (exists1 && exists2 + && values_[tr_index][mm_index] != values->values_[tr_index][mm_index]) + return false; + } + } + return true; +} + +bool +RiseFallMinMax::isOneValue() const +{ + float value; + return isOneValue(value); +} + +bool +RiseFallMinMax::isOneValue(float &value) const +{ + if (exists_[0][0]) { + value = values_[0][0]; + for (int tr_index=0;tr_indexindex(); + if (exists_[0][mm_index]) { + value = values_[0][mm_index]; + for (int tr_index=0;tr_index. + +#ifndef STA_RISE_FALL_MIN_MAX_H +#define STA_RISE_FALL_MIN_MAX_H + +#include "DisallowCopyAssign.hh" +#include "Transition.hh" +#include "MinMax.hh" + +namespace sta { + +// Rise/Fall/Min/Max group of four values common to many constraints. +class RiseFallMinMax +{ +public: + RiseFallMinMax(); + RiseFallMinMax(const RiseFallMinMax *rfmm); + explicit RiseFallMinMax(float init_value); + float value(const TransRiseFall *tr, + const MinMax *min_max) const; + void value(const TransRiseFall *tr, + const MinMax *min_max, + float &value, + bool &exists) const; + bool hasValue() const; + bool empty() const; + bool hasValue(const TransRiseFall *tr, + const MinMax *min_max) const; + void setValue(const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float value); + void setValue(const TransRiseFallBoth *tr, + const MinMax *min_max, + float value); + void setValue(const TransRiseFall *tr, + const MinMax *min_max, float value); + void setValue(float value); + void mergeValue(const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float value); + void setValues(RiseFallMinMax *values); + void removeValue(const TransRiseFallBoth *tr, + const MinMax *min_max); + void removeValue(const TransRiseFallBoth *tr, + const MinMaxAll *min_max); + // Merge all values of rfmm. + void mergeWith(RiseFallMinMax *rfmm); + void clear(); + bool equal(const RiseFallMinMax *values) const; + bool isOneValue() const; + bool isOneValue(float &value) const; + bool isOneValue(const MinMax *min_max, + // Return values. + float &value) const; + +private: + float values_[TransRiseFall::index_count][MinMax::index_count]; + bool exists_[TransRiseFall::index_count][MinMax::index_count]; +}; + +} // namespace +#endif diff --git a/sdc/RiseFallValues.cc b/sdc/RiseFallValues.cc new file mode 100644 index 0000000..cea6864 --- /dev/null +++ b/sdc/RiseFallValues.cc @@ -0,0 +1,98 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "RiseFallValues.hh" + +namespace sta { + +RiseFallValues::RiseFallValues() +{ + clear(); +} + +void +RiseFallValues::clear() +{ + for (auto tr_index : TransRiseFall::rangeIndex()) + exists_[tr_index] = false; +} + +RiseFallValues::RiseFallValues(float init_value) +{ + for (auto tr_index : TransRiseFall::rangeIndex()) { + values_[tr_index] = init_value; + exists_[tr_index] = true; + } +} + +void +RiseFallValues::setValue(float value) +{ + setValue(TransRiseFallBoth::riseFall(), value); +} + +void +RiseFallValues::setValue(const TransRiseFallBoth *tr, + float value) +{ + for (auto tr_index : tr->rangeIndex()) { + values_[tr_index] = value; + exists_[tr_index] = true; + } +} + +void +RiseFallValues::setValue(const TransRiseFall *tr, + float value) +{ + int tr_index = tr->index(); + values_[tr_index] = value; + exists_[tr_index] = true; +} + +void +RiseFallValues::setValues(RiseFallValues *values) +{ + for (auto tr_index : TransRiseFall::rangeIndex()) { + values_[tr_index] = values->values_[tr_index]; + exists_[tr_index] = values->exists_[tr_index]; + } +} + +void +RiseFallValues::value(const TransRiseFall *tr, + float &value, bool &exists) const +{ + int tr_index = tr->index(); + exists = exists_[tr_index]; + if (exists) + value = values_[tr_index]; +} + +float +RiseFallValues::value(const TransRiseFall *tr) const +{ + return values_[tr->index()]; +} + +bool +RiseFallValues::hasValue(const TransRiseFall *tr) const +{ + return exists_[tr->index()]; +} + +} // namespace diff --git a/sdc/RiseFallValues.hh b/sdc/RiseFallValues.hh new file mode 100644 index 0000000..7ecc539 --- /dev/null +++ b/sdc/RiseFallValues.hh @@ -0,0 +1,49 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_RISE_FALL_VALUES_H +#define STA_RISE_FALL_VALUES_H + +#include "DisallowCopyAssign.hh" +#include "Transition.hh" + +namespace sta { + +// Rise/fall group of two values. +class RiseFallValues +{ +public: + RiseFallValues(); + explicit RiseFallValues(float init_value); + float value(const TransRiseFall *tr) const; + void value(const TransRiseFall *tr, + float &value, bool &exists) const; + bool hasValue(const TransRiseFall *tr) const; + void setValue(const TransRiseFallBoth *tr, float value); + void setValue(const TransRiseFall *tr, float value); + void setValue(float value); + void setValues(RiseFallValues *values); + void clear(); + +private: + DISALLOW_COPY_AND_ASSIGN(RiseFallValues); + + float values_[TransRiseFall::index_count]; + bool exists_[TransRiseFall::index_count]; +}; + +} // namespace +#endif diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc new file mode 100644 index 0000000..e6f297a --- /dev/null +++ b/sdc/Sdc.cc @@ -0,0 +1,6731 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "Stats.hh" +#include "Debug.hh" +#include "Mutex.hh" +#include "Report.hh" +#include "PatternMatch.hh" +#include "MinMax.hh" +#include "PortDirection.hh" +#include "RiseFallMinMax.hh" +#include "Transition.hh" +#include "TimingRole.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Clock.hh" +#include "ClockLatency.hh" +#include "ClockInsertion.hh" +#include "CycleAccting.hh" +#include "PortDelay.hh" +#include "ExceptionPath.hh" +#include "PortExtCap.hh" +#include "DisabledPorts.hh" +#include "InputDrive.hh" +#include "DataCheck.hh" +#include "ClockGatingCheck.hh" +#include "ClockGroups.hh" +#include "DeratingFactors.hh" +#include "Graph.hh" +#include "Levelize.hh" +#include "HpinDrvrLoad.hh" +#include "Corner.hh" +#include "Sdc.hh" + +namespace sta { + +bool +ClockPairLess::operator()(const ClockPair &pair1, + const ClockPair &pair2) const +{ + int first1 = pair1.first->index(); + int second1 = pair1.second->index(); + if (first1 > second1) + std::swap(first1, second1); + int first2 = pair2.first->index(); + int second2 = pair2.second->index(); + if (first2 > second2) + std::swap(first2, second2); + return (first1 < first2) + || (first1 == first2 + && second1 < second2); +} + +//////////////////////////////////////////////////////////////// + +typedef Vector ClockPairSeq; +typedef Set PvtSet; + +static ExceptionThruSeq * +clone(ExceptionThruSeq *thrus, + Network *network); +static void +annotateGraphDisabledWireEdge(Pin *from_pin, + Pin *to_pin, + bool annotate, + Graph *graph); + +//////////////////////////////////////////////////////////////// + +Sdc::Sdc(StaState *sta) : + StaState(sta), + derating_factors_(nullptr), + net_derating_factors_(nullptr), + inst_derating_factors_(nullptr), + cell_derating_factors_(nullptr), + clk_index_(0), + clk_insertions_(nullptr), + clk_group_exclusions_(nullptr), + clk_group_same_(nullptr), + clk_sense_map_(network_), + clk_gating_check_(nullptr), + input_delay_index_(0), + port_cap_map_(nullptr), + net_wire_cap_map_(nullptr), + drvr_pin_wire_cap_map_(nullptr), + first_from_pin_exceptions_(nullptr), + first_from_clk_exceptions_(nullptr), + first_from_inst_exceptions_(nullptr), + first_thru_pin_exceptions_(nullptr), + first_thru_inst_exceptions_(nullptr), + first_thru_net_exceptions_(nullptr), + first_to_pin_exceptions_(nullptr), + first_to_clk_exceptions_(nullptr), + first_to_inst_exceptions_(nullptr), + first_thru_edge_exceptions_(nullptr), + path_delay_internal_startpoints_(nullptr), + path_delay_internal_endpoints_(nullptr) +{ + initVariables(); + sdc_ = this; + setWireload(nullptr, MinMaxAll::all()); + setWireloadSelection(nullptr, MinMaxAll::all()); + setOperatingConditions(nullptr, MinMaxAll::all()); + makeDefaultArrivalClock(); + initInstancePvtMaps(); +} + +void +Sdc::makeDefaultArrivalClock() +{ + FloatSeq *waveform = new FloatSeq; + waveform->push_back(0.0); + waveform->push_back(0.0); + default_arrival_clk_ = new Clock("input port clock", clk_index_++); + default_arrival_clk_->initClk(0, false, 0.0, waveform, nullptr, network_); +} + +Sdc::~Sdc() +{ + deleteConstraints(); +} + +// This does NOT call initVariables() because those variable values +// survive linking a new design. +void +Sdc::clear() +{ + removeLibertyAnnotations(); + deleteConstraints(); + propagated_clk_pins_.clear(); + clocks_.clear(); + clock_name_map_.clear(); + clock_pin_map_.clear(); + clock_vertex_pin_map_.clear(); + clk_latencies_.clear(); + edge_clk_latency_.clear(); + if (clk_insertions_) + clk_insertions_->clear(); + + pin_clk_uncertainty_map_.clear(); + inter_clk_uncertainties_.clear(); + + clk_groups_name_map_.clear(); + clearClkGroupExclusions(); + + clk_gating_check_map_.clear(); + inst_clk_gating_check_map_.clear(); + pin_clk_gating_check_map_.clear(); + data_checks_from_map_.clear(); + data_checks_to_map_.clear(); + + input_delay_map_.clear(); + input_delay_index_ = 0; + input_delay_ref_pin_map_.clear(); + input_delay_vertex_map_.clear(); + input_delay_internal_pin_map_.clear(); + output_delay_map_.clear(); + output_delay_vertex_map_.clear(); + + port_slew_limit_map_.clear(); + cell_slew_limit_map_.clear(); + have_clk_slew_limits_ = false; + + cell_cap_limit_map_.clear(); + port_cap_limit_map_.clear(); + pin_cap_limit_map_.clear(); + + port_fanout_limit_map_.clear(); + cell_fanout_limit_map_.clear(); + + disabled_pins_.clear(); + disabled_ports_.clear(); + disabled_lib_ports_.clear(); + disabled_edges_.clear(); + disabled_cell_ports_.clear(); + disabled_inst_ports_.clear(); + + disabled_clk_gating_checks_inst_.clear(); + disabled_clk_gating_checks_pin_.clear(); + + input_drive_map_.clear(); + logic_value_map_.clear(); + case_value_map_.clear(); + + pin_latch_borrow_limit_map_.clear(); + inst_latch_borrow_limit_map_.clear(); + clk_latch_borrow_limit_map_.clear(); + + min_pulse_width_.clear(); + + setWireload(nullptr, MinMaxAll::all()); + setWireloadSelection(nullptr, MinMaxAll::all()); + // Operating conditions are owned by Liberty libraries. + setOperatingConditions(nullptr, MinMaxAll::all()); + clk_index_ = 0; + makeDefaultArrivalClock(); + + unsetTimingDerate(); +} + +void +Sdc::initVariables() +{ + analysis_type_ = AnalysisType::ocv; + use_default_arrival_clock_ = false; + 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; + wireload_mode_ = WireloadMode::unknown; + max_area_ = 0.0; + path_delays_without_to_ = false; + clk_hpin_disables_valid_ = false; +} + +void +Sdc::deleteConstraints() +{ + clocks_.deleteContents(); + delete default_arrival_clk_; + clock_pin_map_.deleteContents(); + clock_vertex_pin_map_.deleteContents(); + clk_latencies_.deleteContents(); + if (clk_insertions_) { + clk_insertions_->deleteContents(); + delete clk_insertions_; + clk_insertions_ = nullptr; + } + + clk_groups_name_map_.deleteContents(); + clearClkGroupExclusions(); + + pin_clk_uncertainty_map_.deleteContents(); + inter_clk_uncertainties_.deleteContents(); + 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(); + + DataChecksMap::Iterator data_checks_iter1(data_checks_from_map_); + while (data_checks_iter1.hasNext()) { + DataCheckSet *checks = data_checks_iter1.next(); + checks->deleteContents(); + delete checks; + } + + DataChecksMap::Iterator data_checks_iter2(data_checks_to_map_); + while (data_checks_iter2.hasNext()) { + DataCheckSet *checks = data_checks_iter2.next(); + delete checks; + } + + InputDelayMap::Iterator input_iter(input_delay_map_); + while (input_iter.hasNext()) { + InputDelay *input_delay = input_iter.next(); + while (input_delay) { + InputDelay *next = input_delay->next(); + delete input_delay; + input_delay = next; + } + } + + InputDelayRefPinMap::Iterator input_set_iter(input_delay_ref_pin_map_); + while (input_set_iter.hasNext()) { + InputDelaySet *input_delays = input_set_iter.next(); + delete input_delays; + } + input_delay_internal_pin_map_.deleteContents(); + + OutputDelayMap::Iterator output_iter(output_delay_map_); + while (output_iter.hasNext()) { + OutputDelay *output_delay = output_iter.next(); + while (output_delay) { + OutputDelay *next = output_delay->next(); + delete output_delay; + output_delay = next; + } + } + + clk_hpin_disables_.deleteContentsClear(); + clk_hpin_disables_valid_ = false; + + clearCycleAcctings(); + deleteExceptions(); + clearGroupPathMap(); + deleteInstancePvts(); + deleteDeratingFactors(); + removeLoadCaps(); + clk_sense_map_.clear(); +} + +void +Sdc::deleteInstancePvts() +{ + // Multiple instances can share a pvt, so put them in a set + // so they are only deleted once. + PvtSet pvts; + for (auto mm_index : MinMax::rangeIndex()) { + InstancePvtMap *pvt_map = instance_pvt_maps_[mm_index]; + InstancePvtMap::Iterator pvt_iter(pvt_map); + while (pvt_iter.hasNext()) { + Pvt *pvt = pvt_iter.next(); + pvts.insert(pvt); + } + delete pvt_map; + } + pvts.deleteContents(); +} + +void +Sdc::removeNetLoadCaps() +{ + delete [] net_wire_cap_map_; + net_wire_cap_map_ = nullptr; + + delete [] drvr_pin_wire_cap_map_; + drvr_pin_wire_cap_map_ = nullptr; +} + +void +Sdc::removeLoadCaps() +{ + if (port_cap_map_) { + port_cap_map_->deleteContents(); + delete port_cap_map_; + port_cap_map_ = nullptr; + } + removeNetLoadCaps(); +} + +void +Sdc::removeLibertyAnnotations() +{ + DisabledCellPortsMap::Iterator disabled_iter(disabled_cell_ports_); + while (disabled_iter.hasNext()) { + DisabledCellPorts *disable = disabled_iter.next(); + LibertyCell *cell = disable->cell(); + if (disable->all()) + cell->setIsDisabledConstraint(false); + + LibertyPortSet::Iterator from_iter(disable->from()); + while (from_iter.hasNext()) { + LibertyPort *from = from_iter.next(); + from->setIsDisabledConstraint(false); + } + + LibertyPortSet::Iterator to_iter(disable->to()); + while (to_iter.hasNext()) { + LibertyPort *to = to_iter.next(); + to->setIsDisabledConstraint(false); + } + + if (disable->timingArcSets()) { + TimingArcSetSet::Iterator arc_iter(disable->timingArcSets()); + while (arc_iter.hasNext()) { + TimingArcSet *arc_set = arc_iter.next(); + arc_set->setIsDisabledConstraint(false); + } + } + + LibertyPortPairSet::Iterator from_to_iter(disable->fromTo()); + while (from_to_iter.hasNext()) { + LibertyPortPair *pair = from_to_iter.next(); + const LibertyPort *from = pair->first; + const LibertyPort *to = pair->second; + LibertyCellTimingArcSetIterator arc_iter(cell, from, to); + while (arc_iter.hasNext()) { + TimingArcSet *arc_set = arc_iter.next(); + arc_set->setIsDisabledConstraint(false); + } + } + } + + LibertyPortSet::Iterator port_iter(disabled_lib_ports_); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + port->setIsDisabledConstraint(false); + } +} + +void +Sdc::initInstancePvtMaps() +{ + for (auto mm_index : MinMax::rangeIndex()) + instance_pvt_maps_[mm_index] = nullptr; +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::searchPreamble() +{ + ensureClkHpinDisables(); + ensureClkGroupExclusions(); +} + +//////////////////////////////////////////////////////////////// + +bool +Sdc::isConstrained(const Pin *pin) const +{ + Pin *pin1 = const_cast(pin); + Port *port = network_->isTopLevelPort(pin) ? network_->port(pin) : nullptr; + return clock_pin_map_.hasKey(pin) + || propagated_clk_pins_.hasKey(pin1) + || 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_map_.hasKey(pin) + || output_delay_map_.hasKey(pin) + || port_slew_limit_map_.hasKey(port) + || pin_cap_limit_map_.hasKey(pin1) + || port_cap_limit_map_.hasKey(port) + || port_fanout_limit_map_.hasKey(port) + || hasPortExtCap(port) + || disabled_pins_.hasKey(pin1) + || disabled_ports_.hasKey(port) + || disabled_clk_gating_checks_pin_.hasKey(pin1) + || (first_from_pin_exceptions_ + && first_from_pin_exceptions_->hasKey(pin)) + || (first_thru_pin_exceptions_ + && first_thru_pin_exceptions_->hasKey(pin)) + || (first_to_pin_exceptions_ + && 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); +} + +bool +Sdc::isConstrained(const Instance *inst) const +{ + Instance *inst1 = const_cast(inst); + return (instance_pvt_maps_[MinMax::minIndex()] + && instance_pvt_maps_[MinMax::minIndex()]->hasKey(inst1)) + || (instance_pvt_maps_[MinMax::maxIndex()] + && instance_pvt_maps_[MinMax::maxIndex()]->hasKey(inst1)) + || (inst_derating_factors_ + && inst_derating_factors_->hasKey(inst)) + || inst_clk_gating_check_map_.hasKey(inst) + || disabled_inst_ports_.hasKey(inst1) + || (first_from_inst_exceptions_ + && first_from_inst_exceptions_->hasKey(inst)) + || (first_thru_inst_exceptions_ + && first_thru_inst_exceptions_->hasKey(inst)) + || (first_to_inst_exceptions_->hasKey(inst) + && first_to_inst_exceptions_) + || inst_latch_borrow_limit_map_.hasKey(inst) + || inst_min_pulse_width_map_.hasKey(inst); +} + +bool +Sdc::isConstrained(const Net *net) const +{ + Net *net1 = const_cast(net); + return (net_derating_factors_ + && net_derating_factors_->hasKey(net)) + || hasNetWireCap(net1) + || net_res_map_.hasKey(net1) + || (first_thru_net_exceptions_ + && first_thru_net_exceptions_->hasKey(net)); +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setAnalysisType(AnalysisType analysis_type) +{ + analysis_type_ = analysis_type; +} + +void +Sdc::setOperatingConditions(OperatingConditions *op_cond, + const MinMaxAll *min_max) +{ + for (auto mm_index : min_max->rangeIndex()) + operating_conditions_[mm_index] = op_cond; +} + +void +Sdc::setOperatingConditions(OperatingConditions *op_cond, + const MinMax *min_max) +{ + int mm_index = min_max->index(); + operating_conditions_[mm_index] = op_cond; +} + +OperatingConditions * +Sdc::operatingConditions(const MinMax *min_max) +{ + int mm_index = min_max->index(); + return operating_conditions_[mm_index]; +} + +Pvt * +Sdc::pvt(Instance *inst, + const MinMax *min_max) const +{ + InstancePvtMap *pvt_map = instance_pvt_maps_[min_max->index()]; + if (pvt_map) + return pvt_map->findKey(inst); + else + return nullptr; +} + +void +Sdc::setPvt(Instance *inst, const + MinMaxAll *min_max, + Pvt *pvt) +{ + for (auto mm_index : min_max->rangeIndex()) { + InstancePvtMap *pvt_map = instance_pvt_maps_[mm_index]; + if (pvt_map == nullptr) { + pvt_map = new InstancePvtMap; + instance_pvt_maps_[mm_index] = pvt_map; + } + (*pvt_map)[inst] = pvt; + } +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setTimingDerate(TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate) +{ + if (derating_factors_ == nullptr) + derating_factors_ = new DeratingFactorsGlobal; + derating_factors_->setFactor(type, clk_data, tr, early_late, derate); +} + +void +Sdc::setTimingDerate(const Net *net, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate) +{ + if (net_derating_factors_ == nullptr) + net_derating_factors_ = new NetDeratingFactorsMap; + DeratingFactorsNet *factors = net_derating_factors_->findKey(net); + if (factors == nullptr) { + factors = new DeratingFactorsNet; + (*net_derating_factors_)[net] = factors; + } + factors->setFactor(clk_data, tr, early_late, derate); +} + +void +Sdc::setTimingDerate(const Instance *inst, + TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate) +{ + if (inst_derating_factors_ == nullptr) + inst_derating_factors_ = new InstDeratingFactorsMap; + DeratingFactorsCell *factors = inst_derating_factors_->findKey(inst); + if (factors == nullptr) { + factors = new DeratingFactorsCell; + (*inst_derating_factors_)[inst] = factors; + } + factors->setFactor(type, clk_data, tr, early_late, derate); +} + +void +Sdc::setTimingDerate(const LibertyCell *cell, + TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate) +{ + if (cell_derating_factors_ == nullptr) + cell_derating_factors_ = new CellDeratingFactorsMap; + DeratingFactorsCell *factors = cell_derating_factors_->findKey(cell); + if (factors == nullptr) { + factors = new DeratingFactorsCell; + (*cell_derating_factors_)[cell] = factors; + } + factors->setFactor(type, clk_data, tr, early_late, derate); +} + +float +Sdc::timingDerateInstance(const Pin *pin, + TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFall *tr, + const EarlyLate *early_late) const +{ + if (inst_derating_factors_) { + const Instance *inst = network_->instance(pin); + DeratingFactorsCell *factors = inst_derating_factors_->findKey(inst); + if (factors) { + float factor; + bool exists; + factors->factor(type, clk_data, tr, early_late, factor, exists); + if (exists) + return factor; + } + } + + if (cell_derating_factors_) { + const Instance *inst = network_->instance(pin); + const LibertyCell *cell = network_->libertyCell(inst); + if (cell) { + DeratingFactorsCell *factors = cell_derating_factors_->findKey(cell); + float factor; + bool exists; + if (factors) { + factors->factor(type, clk_data, tr, early_late, factor, exists); + if (exists) + return factor; + } + } + } + if (derating_factors_) { + float factor; + bool exists; + derating_factors_->factor(type, clk_data, tr, early_late, factor, exists); + if (exists) + return factor; + } + return 1.0; +} + +float +Sdc::timingDerateNet(const Pin *pin, + PathClkOrData clk_data, + const TransRiseFall *tr, + const EarlyLate *early_late) const +{ + if (net_derating_factors_) { + const Net *net = network_->net(pin); + DeratingFactorsNet *factors = net_derating_factors_->findKey(net); + if (factors) { + float factor; + bool exists; + factors->factor(clk_data, tr, early_late, factor, exists); + if (exists) + return factor; + } + } + if (derating_factors_) { + float factor; + bool exists; + derating_factors_->factor(TimingDerateType::net_delay, clk_data, tr, + early_late, factor, exists); + if (exists) + return factor; + } + return 1.0; +} + +void +Sdc::unsetTimingDerate() +{ + deleteDeratingFactors(); +} + +void +Sdc::deleteDeratingFactors() +{ + if (net_derating_factors_) { + NetDeratingFactorsMap::Iterator net_iter(net_derating_factors_); + while (net_iter.hasNext()) { + DeratingFactorsNet *factors = net_iter.next(); + delete factors; + } + delete net_derating_factors_; + net_derating_factors_ = nullptr; + } + + if (inst_derating_factors_) { + InstDeratingFactorsMap::Iterator inst_iter(inst_derating_factors_); + while (inst_iter.hasNext()) { + DeratingFactorsCell *factors = inst_iter.next(); + delete factors; + } + delete inst_derating_factors_; + inst_derating_factors_ = nullptr; + } + + if (cell_derating_factors_) { + CellDeratingFactorsMap::Iterator cell_iter(cell_derating_factors_); + while (cell_iter.hasNext()) { + DeratingFactorsCell *factors = cell_iter.next(); + delete factors; + } + delete cell_derating_factors_; + cell_derating_factors_ = nullptr; + } + + delete derating_factors_; + derating_factors_ = nullptr; +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setDriveCell(LibertyLibrary *library, + LibertyCell *cell, + Port *port, + LibertyPort *from_port, + float *from_slews, + LibertyPort *to_port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max) +{ + ensureInputDrive(port)->setDriveCell(library, cell, from_port, from_slews, + to_port, tr, min_max); +} + +void +Sdc::setInputSlew(Port *port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float slew) +{ + ensureInputDrive(port)->setSlew(tr, min_max, slew); +} + +void +Sdc::setDriveResistance(Port *port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float res) +{ + ensureInputDrive(port)->setDriveResistance(tr, min_max, res); +} + +InputDrive * +Sdc::ensureInputDrive(Port *port) +{ + InputDrive *drive = input_drive_map_.findKey(port); + if (drive == nullptr) { + drive = new InputDrive; + input_drive_map_[port] = drive; + } + return drive; +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setSlewLimit(Clock *clk, + const TransRiseFallBoth *tr, + const PathClkOrData clk_data, + const MinMax *min_max, + float slew) +{ + clk->setSlewLimit(tr, clk_data, min_max, slew); + have_clk_slew_limits_ = true; +} + +bool +Sdc::haveClkSlewLimits() const +{ + return have_clk_slew_limits_; +} + +void +Sdc::slewLimit(Clock *clk, const TransRiseFall *tr, + const PathClkOrData clk_data, + const MinMax *min_max, + float &slew, + bool &exists) +{ + clk->slewLimit(tr, clk_data, min_max, slew, exists); +} + +void +Sdc::slewLimit(Port *port, + const MinMax *min_max, + float &slew, + bool &exists) +{ + slew = 0.0; + MinMaxFloatValues values; + port_slew_limit_map_.findKey(port, values, exists); + if (exists) + values.value(min_max, slew, exists); +} + +void +Sdc::setSlewLimit(Port *port, + const MinMax *min_max, + float slew) +{ + MinMaxFloatValues &values = port_slew_limit_map_[port]; + values.setValue(min_max, slew); +} + +void +Sdc::slewLimit(const Pin *pin, + const MinMax *min_max, + float &slew, + bool &exists) +{ + slew = 0.0; + MinMaxFloatValues values; + pin_slew_limit_map_.findKey(pin, values, exists); + if (exists) + values.value(min_max, slew, exists); +} + +void +Sdc::setSlewLimit(const Pin *pin, + const MinMax *min_max, + float slew) +{ + MinMaxFloatValues &values = pin_slew_limit_map_[pin]; + values.setValue(min_max, slew); +} + +void +Sdc::slewLimitPins(ConstPinSeq &pins) +{ + PinSlewLimitMap::Iterator iter(pin_slew_limit_map_); + while (iter.hasNext()) { + const Pin *pin; + MinMaxFloatValues values; + iter.next(pin, values); + pins.push_back(pin); + } +} + +void +Sdc::slewLimit(Cell *cell, + const MinMax *min_max, + float &slew, + bool &exists) +{ + slew = 0.0; + MinMaxFloatValues values; + cell_slew_limit_map_.findKey(cell, values, exists); + if (exists) + values.value(min_max, slew, exists); +} + +void +Sdc::setSlewLimit(Cell *cell, + const MinMax *min_max, + float slew) +{ + MinMaxFloatValues &values = cell_slew_limit_map_[cell]; + values.setValue(min_max, slew); +} + +void +Sdc::capacitanceLimit(Cell *cell, + const MinMax *min_max, + float &cap, + bool &exists) +{ + cap = 0.0; + exists = false; + MinMaxFloatValues values; + cell_cap_limit_map_.findKey(cell, values, exists); + if (exists) + values.value(min_max, cap, exists); +} + +void +Sdc::setCapacitanceLimit(Cell *cell, + const MinMax *min_max, + float cap) +{ + MinMaxFloatValues &values = cell_cap_limit_map_[cell]; + values.setValue(min_max, cap); +} + +void +Sdc::capacitanceLimit(Port *port, + const MinMax *min_max, + float &cap, + bool &exists) +{ + cap = 0.0; + exists = false; + MinMaxFloatValues values; + port_cap_limit_map_.findKey(port, values, exists); + if (exists) + values.value(min_max, cap, exists); +} + +void +Sdc::setCapacitanceLimit(Port *port, + const MinMax *min_max, + float cap) +{ + MinMaxFloatValues &values = port_cap_limit_map_[port]; + values.setValue(min_max, cap); +} + +void +Sdc::capacitanceLimit(Pin *pin, + const MinMax *min_max, + float &cap, + bool &exists) +{ + cap = 0.0; + exists = false; + MinMaxFloatValues values; + pin_cap_limit_map_.findKey(pin, values, exists); + if (exists) + values.value(min_max, cap, exists); +} + +void +Sdc::setCapacitanceLimit(Pin *pin, + const MinMax *min_max, + float cap) +{ + MinMaxFloatValues &values = pin_cap_limit_map_[pin]; + values.setValue(min_max, cap); +} + +void +Sdc::fanoutLimit(Cell *cell, + const MinMax *min_max, + float &fanout, + bool &exists) +{ + fanout = 0.0; + MinMaxFloatValues values; + cell_fanout_limit_map_.findKey(cell, values, exists); + if (exists) + values.value(min_max, fanout, exists); +} + +void +Sdc::setFanoutLimit(Cell *cell, + const MinMax *min_max, + float fanout) +{ + MinMaxFloatValues &values = cell_fanout_limit_map_[cell]; + values.setValue(min_max, fanout); +} + +void +Sdc::fanoutLimit(Port *port, + const MinMax *min_max, + float &fanout, + bool &exists) +{ + fanout = 0.0; + MinMaxFloatValues values; + port_fanout_limit_map_.findKey(port, values, exists); + if (exists) + values.value(min_max, fanout, exists); +} + +void +Sdc::setFanoutLimit(Port *port, + const MinMax *min_max, + float fanout) +{ + MinMaxFloatValues &values = port_fanout_limit_map_[port]; + values.setValue(min_max, fanout); +} + +void +Sdc::setMaxArea(float area) +{ + max_area_ = area; +} + +float +Sdc::maxArea() const +{ + return max_area_; +} + +//////////////////////////////////////////////////////////////// + +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); + if (!add_to_pins) + deletePinClocks(clk, pins); + if (clk) + // Named clock redefinition. + deleteClkPinMappings(clk); + else { + // Fresh clock definition. + clk = new Clock(name, clk_index_++); + clk->setIsPropagated(propagate_all_clks_); + clocks_.push_back(clk); + // Use the copied name in the map. + clock_name_map_[clk->name()] = clk; + } + clk->initClk(pins, add_to_pins, period, waveform, comment, network_); + makeClkPinMappings(clk); + clearCycleAcctings(); + invalidateGeneratedClks(); + clkHpinDisablesInvalid(); + return clk; +} + +Clock * +Sdc::makeGeneratedClock(const char *name, + PinSet *pins, + bool add_to_pins, + Pin *src_pin, + Clock *master_clk, + Pin *pll_out, + Pin *pll_fdbk, + 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); + if (!add_to_pins) + deletePinClocks(clk, pins); + if (clk) + deleteClkPinMappings(clk); + else { + clk = new Clock(name, clk_index_++); + clocks_.push_back(clk); + clock_name_map_[clk->name()] = clk; + } + clk->initGeneratedClk(pins, add_to_pins, src_pin, master_clk, + pll_out, pll_fdbk, + divide_by, multiply_by, duty_cycle, + invert, combinational, + edges, edge_shifts, propagate_all_clks_, + comment, network_); + makeClkPinMappings(clk); + clearCycleAcctings(); + invalidateGeneratedClks(); + clkHpinDisablesInvalid(); + return clk; +} + +void +Sdc::invalidateGeneratedClks() const +{ + for (auto clk : clocks_) { + if (clk->isGenerated()) + clk->waveformInvalid(); + } +} + +// If the clock is not defined with the -add option, any pins that already +// have a clock attached to them are removed from the pin. If the clock +// is not the clock being defined and has no pins it is removed. +void +Sdc::deletePinClocks(Clock *defining_clk, + PinSet *pins) +{ + // Find all the clocks defined on pins to avoid finding the clock's + // vertex pins multiple times. + ClockSet clks; + PinSet::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + ClockSet *pin_clks = clock_pin_map_.findKey(pin); + if (pin_clks) { + ClockSet::Iterator clk_iter(pin_clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + clks.insert(clk); + } + } + } + ClockSet::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + deleteClkPinMappings(clk); + PinSet::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + clk->deletePin(pin); + } + if (clk != defining_clk) { + if (clk->pins()->empty()) + removeClock(clk); + else { + clk->makeVertexPins(network_); + // One of the remaining clock pins may use a vertex pin that + // was deleted above. + makeClkPinMappings(clk); + } + } + } +} + +void +Sdc::deleteClkPinMappings(Clock *clk) +{ + ClockPinIterator pin_iter1(clk); + while (pin_iter1.hasNext()) { + Pin *pin = pin_iter1.next(); + ClockSet *pin_clks = clock_pin_map_.findKey(pin); + if (pin_clks) { + pin_clks->erase(clk); + if (pin_clks->empty()) { + clock_pin_map_.erase(pin); + delete pin_clks; + } + } + } + + ClockVertexPinIterator pin_iter2(clk); + while (pin_iter2.hasNext()) { + Pin *pin = pin_iter2.next(); + ClockSet *pin_clks = clock_vertex_pin_map_.findKey(pin); + if (pin_clks) { + pin_clks->erase(clk); + if (pin_clks->empty()) { + clock_vertex_pin_map_.erase(pin); + delete pin_clks; + } + } + } +} + +void +Sdc::makeClkPinMappings(Clock *clk) +{ + ClockPinIterator pin_iter1(clk); + while (pin_iter1.hasNext()) { + Pin *pin = pin_iter1.next(); + ClockSet *pin_clks = clock_pin_map_.findKey(pin); + if (pin_clks == nullptr) { + pin_clks = new ClockSet; + clock_pin_map_.insert(pin, pin_clks); + } + pin_clks->insert(clk); + } + + ClockVertexPinIterator pin_iter2(clk); + while (pin_iter2.hasNext()) { + Pin *pin = pin_iter2.next(); + ClockSet *pin_clks = clock_vertex_pin_map_.findKey(pin); + if (pin_clks == nullptr) { + pin_clks = new ClockSet; + clock_vertex_pin_map_.insert(pin, pin_clks); + } + pin_clks->insert(clk); + } +} + +void +Sdc::removeClock(Clock *clk) +{ + deleteExceptionsReferencing(clk); + deleteInputDelaysReferencing(clk); + deleteOutputDelaysReferencing(clk); + deleteClockLatenciesReferencing(clk); + deleteClockInsertionsReferencing(clk); + deleteInterClockUncertaintiesReferencing(clk); + deleteLatchBorrowLimitsReferencing(clk); + deleteMinPulseWidthReferencing(clk); + deleteMasterClkRefs(clk); + clockGroupsDeleteClkRefs(clk); + clearCycleAcctings(); + + deleteClkPinMappings(clk); + clocks_.eraseObject(clk); + clock_name_map_.erase(clk->name()); + delete clk; +} + +// Delete references to clk as a master clock. +void +Sdc::deleteMasterClkRefs(Clock *clk) +{ + for (auto gclk : clocks_) { + if (gclk->isGenerated() + && gclk->masterClk() == clk) { + gclk->setMasterClk(nullptr); + } + } +} + +void +Sdc::clockDeletePin(Clock *clk, + Pin *pin) +{ + ClockSet *pin_clks = clock_pin_map_.findKey(pin); + pin_clks->erase(clk); + if (pin_clks->empty()) + clock_pin_map_.erase(pin); + clk->deletePin(pin); + clk->makeVertexPins(network_); + makeClkPinMappings(clk); +} + +Clock * +Sdc::findClock(const char *name) const +{ + return clock_name_map_.findKey(name); +} + +bool +Sdc::isClock(const Pin *pin) const +{ + ClockSet *clks = findClocks(pin); + return clks && !clks->empty(); +} + +bool +Sdc::isVertexPinClock(const Pin *pin) const +{ + ClockSet *clks = findVertexPinClocks(pin); + return clks && !clks->empty(); +} + +bool +Sdc::isVertexPinNonGeneratedClock(const Pin *pin) const +{ + ClockSet *clks = findVertexPinClocks(pin); + if (clks) { + ClockSet::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + if (!clk->isGenerated()) + return true; + } + return false; + } + else + return false; +} + +ClockSet * +Sdc::findVertexPinClocks(const Pin *pin) const +{ + return clock_vertex_pin_map_.findKey(pin); +} + +ClockSet * +Sdc::findClocks(const Pin *pin) const +{ + return clock_pin_map_.findKey(pin); +} + +void +Sdc::findClocksMatching(PatternMatch *pattern, + ClockSeq *clks) const +{ + if (!pattern->hasWildcards()) { + Clock *clk = findClock(pattern->pattern()); + if (clk) + clks->push_back(clk); + } + else { + for (auto clk : clocks_) { + if (pattern->match(clk->name())) + clks->push_back(clk); + } + } +} + +ClockIterator * +Sdc::clockIterator() +{ + return new ClockIterator(clocks_); +} + +void +Sdc::sortedClocks(ClockSeq &clks) +{ + for (auto clk : clocks_) + clks.push_back(clk); + sort(clks, ClkNameLess()); +} + +//////////////////////////////////////////////////////////////// + +class ClkHpinDisable +{ +public: + ClkHpinDisable(const Clock *clk, + 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_; } + +private: + const Clock *clk_; + const Pin *from_pin_; + const Pin *to_pin_; +}; + +ClkHpinDisable::ClkHpinDisable(const Clock *clk, + const Pin *from_pin, + const Pin *to_pin) : + clk_(clk), + from_pin_(from_pin), + to_pin_(to_pin) +{ +} + +bool +ClkHpinDisableLess::operator()(const ClkHpinDisable *disable1, + const ClkHpinDisable *disable2) const +{ + int clk_index1 = disable1->clk()->index(); + int clk_index2 = disable2->clk()->index(); + if (clk_index1 == clk_index2) { + const Pin *from_pin1 = disable1->fromPin(); + const Pin *from_pin2 = disable2->fromPin(); + if (from_pin1 == from_pin2) { + const Pin *to_pin1 = disable1->toPin(); + const Pin *to_pin2 = disable2->toPin(); + return to_pin1 < to_pin2; + } + else + return from_pin1 < from_pin2; + } + else + return clk_index1 < clk_index2; +} + +class FindClkHpinDisables : public HpinDrvrLoadVisitor +{ +public: + FindClkHpinDisables(Clock *clk, + const Network *network, + Sdc *sdc); + ~FindClkHpinDisables(); + bool drvrLoadExists(Pin *drvr, + Pin *load); + +protected: + virtual void visit(HpinDrvrLoad *drvr_load); + void makeClkHpinDisables(Pin *clk_src, + Pin *drvr, + Pin *load); + + Clock *clk_; + PinPairSet drvr_loads_; + const Network *network_; + Sdc *sdc_; + +private: + DISALLOW_COPY_AND_ASSIGN(FindClkHpinDisables); +}; + +FindClkHpinDisables::FindClkHpinDisables(Clock *clk, + const Network *network, + Sdc *sdc) : + HpinDrvrLoadVisitor(), + clk_(clk), + network_(network), + sdc_(sdc) +{ +} + +FindClkHpinDisables::~FindClkHpinDisables() +{ + drvr_loads_.deleteContents(); +} + +void +FindClkHpinDisables::visit(HpinDrvrLoad *drvr_load) +{ + Pin *drvr = drvr_load->drvr(); + Pin *load = drvr_load->load(); + + makeClkHpinDisables(drvr, drvr, load); + + PinSet *hpins_from_drvr = drvr_load->hpinsFromDrvr(); + PinSet::Iterator hpin_iter(hpins_from_drvr); + while (hpin_iter.hasNext()) { + Pin *hpin = hpin_iter.next(); + makeClkHpinDisables(hpin, drvr, load); + } + drvr_loads_.insert(new PinPair(drvr, load)); +} + +void +FindClkHpinDisables::makeClkHpinDisables(Pin *clk_src, + Pin *drvr, + Pin *load) +{ + ClockSet *clks = sdc_->findClocks(clk_src); + ClockSet::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + if (clk != clk_) + // Do not propagate clock from source pin if another + // clock is defined on a hierarchical pin between the + // driver and load. + sdc_->makeClkHpinDisable(clk, drvr, load); + } +} + +bool +FindClkHpinDisables::drvrLoadExists(Pin *drvr, + Pin *load) +{ + PinPair probe(drvr, load); + return drvr_loads_.hasKey(&probe); +} + +void +Sdc::makeClkHpinDisable(Clock *clk, + Pin *drvr, + Pin *load) +{ + ClkHpinDisable probe(clk, drvr, load); + if (!clk_hpin_disables_.hasKey(&probe)) { + ClkHpinDisable *disable = new ClkHpinDisable(clk, drvr, load); + clk_hpin_disables_.insert(disable); + } +} + +void +Sdc::ensureClkHpinDisables() +{ + if (!clk_hpin_disables_valid_) { + clk_hpin_disables_.deleteContentsClear(); + for (auto clk : clocks_) { + PinSet *srcs = clk->pins(); + PinSet::Iterator src_iter(srcs); + while (src_iter.hasNext()) { + Pin *src = src_iter.next(); + if (network_->isHierarchical(src)) { + FindClkHpinDisables visitor(clk, network_, this); + visitHpinDrvrLoads(src, network_, &visitor); + // Disable fanouts from the src driver pins that do + // not go thru the hierarchical src pin. + PinSet *vpins = clk->vertexPins(); + PinSet::Iterator vpin_iter(vpins); + while (vpin_iter.hasNext()) { + Pin *vpin = vpin_iter.next(); + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(vpin, vertex, bidirect_drvr_vertex); + makeVertexClkHpinDisables(clk, vertex, visitor); + if (bidirect_drvr_vertex) + makeVertexClkHpinDisables(clk, bidirect_drvr_vertex, visitor); + } + } + } + } + clk_hpin_disables_valid_ = true; + } +} + +void +Sdc::makeVertexClkHpinDisables(Clock *clk, + Vertex *vertex, + FindClkHpinDisables &visitor) +{ + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->isWire()) { + Pin *drvr = edge->from(graph_)->pin(); + Pin *load = edge->to(graph_)->pin(); + if (!visitor.drvrLoadExists(drvr, load)) + makeClkHpinDisable(clk, drvr, load); + } + } +} + +void +Sdc::clkHpinDisablesInvalid() +{ + clk_hpin_disables_valid_ = false; + for (auto clk : clocks_) + clk->makeVertexPins(network_); +} + +// Check that driver/load edge goes thru clock hpin. +// 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) +{ + if (clk->vertexPins() + && clk->vertexPins()->hasKey(const_cast(from_pin))) { + ClkHpinDisable probe(clk, from_pin, to_pin); + return clk_hpin_disables_.hasKey(&probe); + } + else + return false; +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setPropagatedClock(Clock *clk) +{ + clk->setIsPropagated(true); + removeClockLatency(clk, nullptr); +} + +void +Sdc::removePropagatedClock(Clock *clk) +{ + clk->setIsPropagated(false); +} + +void +Sdc::setPropagatedClock(Pin *pin) +{ + propagated_clk_pins_.insert(pin); + removeClockLatency(nullptr, pin); +} + +void +Sdc::removePropagatedClock(Pin *pin) +{ + propagated_clk_pins_.erase(pin); +} + +bool +Sdc::isPropagatedClock(const Pin *pin) +{ + return propagated_clk_pins_.hasKey(const_cast(pin)); +} + +void +Sdc::setClockSlew(Clock *clk, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float slew) +{ + clk->setSlew(tr, min_max, slew); +} + +void +Sdc::removeClockSlew(Clock *clk) +{ + clk->removeSlew(); +} + +void +Sdc::setClockLatency(Clock *clk, + Pin *pin, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float delay) +{ + ClockLatency probe(clk, pin); + ClockLatency *latency = clk_latencies_.findKey(&probe); + if (latency == nullptr) { + latency = new ClockLatency(clk, pin); + clk_latencies_.insert(latency); + } + latency->setDelay(tr, min_max, delay); + if (pin && graph_ && network_->isHierarchical(pin)) + annotateHierClkLatency(pin, latency); + + // set_clock_latency removes set_propagated_clock on the same object. + if (clk && pin == nullptr) + removePropagatedClock(clk); + if (pin) + removePropagatedClock(pin); +} + +void +Sdc::removeClockLatency(const Clock *clk, + const Pin *pin) +{ + ClockLatency probe(clk, pin); + ClockLatency *latency = clk_latencies_.findKey(&probe); + if (latency) + deleteClockLatency(latency); +} + +void +Sdc::deleteClockLatency(ClockLatency *latency) +{ + const Pin *pin = latency->pin(); + if (pin && graph_ && network_->isHierarchical(pin)) + deannotateHierClkLatency(pin); + clk_latencies_.erase(latency); + delete latency; +} + +void +Sdc::deleteClockLatenciesReferencing(Clock *clk) +{ + ClockLatencies::Iterator latency_iter(clk_latencies_); + while (latency_iter.hasNext()) { + ClockLatency *latency = latency_iter.next(); + if (latency->clock() == clk) + deleteClockLatency(latency); + } +} + +bool +Sdc::hasClockLatency(const Pin *pin) const +{ + ClockLatency probe(nullptr, pin); + return clk_latencies_.hasKey(&probe); +} + +void +Sdc::clockLatency(const Clock *clk, + const Pin *pin, + const TransRiseFall *tr, + 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); + if (latencies) + latencies->delay(tr, min_max, latency, exists); + } + if (!exists) { + ClockLatency probe(nullptr, pin); + ClockLatency *latencies = clk_latencies_.findKey(&probe); + if (latencies) + latencies->delay(tr, min_max, latency, exists); + } +} + +void +Sdc::clockLatency(const Clock *clk, + const TransRiseFall *tr, + 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); + if (latencies) + latencies->delay(tr, min_max, latency, exists); +} + +float +Sdc::clockLatency(const Clock *clk, + const TransRiseFall *tr, + const MinMax *min_max) const +{ + float latency; + bool exists; + clockLatency(clk, tr, min_max, + latency, exists); + return latency; +} + +void +Sdc::setClockUncertainty(Pin *pin, + const SetupHoldAll *setup_hold, + float uncertainty) +{ + ClockUncertainties *uncertainties = pin_clk_uncertainty_map_.findKey(pin); + if (uncertainties == nullptr) { + uncertainties = new ClockUncertainties; + pin_clk_uncertainty_map_[pin] = uncertainties; + } + uncertainties->setValue(setup_hold, uncertainty); +} + +void +Sdc::removeClockUncertainty(Pin *pin, + const SetupHoldAll *setup_hold) +{ + ClockUncertainties *uncertainties = pin_clk_uncertainty_map_.findKey(pin); + if (uncertainties) { + uncertainties->removeValue(setup_hold); + if (uncertainties->empty()) { + delete uncertainties; + pin_clk_uncertainty_map_.erase(pin); + } + } +} + +ClockUncertainties * +Sdc::clockUncertainties(const Pin *pin) +{ + return pin_clk_uncertainty_map_.findKey(pin); +} + +void +Sdc::clockUncertainty(const Pin *pin, + const SetupHold *setup_hold, + float &uncertainty, + bool &exists) +{ + ClockUncertainties *uncertainties = clockUncertainties(pin); + if (uncertainties) + uncertainties->value(setup_hold, uncertainty, exists); + else { + uncertainty = 0.0; + exists = false; + } +} + +void +Sdc::clockUncertainty(const Clock *src_clk, + const TransRiseFall *src_tr, + const Clock *tgt_clk, + const TransRiseFall *tgt_tr, + const SetupHold *setup_hold, + float &uncertainty, + bool &exists) +{ + InterClockUncertainty probe(src_clk, tgt_clk); + InterClockUncertainty *uncertainties = + inter_clk_uncertainties_.findKey(&probe); + if (uncertainties) + uncertainties->uncertainty(src_tr, tgt_tr, setup_hold, + uncertainty, exists); + else { + uncertainty = 0.0; + exists = false; + } +} + +void +Sdc::setClockUncertainty(Clock *from_clk, + const TransRiseFallBoth *from_tr, + Clock *to_clk, + const TransRiseFallBoth *to_tr, + const SetupHoldAll *setup_hold, + float uncertainty) +{ + InterClockUncertainty probe(from_clk, to_clk); + InterClockUncertainty *uncertainties = + inter_clk_uncertainties_.findKey(&probe); + if (uncertainties == nullptr) { + uncertainties = new InterClockUncertainty(from_clk, to_clk); + inter_clk_uncertainties_.insert(uncertainties); + } + uncertainties->setUncertainty(from_tr, to_tr, setup_hold, uncertainty); +} + +void +Sdc::removeClockUncertainty(Clock *from_clk, + const TransRiseFallBoth *from_tr, + Clock *to_clk, + const TransRiseFallBoth *to_tr, + const SetupHoldAll *setup_hold) +{ + InterClockUncertainty probe(from_clk, to_clk); + InterClockUncertainty *uncertainties = + inter_clk_uncertainties_.findKey(&probe); + if (uncertainties) { + uncertainties->removeUncertainty(from_tr, to_tr, setup_hold); + if (uncertainties->empty()) { + inter_clk_uncertainties_.erase(uncertainties); + delete uncertainties; + } + } +} + +void +Sdc::deleteInterClockUncertainty(InterClockUncertainty *uncertainties) +{ + inter_clk_uncertainties_.erase(uncertainties); + delete uncertainties; +} + +void +Sdc::deleteInterClockUncertaintiesReferencing(Clock *clk) +{ + InterClockUncertaintySet::Iterator inter_iter(inter_clk_uncertainties_); + while (inter_iter.hasNext()) { + InterClockUncertainty *uncertainties = inter_iter.next(); + if (uncertainties->src() == clk + || uncertainties->target() == clk) + deleteInterClockUncertainty(uncertainties); + } +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setClockInsertion(const Clock *clk, + const Pin *pin, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay) +{ + if (clk_insertions_ == nullptr) + clk_insertions_ = new ClockInsertions; + ClockInsertion probe(clk, pin); + ClockInsertion *insertion = clk_insertions_->findKey(&probe); + if (insertion == nullptr) { + insertion = new ClockInsertion(clk, pin); + clk_insertions_->insert(insertion); + } + insertion->setDelay(tr, min_max, early_late, delay); +} + +void +Sdc::setClockInsertion(const Clock *clk, + const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max, + const EarlyLate *early_late, + float delay) +{ + if (clk_insertions_ == nullptr) + clk_insertions_ = new ClockInsertions; + ClockInsertion probe(clk, pin); + ClockInsertion *insertion = clk_insertions_->findKey(&probe); + if (insertion == nullptr) { + insertion = new ClockInsertion(clk, pin); + clk_insertions_->insert(insertion); + } + insertion->setDelay(tr, min_max, early_late, delay); +} + +void +Sdc::removeClockInsertion(const Clock *clk, + const Pin *pin) +{ + if (clk_insertions_) { + ClockInsertion probe(clk, pin); + ClockInsertion *insertion = clk_insertions_->findKey(&probe); + if (insertion != nullptr) + deleteClockInsertion(insertion); + } +} + +void +Sdc::deleteClockInsertion(ClockInsertion *insertion) +{ + clk_insertions_->erase(insertion); + delete insertion; +} + +void +Sdc::deleteClockInsertionsReferencing(Clock *clk) +{ + ClockInsertions::Iterator insertion_iter(clk_insertions_); + while (insertion_iter.hasNext()) { + ClockInsertion *insertion = insertion_iter.next(); + if (insertion->clock() == clk) + deleteClockInsertion(insertion); + } +} + +float +Sdc::clockInsertion(const Clock *clk, + const TransRiseFall *tr, + const MinMax *min_max, + const EarlyLate *early_late) const +{ + float insertion; + bool exists; + clockInsertion(clk, nullptr, tr, min_max, early_late, insertion, exists); + return insertion; +} + +bool +Sdc::hasClockInsertion(const Pin *pin) const +{ + if (clk_insertions_) { + ClockInsertion probe(nullptr, pin); + return clk_insertions_->hasKey(&probe); + } + else + return false; +} + +void +Sdc::clockInsertion(const Clock *clk, + const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max, + const EarlyLate *early_late, + // Return values. + float &insertion, + bool &exists) const +{ + ClockInsertion *insert = nullptr; + if (clk_insertions_) { + if (clk && pin) { + ClockInsertion probe(clk, pin); + insert = clk_insertions_->findKey(&probe); + } + if (insert == nullptr && pin) { + ClockInsertion probe(nullptr, pin); + insert = clk_insertions_->findKey(&probe); + } + if (insert == nullptr && clk) { + ClockInsertion probe(clk, nullptr); + insert = clk_insertions_->findKey(&probe); + } + } + if (insert) + insert->delay(tr, min_max, early_late, insertion, exists); + else { + insertion = 0.0; + exists = false; + } +} + +//////////////////////////////////////////////////////////////// + +bool +ClockLatencyPinClkLess::operator()(const ClockLatency *latency1, + const ClockLatency *latency2) const +{ + const Clock *clk1 = latency1->clock(); + const Clock *clk2 = latency2->clock(); + const Pin *pin1 = latency1->pin(); + const Pin *pin2 = latency2->pin(); + return clk1 < clk2 + || (clk1 == clk2 + && pin1 < pin2); +} + +//////////////////////////////////////////////////////////////// + +bool +ClockInsertionPinClkLess::operator()(const ClockInsertion *insert1, + const ClockInsertion *insert2)const +{ + const Clock *clk1 = insert1->clock(); + const Clock *clk2 = insert2->clock(); + const Pin *pin1 = insert1->pin(); + const Pin *pin2 = insert2->pin(); + return (clk1 && clk2 + && (clk1 < clk2 + || (clk1 == clk2 + && pin1 && pin2 + && pin1 < pin2))) + || (clk1 == nullptr && clk2) + || (clk1 == nullptr && clk2 == nullptr + && ((pin1 && pin2 + && pin1 < pin2) + || (pin1 == nullptr && pin2))); +} + +//////////////////////////////////////////////////////////////// + +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(); + else { + ClockGroups *groups = clk_groups_name_map_.findKey(name); + if (groups) + removeClockGroups(groups); + } + ClockGroups *groups = new ClockGroups(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 * +Sdc::makeClockGroupsName() +{ + char *name = nullptr; + int i = 0; + do { + i++; + stringDelete(name); + name = stringPrint("group%d", i); + } while (clk_groups_name_map_.hasKey(name)); + return name; +} + +void +Sdc::makeClockGroup(ClockGroups *clk_groups, + ClockSet *clks) +{ + clk_groups->makeClockGroup(clks); +} + +ClockGroupIterator * +Sdc::clockGroupIterator() +{ + return new ClockGroupIterator(clk_groups_name_map_); +} + +void +Sdc::ensureClkGroupExclusions() +{ + if (clk_group_exclusions_ == nullptr) { + clk_group_exclusions_ = new ClockPairSet; + clk_group_same_ = new ClockPairSet; + for (auto name_clk_groups : clk_groups_name_map_) + makeClkGroupExclusions(name_clk_groups.second); + } +} + +void +Sdc::makeClkGroupExclusions(ClockGroups *clk_groups) +{ + if (!(clk_groups->asynchronous() + && clk_groups->allowPaths())) { + ClockGroupSet *groups = clk_groups->groups(); + if (groups->size() == 1) + makeClkGroupExclusions1(groups); + else + makeClkGroupExclusions(groups); + } +} + +// If there is only one group all clocks not in the group +// are excluded. +void +Sdc::makeClkGroupExclusions1(ClockGroupSet *groups) +{ + ClockGroupSet::Iterator group_iter1(groups); + ClockGroup *group1 = group_iter1.next(); + ClockSet *clks1 = group1->clks(); + for (auto clk1 : *clks1) { + for (Clock *clk2 : clocks_) { + if (clk2 != clk1 + && !group1->isMember(clk2)) + clk_group_exclusions_->insert(ClockPair(clk1, clk2)); + } + } + makeClkGroupSame(group1); +} + +void +Sdc::makeClkGroupExclusions(ClockGroupSet *groups) +{ + for (auto group1 : *groups) { + ClockSet *clks1 = group1->clks(); + for (auto group2 : *groups) { + if (group1 != group2) { + ClockSet *clks2 = group2->clks(); + for (auto clk1 : *clks1) { + for (auto clk2 : *clks2) { + // ClockPair is symmetric so only add one clk1/clk2 pair. + if (clk1->index() < clk2->index()) { + clk_group_exclusions_->insert(ClockPair(clk1, clk2)); + } + } + } + } + } + makeClkGroupSame(group1); + } +} + +void +Sdc::makeClkGroupSame(ClockGroup *group) +{ + ClockSet *clks = group->clks(); + for (auto clk1 : *clks) { + for (auto clk2 : *clks) { + if (clk1->index() <= clk2->index()) { + ClockPair clk_pair(clk1, clk2); + if (!clk_group_same_->hasKey(clk_pair)) + clk_group_same_->insert(clk_pair); + } + } + } +} + +void +Sdc::clearClkGroupExclusions() +{ + if (clk_group_exclusions_) { + delete clk_group_exclusions_; + delete clk_group_same_; + clk_group_exclusions_ = nullptr; + clk_group_same_ = nullptr; + } +} + +bool +Sdc::sameClockGroup(const Clock *clk1, + const Clock *clk2) +{ + if (clk1 && clk2) { + ClockPair clk_pair(clk1, clk2); + bool excluded = clk_group_exclusions_->hasKey(clk_pair); + return !excluded; + } + else + return true; +} + +bool +Sdc::sameClockGroupExplicit(const Clock *clk1, + const Clock *clk2) +{ + ClockPair clk_pair(clk1, clk2); + return clk_group_same_->hasKey(clk_pair); +} + +void +Sdc::removeClockGroups(const char *name) +{ + ClockGroups *clk_groups = clk_groups_name_map_.findKey(name); + if (clk_groups) + removeClockGroups(clk_groups); +} + +void +Sdc::removeClockGroupsLogicallyExclusive(const char *name) +{ + if (name) { + ClockGroups *groups = clk_groups_name_map_.findKey(name); + if (groups->logicallyExclusive()) + removeClockGroups(groups); + } + else { + ClockGroupsNameMap::Iterator groups_iter(clk_groups_name_map_); + while (groups_iter.hasNext()) { + ClockGroups *groups = groups_iter.next(); + if (groups->logicallyExclusive()) + removeClockGroups(groups); + } + } +} + +void +Sdc::removeClockGroupsPhysicallyExclusive(const char *name) +{ + if (name) { + ClockGroups *groups = clk_groups_name_map_.findKey(name); + if (groups->physicallyExclusive()) + removeClockGroups(groups); + } + else { + ClockGroupsNameMap::Iterator groups_iter(clk_groups_name_map_); + while (groups_iter.hasNext()) { + ClockGroups *groups = groups_iter.next(); + if (groups->physicallyExclusive()) + removeClockGroups(groups); + } + } +} + +void +Sdc::removeClockGroupsAsynchronous(const char *name) +{ + if (name) { + ClockGroups *groups = clk_groups_name_map_.findKey(name); + if (groups->asynchronous()) + removeClockGroups(groups); + } + else { + ClockGroupsNameMap::Iterator groups_iter(clk_groups_name_map_); + while (groups_iter.hasNext()) { + ClockGroups *groups = groups_iter.next(); + if (groups->asynchronous()) + removeClockGroups(groups); + } + } +} + +void +Sdc::removeClockGroups(ClockGroups *groups) +{ + clk_groups_name_map_.erase(groups->name()); + delete groups; + // Can't delete excluded clock pairs for deleted clock groups because + // some other clock groups may exclude the same clock pair. + clearClkGroupExclusions(); +} + +void +Sdc::clockGroupsDeleteClkRefs(Clock *clk) +{ + ClockGroupsNameMap::Iterator groups_iter(clk_groups_name_map_); + while (groups_iter.hasNext()) { + ClockGroups *groups = groups_iter.next(); + groups->removeClock(clk); + } + clearClkGroupExclusions(); +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setClockSense(PinSet *pins, + ClockSet *clks, + ClockSense sense) +{ + if (clks && clks->empty()) { + delete clks; + clks = nullptr; + } + PinSet::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + if (clks) { + ClockSet::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + setClockSense(pin, clk, sense); + } + } + else + setClockSense(pin, nullptr, sense); + } + delete pins; + delete clks; +} + +void +Sdc::setClockSense(const Pin *pin, + const Clock *clk, + ClockSense sense) +{ + PinClockPair probe(pin, clk); + if (clk_sense_map_.hasKey(probe)) + clk_sense_map_[probe] = sense; + else { + PinClockPair pin_clk(pin, clk); + clk_sense_map_[pin_clk] = sense; + } +} + +bool +Sdc::clkStopPropagation(const Pin *pin, + const Clock *clk) const +{ + PinClockPair pin_clk(pin, clk); + ClockSense sense; + bool exists; + clk_sense_map_.findKey(pin_clk, sense, exists); + if (!exists) { + PinClockPair pin_clk1(pin, nullptr); + clk_sense_map_.findKey(pin_clk1, sense, exists); + } + return exists + && sense == ClockSense::stop; +} + +bool +Sdc::clkStopSense(const Pin *to_pin, + const Clock *clk, + const TransRiseFall *from_tr, + const TransRiseFall *to_tr) const +{ + PinClockPair pin_clk(to_pin, clk); + ClockSense sense; + bool exists; + clk_sense_map_.findKey(pin_clk, sense, exists); + if (!exists) { + PinClockPair pin(to_pin, nullptr); + clk_sense_map_.findKey(pin, sense, exists); + } + return exists + && (sense == ClockSense::stop + || (sense == ClockSense::positive + && from_tr != to_tr) + || (sense == ClockSense::negative + && from_tr == to_tr)); +} + +bool +Sdc::clkStopPropagation(const Clock *clk, + const Pin *from_pin, + const TransRiseFall *from_tr, + const Pin *to_pin, + const TransRiseFall *to_tr) const +{ + return clkStopPropagation(from_pin, clk) + || clkStopSense(to_pin, clk, from_tr, to_tr); +} + +PinClockPairLess::PinClockPairLess(const Network *network) : + network_(network) +{ +} + +bool +PinClockPairLess::operator()(const PinClockPair &pin_clk1, + 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; + return pin1 < pin2 + || (pin1 == pin2 + && ((clk1 == nullptr && clk2) + || (clk1 && clk2 + && clk1->index() < clk2->index()))); +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setClockGatingCheck(const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin) +{ + if (clk_gating_check_ == nullptr) + clk_gating_check_ = new ClockGatingCheck; + clk_gating_check_->margins()->setValue(tr, setup_hold, margin); +} + +void +Sdc::setClockGatingCheck(Clock *clk, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin) +{ + ClockGatingCheck *check = clk_gating_check_map_.findKey(clk); + if (check == nullptr) { + check = new ClockGatingCheck(); + clk_gating_check_map_[clk] = check; + } + check->margins()->setValue(tr, setup_hold, margin); +} + +void +Sdc::setClockGatingCheck(Instance *inst, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) +{ + ClockGatingCheck *check = inst_clk_gating_check_map_.findKey(inst); + if (check == nullptr) { + check = new ClockGatingCheck(); + inst_clk_gating_check_map_[inst] = check; + } + check->margins()->setValue(tr, setup_hold, margin); + check->setActiveValue(active_value); +} + +void +Sdc::setClockGatingCheck(const Pin *pin, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) +{ + ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(pin); + if (check == nullptr) { + check = new ClockGatingCheck(); + pin_clk_gating_check_map_[pin] = check; + } + check->margins()->setValue(tr, setup_hold, margin); + check->setActiveValue(active_value); +} + +void +Sdc::clockGatingMarginEnablePin(const Pin *enable_pin, + const TransRiseFall *enable_tr, + const SetupHold *setup_hold, + bool &exists, float &margin) +{ + ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(enable_pin); + if (check) + check->margins()->value(enable_tr, setup_hold, margin, exists); + else + exists = false; +} + +void +Sdc::clockGatingMarginInstance(Instance *inst, + const TransRiseFall *enable_tr, + const SetupHold *setup_hold, + bool &exists, + float &margin) +{ + ClockGatingCheck *check = inst_clk_gating_check_map_.findKey(inst); + if (check) + check->margins()->value(enable_tr, setup_hold, margin, exists); + else + exists = false; +} + +void +Sdc::clockGatingMarginClkPin(const Pin *clk_pin, + const TransRiseFall *enable_tr, + const SetupHold *setup_hold, + bool &exists, + float &margin) +{ + ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(clk_pin); + if (check) + check->margins()->value(enable_tr, setup_hold, margin, exists); + else + exists = false; +} + +void +Sdc::clockGatingMarginClk(const Clock *clk, + const TransRiseFall *enable_tr, + const SetupHold *setup_hold, + bool &exists, + float &margin) +{ + ClockGatingCheck *check = clk_gating_check_map_.findKey(clk); + if (check) + check->margins()->value(enable_tr, setup_hold, margin, exists); + else + exists = false; +} + +void +Sdc::clockGatingMargin(const TransRiseFall *enable_tr, + const SetupHold *setup_hold, + bool &exists, + float &margin) +{ + if (clk_gating_check_) + clk_gating_check_->margins()->value(enable_tr, setup_hold, margin, exists); + else + exists = false; +} + +LogicValue +Sdc::clockGatingActiveValue(const Pin *clk_pin, + const Pin *enable_pin) +{ + ClockGatingCheck *check; + check = pin_clk_gating_check_map_.findKey(enable_pin); + if (check) + return check->activeValue(); + Instance *inst = network_->instance(enable_pin); + check = inst_clk_gating_check_map_.findKey(inst); + if (check) + return check->activeValue(); + check = pin_clk_gating_check_map_.findKey(clk_pin); + if (check) + return check->activeValue(); + return LogicValue::unknown; +} + +//////////////////////////////////////////////////////////////// + +// Determine cycle accounting "on demand". +CycleAccting * +Sdc::cycleAccting(const ClockEdge *src, + const ClockEdge *tgt) +{ + if (src == nullptr) + src = tgt; + CycleAccting probe(src, tgt); + CycleAccting *acct = cycle_acctings_.findKey(&probe); + if (acct == nullptr) { + UniqueLock lock(cycle_acctings_lock_); + // Recheck with lock. + acct = cycle_acctings_.findKey(&probe); + if (acct == nullptr) { + acct = new CycleAccting(src, tgt); + if (src == defaultArrivalClockEdge()) + acct->findDefaultArrivalSrcDelays(); + else + acct->findDelays(this); + cycle_acctings_.insert(acct); + } + } + return acct; +} + +void +Sdc::reportClkToClkMaxCycleWarnings() +{ + // Find cycle acctings that exceed max cycle count. Eliminate + // duplicate warnings between different src/tgt clk edges. + ClockPairSet clk_warnings; + ClockPairSeq clk_warnings2; + CycleAcctingSet::Iterator acct_iter(cycle_acctings_); + while (acct_iter.hasNext()) { + CycleAccting *acct = acct_iter.next(); + if (acct->maxCyclesExceeded()) { + Clock *src = acct->src()->clock(); + Clock *tgt = acct->target()->clock(); + // Canonicalize the warning wrt src/tgt. + if (src->index() > tgt->index()) + std::swap(src, tgt); + ClockPair clk_pair(src, tgt); + if (!clk_warnings.hasKey(clk_pair)) { + clk_warnings.insert(clk_pair); + clk_warnings2.push_back(clk_pair); + } + } + } + + // Sort clk pairs so that results are stable. + sort(clk_warnings2, ClockPairLess()); + + for (auto pair : clk_warnings2) { + report_->warn("No common period was found between clocks %s and %s.\n", + pair.first->name(), + pair.second->name()); + } +} + +void +Sdc::clearCycleAcctings() +{ + cycle_acctings_.deleteContentsClear(); +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setDataCheck(Pin *from, + const TransRiseFallBoth *from_tr, + Pin *to, + const TransRiseFallBoth *to_tr, + Clock *clk, + const SetupHoldAll *setup_hold, + float margin) +{ + DataCheck *check = nullptr; + DataCheckSet *checks = data_checks_from_map_.findKey(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); + } + if (check == nullptr) + check = new DataCheck(from, to, clk); + check->setMargin(from_tr, to_tr, setup_hold, margin); + checks->insert(check); + + checks = data_checks_to_map_.findKey(to); + if (checks == nullptr) { + checks = new DataCheckSet(DataCheckLess(network_)); + data_checks_to_map_[to] = checks; + } + checks->insert(check); + + if (graph_) + annotateGraphConstrained(to, true); +} + +void +Sdc::removeDataCheck(Pin *from, + const TransRiseFallBoth *from_tr, + Pin *to, + const TransRiseFallBoth *to_tr, + Clock *clk, + const SetupHoldAll *setup_hold) +{ + DataCheck probe(from, to, clk); + DataCheckSet *checks = data_checks_from_map_.findKey(from); + if (checks) { + DataCheck *check = checks->findKey(&probe); + if (check) { + check->removeMargin(from_tr, to_tr, setup_hold); + if (check->empty()) { + checks->erase(check); + checks = data_checks_to_map_.findKey(to); + if (checks) + checks->erase(check); + delete check; + } + } + } +} + +DataCheckSet * +Sdc::dataChecksFrom(const Pin *from) const +{ + return data_checks_from_map_.findKey(from); +} + +DataCheckSet * +Sdc::dataChecksTo(const Pin *to) const +{ + return data_checks_to_map_.findKey(to); +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setLatchBorrowLimit(Pin *pin, + float limit) +{ + pin_latch_borrow_limit_map_[pin] = limit; +} + +void +Sdc::setLatchBorrowLimit(Instance *inst, + float limit) +{ + inst_latch_borrow_limit_map_[inst] = limit; +} + +void +Sdc::setLatchBorrowLimit(Clock *clk, + float limit) +{ + clk_latch_borrow_limit_map_[clk] = limit; +} + +void +Sdc::deleteLatchBorrowLimitsReferencing(Clock *clk) +{ + clk_latch_borrow_limit_map_.erase(clk); +} + +void +Sdc::latchBorrowLimit(Pin *data_pin, + Pin *enable_pin, + Clock *clk, + // Return values. + float &limit, + bool &exists) +{ + pin_latch_borrow_limit_map_.findKey(data_pin, limit, exists); + if (!exists) { + pin_latch_borrow_limit_map_.findKey(enable_pin, limit, exists); + if (!exists) { + Instance *inst = network_->instance(data_pin); + inst_latch_borrow_limit_map_.findKey(inst, limit, exists); + if (!exists) + clk_latch_borrow_limit_map_.findKey(clk, limit, exists); + } + } +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setMinPulseWidth(const TransRiseFallBoth *tr, + float min_width) +{ + for (auto tr1 : tr->range()) + min_pulse_width_.setValue(tr1, min_width); +} + +void +Sdc::setMinPulseWidth(const Pin *pin, + const TransRiseFallBoth *tr, + float min_width) +{ + RiseFallValues *widths = pin_min_pulse_width_map_.findKey(pin); + if (widths == nullptr) { + widths = new RiseFallValues; + pin_min_pulse_width_map_[pin] = widths; + } + for (auto tr1 : tr->range()) + widths->setValue(tr1, min_width); +} + +void +Sdc::setMinPulseWidth(const Instance *inst, + const TransRiseFallBoth *tr, + float min_width) +{ + RiseFallValues *widths = inst_min_pulse_width_map_.findKey(inst); + if (widths == nullptr) { + widths = new RiseFallValues; + inst_min_pulse_width_map_[inst] = widths; + } + for (auto tr1 : tr->range()) + widths->setValue(tr1, min_width); +} + +void +Sdc::setMinPulseWidth(const Clock *clk, + const TransRiseFallBoth *tr, + float min_width) +{ + RiseFallValues *widths = clk_min_pulse_width_map_.findKey(clk); + if (widths == nullptr) { + widths = new RiseFallValues; + clk_min_pulse_width_map_[clk] = widths; + } + for (auto tr1 : tr->range()) + widths->setValue(tr1, min_width); +} + +void +Sdc::minPulseWidth(const Pin *pin, + const Clock *clk, + const TransRiseFall *hi_low, + float &min_width, + bool &exists) const +{ + RiseFallValues *widths = pin_min_pulse_width_map_.findKey(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); + } + if (widths == nullptr) + widths = clk_min_pulse_width_map_.findKey(clk); + if (widths) + widths->value(hi_low, min_width, exists); + else + min_pulse_width_.value(hi_low, min_width, exists); + } +} + +void +Sdc::deleteMinPulseWidthReferencing(Clock *clk) +{ + RiseFallValues *widths = clk_min_pulse_width_map_.findKey(clk); + if (widths) { + delete widths; + clk_min_pulse_width_map_.erase(clk); + } +} + +//////////////////////////////////////////////////////////////// + +InputDrive * +Sdc::findInputDrive(Port *port) +{ + return input_drive_map_.findKey(port); +} + +void +Sdc::setInputDelay(Pin *pin, + const TransRiseFallBoth *tr, + Clock *clk, + const TransRiseFall *clk_tr, + 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_tr) : nullptr; + InputDelay *input_delay = ensureInputDelay(pin, clk_edge, ref_pin); + RiseFallMinMax *delays = input_delay->delays(); + if (add) + delays->mergeValue(tr, min_max, delay); + else { + deleteInputDelays(pin, input_delay); + delays->setValue(tr, min_max, delay); + } + input_delay->setSourceLatencyIncluded(source_latency_included); + input_delay->setNetworkLatencyIncluded(network_latency_included); +} + +InputDelay * +Sdc::ensureInputDelay(Pin *pin, + ClockEdge *clk_edge, + Pin *ref_pin) +{ + InputDelay *input_delay = findInputDelay(pin, clk_edge, ref_pin); + if (input_delay == nullptr) { + input_delay = new InputDelay(pin, clk_edge, ref_pin, input_delay_index_++, + network_); + input_delay->setNext(input_delay_map_[pin]); + input_delay_map_[pin] = input_delay; + if (ref_pin) { + InputDelaySet *ref_inputs = input_delay_ref_pin_map_.findKey(ref_pin); + if (ref_inputs == nullptr) { + ref_inputs = new InputDelaySet; + input_delay_ref_pin_map_[ref_pin] = ref_inputs; + } + ref_inputs->insert(input_delay); + } + InputDelayVertexPinIterator vpin_iter(input_delay); + while (vpin_iter.hasNext()) { + Pin *vpin = vpin_iter.next(); + input_delay_vertex_map_[vpin] = input_delay; + if (!network_->isTopLevelPort(vpin)) { + InputDelaySet *input_set = input_delay_internal_pin_map_[vpin]; + if (input_set == nullptr) { + input_set = new InputDelaySet; + input_delay_internal_pin_map_[vpin] = input_set; + } + input_set->insert(input_delay); + } + } + } + return input_delay; +} + +InputDelay * +Sdc::findInputDelay(const Pin *pin, + ClockEdge *clk_edge, + Pin *ref_pin) +{ + for (InputDelay *input_delay = input_delay_map_.findKey(pin); + input_delay; + input_delay = input_delay->next()) { + if (input_delay->clkEdge() == clk_edge + && input_delay->refPin() == ref_pin) + return input_delay; + } + return nullptr; +} + +void +Sdc::removeInputDelay(Pin *pin, + TransRiseFallBoth *tr, + Clock *clk, + TransRiseFall *clk_tr, + MinMaxAll *min_max) +{ + ClockEdge *clk_edge = clk ? clk->edge(clk_tr) : nullptr; + InputDelay *input_delay = findInputDelay(pin, clk_edge, nullptr); + if (input_delay) { + RiseFallMinMax *delays = input_delay->delays(); + delays->removeValue(tr, min_max); + if (delays->empty()) + deleteInputDelay(input_delay); + } +} + +void +Sdc::deleteInputDelays(Pin *pin, + InputDelay *except) +{ + InputDelay *input_delay = input_delay_map_.findKey(pin); + while (input_delay) { + InputDelay *next = input_delay->next(); + if (input_delay == except) + input_delay->setNext(nullptr); + else + delete input_delay; + input_delay = next; + } + input_delay_map_[pin] = except; + InputDelayVertexPinIterator vpin_iter(except); + while (vpin_iter.hasNext()) { + Pin *vpin = vpin_iter.next(); + input_delay_vertex_map_[vpin] = except; + InputDelaySet *input_delays = + input_delay_internal_pin_map_.findKey(vpin); + if (input_delays) { + input_delays->clear(); + input_delays->insert(except); + } + } +} + +InputDelaySet * +Sdc::refPinInputDelays(const Pin *ref_pin) const +{ + return input_delay_ref_pin_map_.findKey(ref_pin); +} + +InputDelayIterator * +Sdc::inputDelayIterator() +{ + return new InputDelayIterator(input_delay_map_); +} + +InputDelayVertexPinsIterator * +Sdc::inputDelayVertexPinsIterator() +{ + return new InputDelayVertexPinsIterator(input_delay_vertex_map_); +} + +PinInputDelayIterator * +Sdc::inputDelayIterator(const Pin *pin) const +{ + + return new PinInputDelayIterator(pin, this); +} + +PinInputDelayIterator * +Sdc::inputDelayVertexIterator(const Pin *vertex_pin) const +{ + return new VertexPinInputDelayIterator(vertex_pin, this); +} + +bool +Sdc::hasInputDelay(const Pin *vertex_pin) const +{ + return input_delay_vertex_map_.findKey(vertex_pin) != nullptr; +} + +bool +Sdc::isInputDelayInternal(const Pin *pin) const +{ + InputDelaySet *input_delays = + input_delay_internal_pin_map_.findKey(pin); + return input_delays && !input_delays->empty(); +} + + + +void +Sdc::deleteInputDelaysReferencing(Clock *clk) +{ + InputDelayIterator input_iter(input_delay_map_); + while (input_iter.hasNext()) { + InputDelay *input_delay = input_iter.next(); + if (input_delay->clock() == clk) + deleteInputDelay(input_delay); + } +} + +void +Sdc::deleteInputDelay(InputDelay *input_delay) +{ + Pin *pin = input_delay->pin(); + InputDelay *head = input_delay_map_.findKey(pin); + if (head == input_delay) { + InputDelay *next = input_delay->next(); + if (next) + input_delay_map_[pin] = next; + else + input_delay_map_.erase(pin); + head = next; + } + else { + for (InputDelay *delay = head; delay; ) { + InputDelay *next = delay->next(); + if (next == input_delay) { + delay->setNext(input_delay->next()); + break; + } + delay = next; + } + } + + InputDelayVertexPinIterator vpin_iter(input_delay); + while (vpin_iter.hasNext()) { + Pin *vpin = vpin_iter.next(); + if (head) + input_delay_vertex_map_[vpin] = head; + else + input_delay_vertex_map_.erase(vpin); + } + + delete input_delay; +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setOutputDelay(Pin *pin, + const TransRiseFallBoth *tr, + Clock *clk, + const TransRiseFall *clk_tr, + 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_tr) : nullptr; + OutputDelay *output_delay = ensureOutputDelay(pin, clk_edge, ref_pin); + RiseFallMinMax *delays = output_delay->delays(); + if (add) + delays->mergeValue(tr, min_max, delay); + else { + deleteOutputDelays(pin, output_delay); + delays->setValue(tr, min_max, delay); + } + output_delay->setSourceLatencyIncluded(source_latency_included); + output_delay->setNetworkLatencyIncluded(network_latency_included); +} + +OutputDelay * +Sdc::ensureOutputDelay(Pin *pin, + ClockEdge *clk_edge, + Pin *ref_pin) +{ + OutputDelay *output_delay = findOutputDelay(pin, clk_edge, ref_pin); + if (output_delay == nullptr) { + output_delay = new OutputDelay(pin, clk_edge, ref_pin, network_); + output_delay->setNext(output_delay_map_[pin]); + output_delay_map_[pin] = output_delay; + OutputDelayVertexPinIterator vpin_iter(output_delay); + while (vpin_iter.hasNext()) { + Pin *vpin = vpin_iter.next(); + output_delay_vertex_map_[vpin] = output_delay; + if (graph_) + annotateGraphConstrained(vpin, true); + } + } + return output_delay; +} + +OutputDelay * +Sdc::findOutputDelay(const Pin *pin, + ClockEdge *clk_edge, + Pin *ref_pin) +{ + for (OutputDelay *output_delay = output_delay_map_.findKey(pin); + output_delay; + output_delay = output_delay->next()) { + if (output_delay->clkEdge() == clk_edge + && output_delay->refPin() == ref_pin) + return output_delay; + } + return nullptr; +} + +void +Sdc::removeOutputDelay(Pin *pin, + TransRiseFallBoth *tr, + Clock *clk, + TransRiseFall *clk_tr, + MinMaxAll *min_max) +{ + ClockEdge *clk_edge = clk ? clk->edge(clk_tr) : nullptr; + InputDelay *input_delay = findInputDelay(pin, clk_edge, nullptr); + if (input_delay) { + RiseFallMinMax *delays = input_delay->delays(); + delays->removeValue(tr, min_max); + } +} + +void +Sdc::deleteOutputDelays(Pin *pin, + OutputDelay *except) +{ + OutputDelay *output_delay = output_delay_map_.findKey(pin); + while (output_delay) { + OutputDelay *next = output_delay->next(); + if (output_delay == except) + output_delay->setNext(nullptr); + else + delete output_delay; + output_delay = next; + } + output_delay_map_[pin] = except; + OutputDelayVertexPinIterator vpin_iter(except); + while (vpin_iter. hasNext()) { + Pin *vpin = vpin_iter.next(); + output_delay_vertex_map_[vpin] = except; + } +} + +OutputDelayIterator * +Sdc::outputDelayIterator() +{ + return new OutputDelayIterator(output_delay_map_); +} + +PinOutputDelayIterator * +Sdc::outputDelayIterator(const Pin *pin) const +{ + return new PinOutputDelayIterator(pin, this); +} + +PinOutputDelayIterator * +Sdc::outputDelayVertexIterator(const Pin *vertex_pin) const +{ + return new VertexPinOutputDelayIterator(vertex_pin, this); +} + +bool +Sdc::hasOutputDelay(const Pin *vertex_pin) const +{ + return output_delay_vertex_map_.hasKey(vertex_pin); +} + +void +Sdc::deleteOutputDelaysReferencing(Clock *clk) +{ + OutputDelayIterator output_iter(output_delay_map_); + while (output_iter.hasNext()) { + OutputDelay *output_delay = output_iter.next(); + if (output_delay->clock() == clk) + deleteOutputDelay(output_delay); + } +} + +void +Sdc::deleteOutputDelay(OutputDelay *output_delay) +{ + Pin *pin = output_delay->pin(); + OutputDelay *head = output_delay_map_.findKey(pin); + if (head == output_delay) { + OutputDelay *next = output_delay->next(); + if (next) + output_delay_map_[pin] = next; + else + output_delay_map_.erase(pin); + head = next; + } + else { + for (OutputDelay *delay = head; delay; ) { + OutputDelay *next = delay->next(); + if (next == output_delay) { + delay->setNext(output_delay->next()); + break; + } + delay = next; + } + } + + OutputDelayVertexPinIterator vpin_iter(output_delay); + while (vpin_iter.hasNext()) { + Pin *vpin = vpin_iter.next(); + if (head) + output_delay_vertex_map_[vpin] = head; + else + output_delay_vertex_map_.erase(vpin); + } + + delete output_delay; +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setPortExtPinCap(Port *port, + const TransRiseFall *tr, + const MinMax *min_max, + float cap) +{ + PortExtCap *port_cap = ensurePortExtPinCap(port); + port_cap->setPinCap(cap, tr, min_max); +} + +void +Sdc::setPortExtWireCap(Port *port, + bool subtract_pin_cap, + const TransRiseFall *tr, + const Corner *corner, + const MinMax *min_max, + float cap) +{ + PortExtCap *port_cap = ensurePortExtPinCap(port); + if (subtract_pin_cap) { + Pin *pin = network_->findPin(network_->name(port)); + const OperatingConditions *op_cond = operatingConditions(min_max); + cap -= connectedPinCap(pin, tr, op_cond, corner, min_max); + if (cap < 0.0) + cap = 0.0; + } + port_cap->setWireCap(cap, tr, min_max); +} + +PortExtCap * +Sdc::portExtCap(Port *port) const +{ + if (port_cap_map_) + return port_cap_map_->findKey(port); + else + return nullptr; +} + +bool +Sdc::hasPortExtCap(Port *port) const +{ + if (port_cap_map_) + return port_cap_map_->hasKey(port); + else + return false; +} + +void +Sdc::portExtCap(Port *port, + const TransRiseFall *tr, + 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 +{ + if (port_cap_map_) { + PortExtCap *port_cap = port_cap_map_->findKey(port); + if (port_cap) { + port_cap->pinCap(tr, min_max, pin_cap, has_pin_cap); + port_cap->wireCap(tr, min_max, wire_cap, has_wire_cap); + port_cap->fanout(min_max, fanout, has_fanout); + return; + } + } + pin_cap = 0.0F; + has_pin_cap = false; + wire_cap = 0.0F; + has_wire_cap = false; + fanout = 0.0F; + has_fanout = false; +} + +float +Sdc::portExtCap(Port *port, + const TransRiseFall *tr, + const MinMax *min_max) const +{ + float pin_cap, wire_cap; + int fanout; + bool has_pin_cap, has_wire_cap, has_fanout; + portExtCap(port, tr, 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; + if (has_wire_cap) + cap += wire_cap; + return cap; +} + +bool +Sdc::drvrPinHasWireCap(const Pin *pin) +{ + return drvr_pin_wire_cap_map_ + && drvr_pin_wire_cap_map_->hasKey(const_cast(pin)); +} + +void +Sdc::drvrPinWireCap(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + // Return values. + float &cap, + bool &exists) const +{ + if (drvr_pin_wire_cap_map_) { + MinMaxFloatValues *values = + drvr_pin_wire_cap_map_[corner->index()].findKey(const_cast(pin)); + if (values) + return values->value(min_max, cap, exists); + } + cap = 0.0; + exists = false; +} + +void +Sdc::setNetWireCap(Net *net, + bool subtract_pin_cap, + const Corner *corner, + const MinMax *min_max, + float cap) +{ + float wire_cap = cap; + if (subtract_pin_cap) { + OperatingConditions *op_cond = operatingConditions(min_max); + NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); + if (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + float pin_cap_rise = connectedPinCap(pin, TransRiseFall::rise(), + op_cond, corner, min_max); + float pin_cap_fall = connectedPinCap(pin, TransRiseFall::fall(), + op_cond, corner, min_max); + float pin_cap = (pin_cap_rise + pin_cap_fall) / 2.0F; + wire_cap -= pin_cap; + if ((wire_cap + pin_cap) < 0.0) + wire_cap = -pin_cap; + delete pin_iter; + } + } + if (net_wire_cap_map_ == nullptr) + net_wire_cap_map_ = new NetWireCapMap[corners_->count()]; + bool make_drvr_entry = !net_wire_cap_map_[corner->index()].hasKey(net); + MinMaxFloatValues &values = net_wire_cap_map_[corner->index()][net]; + values.setValue(min_max, wire_cap); + + // Only need to do this when there is new net_wire_cap_map_ entry. + if (make_drvr_entry) { + for (Pin *pin : *network_->drivers(net)) { + if (drvr_pin_wire_cap_map_ == nullptr) + drvr_pin_wire_cap_map_ = new PinWireCapMap[corners_->count()]; + drvr_pin_wire_cap_map_[corner->index()][pin] = &values; + } + } +} + +bool +Sdc::hasNetWireCap(Net *net) const +{ + if (net_wire_cap_map_) { + for (int i = 0; i < corners_->count(); i++) { + if (net_wire_cap_map_[i].hasKey(net)) + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::connectedCap(const Pin *pin, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_set_load) const +{ + netCaps(pin, tr, op_cond, corner, min_max, + pin_cap, wire_cap, fanout, has_set_load); + float net_wire_cap; + bool has_net_wire_cap; + drvrPinWireCap(pin, corner, min_max, net_wire_cap, has_net_wire_cap); + if (has_net_wire_cap) { + wire_cap += net_wire_cap; + has_set_load = true; + } +} + +float +Sdc::connectedPinCap(const Pin *pin, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max) +{ + float pin_cap, wire_cap, fanout; + bool has_set_load; + connectedCap(pin, tr, op_cond, corner, min_max, + pin_cap, wire_cap, fanout, has_set_load); + return pin_cap; +} + +class FindNetCaps : public PinVisitor +{ +public: + FindNetCaps(const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_set_load, + const Sdc *sdc); + virtual void operator()(Pin *pin); + +protected: + const TransRiseFall *tr_; + const OperatingConditions *op_cond_; + const Corner *corner_; + const MinMax *min_max_; + float &pin_cap_; + float &wire_cap_; + float &fanout_; + bool &has_set_load_; + const Sdc *sdc_; + +private: + DISALLOW_COPY_AND_ASSIGN(FindNetCaps); +}; + +FindNetCaps::FindNetCaps(const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_set_load, + const Sdc *sdc) : + PinVisitor(), + tr_(tr), + op_cond_(op_cond), + corner_(corner), + min_max_(min_max), + pin_cap_(pin_cap), + wire_cap_(wire_cap), + fanout_(fanout), + has_set_load_(has_set_load), + sdc_(sdc) +{ +} + +void +FindNetCaps::operator()(Pin *pin) +{ + sdc_->pinCaps(pin, tr_, op_cond_, corner_, min_max_, + pin_cap_, wire_cap_, fanout_, has_set_load_); +} + +// Capacitances for all pins connected to drvr_pin's net. +void +Sdc::netCaps(const Pin *drvr_pin, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_set_load) const +{ + pin_cap = 0.0; + wire_cap = 0.0; + fanout = 0.0; + has_set_load = false; + FindNetCaps visitor(tr, op_cond, corner, min_max, pin_cap, + wire_cap, fanout, has_set_load, this); + network_->visitConnectedPins(const_cast(drvr_pin), visitor); +} + +void +Sdc::pinCaps(const Pin *pin, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_set_load) const +{ + if (network_->isTopLevelPort(pin)) { + Port *port = network_->port(pin); + bool is_output = network_->direction(port)->isAnyOutput(); + float port_pin_cap, port_wire_cap; + int port_fanout; + bool has_pin_cap, has_wire_cap, has_fanout; + portExtCap(port, tr, 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; + // Output port counts as a fanout. + fanout++; + } + has_set_load |= has_pin_cap || has_wire_cap; + } + else { + LibertyPort *port = network_->libertyPort(pin); + if (port) { + Instance *inst = network_->instance(pin); + pin_cap += portCapacitance(inst, port, tr, op_cond, corner, min_max); + if (port->direction()->isAnyInput()) + fanout++; + } + } +} + +float +Sdc::portCapacitance(Instance *inst, + LibertyPort *port, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max) const +{ + Pvt *inst_pvt = nullptr; + if (inst) + inst_pvt = pvt(inst, min_max); + LibertyPort *corner_port = port->cornerPort(corner->libertyIndex(min_max)); + return corner_port->capacitance(tr, min_max, op_cond, inst_pvt); +} + +float +Sdc::pinCapacitance(const Pin *pin, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max) +{ + LibertyPort *port = network_->libertyPort(pin); + if (port) { + Instance *inst = network_->instance(pin); + return portCapacitance(inst, port, tr, op_cond, corner, min_max); + } + else + return 0.0; +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setResistance(Net *net, + const MinMaxAll *min_max, + float res) +{ + MinMaxFloatValues &values = net_res_map_[net]; + values.setValue(min_max, res); +} + +void +Sdc::resistance(Net *net, + const MinMax *min_max, + float &res, + bool &exists) +{ + res = 0.0; + MinMaxFloatValues values; + net_res_map_.findKey(net, values, exists); + if (exists) + values.value(min_max, res, exists); +} + +void +Sdc::setPortExtFanout(Port *port, + const MinMax *min_max, + int fanout) +{ + PortExtCap *port_cap = ensurePortExtPinCap(port); + port_cap->setFanout(fanout, min_max); +} + +void +Sdc::portExtFanout(Port *port, + const MinMax *min_max, + // Return values. + int &fanout, + bool &exists) +{ + PortExtCap *port_cap = portExtCap(port); + if (port_cap) + port_cap->fanout(min_max, fanout, exists); + else { + fanout = 0.0; + exists = false; + } +} + +int +Sdc::portExtFanout(Port *port, + const MinMax *min_max) +{ + int fanout; + bool exists; + portExtFanout(port, min_max, fanout, exists); + if (exists) + return fanout; + else + return 0.0; +} + +PortExtCap * +Sdc::ensurePortExtPinCap(Port *port) +{ + if (port_cap_map_ == nullptr) + port_cap_map_ = new PortExtCapMap; + PortExtCap *port_cap = port_cap_map_->findKey(port); + if (port_cap == nullptr) { + port_cap = new PortExtCap(port); + (*port_cap_map_)[port] = port_cap; + } + return port_cap; +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::disable(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to) +{ + DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); + if (disabled_cell == nullptr) { + disabled_cell = new DisabledCellPorts(cell); + disabled_cell_ports_[cell] = disabled_cell; + } + if (from && to) { + disabled_cell->setDisabledFromTo(from, to); + LibertyCellTimingArcSetIterator arc_iter(cell, from, to); + while (arc_iter.hasNext()) { + TimingArcSet *arc_set = arc_iter.next(); + arc_set->setIsDisabledConstraint(true); + } + } + else if (from) { + disabled_cell->setDisabledFrom(from); + from->setIsDisabledConstraint(true); + } + else if (to) { + disabled_cell->setDisabledTo(to); + to->setIsDisabledConstraint(true); + } + else { + disabled_cell->setDisabledAll(); + cell->setIsDisabledConstraint(true); + } +} + +void +Sdc::removeDisable(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to) +{ + DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); + if (disabled_cell) { + if (from && to) { + disabled_cell->removeDisabledFromTo(from, to); + LibertyCellTimingArcSetIterator arc_iter(cell, from, to); + while (arc_iter.hasNext()) { + TimingArcSet *arc_set = arc_iter.next(); + arc_set->setIsDisabledConstraint(false); + } + } + else if (from) { + disabled_cell->removeDisabledFrom(from); + from->setIsDisabledConstraint(false); + } + else if (to) { + disabled_cell->removeDisabledTo(to); + to->setIsDisabledConstraint(false); + } + else { + disabled_cell->removeDisabledAll(); + cell->setIsDisabledConstraint(false); + } + } +} + +void +Sdc::disable(TimingArcSet *arc_set) +{ + LibertyCell *cell = arc_set->libertyCell(); + DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(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) { + 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 +Sdc::disable(Port *port) +{ + disabled_ports_.insert(port); + if (graph_) { + Pin *pin = network_->findPin(network_->topInstance(), port); + annotateGraphDisabled(pin, true); + } +} + +void +Sdc::removeDisable(Port *port) +{ + if (graph_) { + Pin *pin = network_->findPin(network_->topInstance(), port); + annotateGraphDisabled(pin, false); + } + disabled_ports_.erase(port); +} + +void +Sdc::disable(Instance *inst, + LibertyPort *from, + LibertyPort *to) +{ + DisabledInstancePorts *disabled_inst = disabled_inst_ports_.findKey(inst); + if (disabled_inst == nullptr) { + disabled_inst = new DisabledInstancePorts(inst); + disabled_inst_ports_[inst] = disabled_inst; + } + if (from && to) + disabled_inst->setDisabledFromTo(from, to); + else if (from) + disabled_inst->setDisabledFrom(from); + else if (to) + disabled_inst->setDisabledTo(to); + else + disabled_inst->setDisabledAll(); + + if (graph_) + setEdgeDisabledInstPorts(disabled_inst, true); +} + +void +Sdc::removeDisable(Instance *inst, + LibertyPort *from, + LibertyPort *to) +{ + DisabledInstancePorts *disabled_inst = disabled_inst_ports_.findKey(inst); + if (disabled_inst) { + if (graph_) + setEdgeDisabledInstPorts(disabled_inst, false); + if (from && to) + disabled_inst->removeDisabledFromTo(from, to); + else if (from) + disabled_inst->removeDisabledFrom(from); + else if (to) + disabled_inst->removeDisabledTo(to); + else + disabled_inst->removeDisabledAll(); + } +} + +void +Sdc::disable(Pin *from, + Pin *to) +{ + PinPair probe(from, to); + if (!disabled_wire_edges_.hasKey(&probe)) { + PinPair *pair = new PinPair(from, to); + disabled_wire_edges_.insert(pair); + if (graph_) + annotateGraphDisabledWireEdge(from, to, true, graph_); + } +} + +void +Sdc::removeDisable(Pin *from, + Pin *to) +{ + annotateGraphDisabledWireEdge(from, to, false, graph_); + PinPair probe(from, to); + disabled_wire_edges_.erase(&probe); +} + +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) +{ + return disabled_edges_.hasKey(edge); +} + +class DisableEdgesThruHierPin : public HierPinThruVisitor +{ +public: + DisableEdgesThruHierPin(PinPairSet *pairs, + Graph *graph); + +protected: + virtual void visit(Pin *drvr, + Pin *load); + + PinPairSet *pairs_; + Graph *graph_; + +private: + DISALLOW_COPY_AND_ASSIGN(DisableEdgesThruHierPin); +}; + +DisableEdgesThruHierPin::DisableEdgesThruHierPin(PinPairSet *pairs, + Graph *graph) : + HierPinThruVisitor(), + pairs_(pairs), + graph_(graph) +{ +} + +void +DisableEdgesThruHierPin::visit(Pin *drvr, + Pin *load) +{ + PinPair probe(drvr, load); + if (!pairs_->hasKey(&probe)) { + PinPair *pair = new PinPair(drvr, load); + pairs_->insert(pair); + if (graph_) + annotateGraphDisabledWireEdge(drvr, load, true, graph_); + } +} + +void +Sdc::disable(Pin *pin) +{ + if (network_->isHierarchical(pin)) { + // Add leaf pins thru hierarchical pin to disabled_edges_. + DisableEdgesThruHierPin visitor(&disabled_wire_edges_, graph_); + visitDrvrLoadsThruHierPin(pin, network_, &visitor); + } + else { + disabled_pins_.insert(pin); + if (graph_) + annotateGraphDisabled(pin, true); + } +} + +class RemoveDisableEdgesThruHierPin : public HierPinThruVisitor +{ +public: + RemoveDisableEdgesThruHierPin(PinPairSet *pairs, Graph *graph); + +protected: + virtual void visit(Pin *drvr, Pin *load); + + PinPairSet *pairs_; + Graph *graph_; + +private: + DISALLOW_COPY_AND_ASSIGN(RemoveDisableEdgesThruHierPin); +}; + +RemoveDisableEdgesThruHierPin::RemoveDisableEdgesThruHierPin(PinPairSet *pairs, + Graph *graph) : + HierPinThruVisitor(), + pairs_(pairs), + graph_(graph) +{ +} + +void +RemoveDisableEdgesThruHierPin::visit(Pin *drvr, + Pin *load) +{ + if (graph_) + annotateGraphDisabledWireEdge(drvr, load, false, graph_); + PinPair probe(drvr, load); + PinPair *pair = pairs_->findKey(&probe); + if (pair) { + pairs_->erase(pair); + delete pair; + } +} + +void +Sdc::removeDisable(Pin *pin) +{ + if (network_->isHierarchical(pin)) { + // Remove leaf pins thru hierarchical pin from disabled_edges_. + RemoveDisableEdgesThruHierPin visitor(&disabled_wire_edges_, graph_); + visitDrvrLoadsThruHierPin(pin, network_, &visitor); + } + else { + if (graph_) + annotateGraphDisabled(pin, false); + disabled_pins_.erase(pin); + } +} + +bool +Sdc::isDisabled(const Pin *pin) const +{ + Port *port = network_->port(pin); + LibertyPort *lib_port = network_->libertyPort(pin); + return disabled_pins_.hasKey(const_cast(pin)) + || disabled_ports_.hasKey(port) + || disabled_lib_ports_.hasKey(lib_port); +} + +bool +Sdc::isDisabled(const Instance *inst, + const Pin *from_pin, + const Pin *to_pin, + const TimingRole *role) const +{ + if (role == TimingRole::wire()) { + // Hierarchical thru pin disables. + PinPair pair(const_cast(from_pin), const_cast(to_pin)); + return disabled_wire_edges_.hasKey(&pair); + } + 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(const_cast(inst)); + DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); + return (disabled_inst + && disabled_inst->isDisabled(from_port, to_port, role)) + || (disabled_cell + && disabled_cell->isDisabled(from_port, to_port, role)); + } +} + +bool +Sdc::isDisabled(TimingArcSet *arc_set) const +{ + LibertyCell *cell = arc_set->libertyCell(); + if (cell) { + DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); + return disabled_cell + && disabled_cell->isDisabled(arc_set); + } + else + return false; +} + +DisabledInstancePortsMap * +Sdc::disabledInstancePorts() +{ + return &disabled_inst_ports_; +} + +DisabledCellPortsMap * +Sdc::disabledCellPorts() +{ + return &disabled_cell_ports_; +} + +void +Sdc::disableClockGatingCheck(Instance *inst) +{ + disabled_clk_gating_checks_inst_.insert(inst); +} + +void +Sdc::disableClockGatingCheck(Pin *pin) +{ + disabled_clk_gating_checks_pin_.insert(pin); +} + +void +Sdc::removeDisableClockGatingCheck(Instance *inst) +{ + disabled_clk_gating_checks_inst_.erase(inst); +} + +void +Sdc::removeDisableClockGatingCheck(Pin *pin) +{ + disabled_clk_gating_checks_pin_.erase(pin); +} + +bool +Sdc::isDisableClockGatingCheck(const Instance *inst) +{ + return disabled_clk_gating_checks_inst_.hasKey(const_cast(inst)); +} + +bool +Sdc::isDisableClockGatingCheck(const Pin *pin) +{ + return disabled_clk_gating_checks_pin_.hasKey(const_cast(pin)); +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::setLogicValue(Pin *pin, + LogicValue value) +{ + logic_value_map_[pin] = value; +} + +void +Sdc::logicValue(const Pin *pin, + LogicValue &value, + bool &exists) +{ + logic_value_map_.findKey(pin, value, exists); +} + +void +Sdc::setCaseAnalysis(Pin *pin, + LogicValue value) +{ + case_value_map_[pin] = value; +} + +void +Sdc::removeCaseAnalysis(Pin *pin) +{ + case_value_map_.erase(pin); +} + +void +Sdc::caseLogicValue(const Pin *pin, + LogicValue &value, + bool &exists) +{ + case_value_map_.findKey(pin, value, exists); +} + +bool +Sdc::hasLogicValue(const Pin *pin) +{ + return case_value_map_.hasKey(pin) + || logic_value_map_.hasKey(pin); +} + +//////////////////////////////////////////////////////////////// + +ExceptionFrom * +Sdc::makeExceptionFrom(PinSet *from_pins, + ClockSet *from_clks, + InstanceSet *from_insts, + const TransRiseFallBoth *from_tr) +{ + if ((from_pins && !from_pins->empty()) + || (from_clks && !from_clks->empty()) + || (from_insts && !from_insts->empty())) + return new ExceptionFrom(from_pins, from_clks, from_insts, from_tr, true); + else + return nullptr; +} + +ExceptionThru * +Sdc::makeExceptionThru(PinSet *pins, + NetSet *nets, + InstanceSet *insts, + const TransRiseFallBoth *tr) +{ + if ((pins && !pins->empty()) + || (nets && !nets->empty()) + || (insts && !insts->empty())) + return new ExceptionThru(pins, nets, insts, tr, true, network_); + else + return nullptr; +} + +ExceptionTo * +Sdc::makeExceptionTo(PinSet *pins, + ClockSet *clks, + InstanceSet *insts, + const TransRiseFallBoth *tr, + const TransRiseFallBoth *end_tr) +{ + if ((pins && !pins->empty()) + || (clks && !clks->empty()) + || (insts && !insts->empty()) + || (tr != TransRiseFallBoth::riseFall()) + || (end_tr != TransRiseFallBoth::riseFall())) + return new ExceptionTo(pins, clks, insts, tr, end_tr, true); + else + return nullptr; +} + +// Valid endpoints include gated clock enables which are not +// known until clock arrivals are determined. +bool +Sdc::exceptionToInvalid(const Pin *pin) +{ + Net *net = network_->net(pin); + // Floating pins are invalid. + if ((net == nullptr + && !network_->isTopLevelPort(pin)) + || (net + // Pins connected to power/ground are invalid. + && (network_->isPower(net) + || network_->isGround(net))) + // Hierarchical pins are invalid. + || network_->isHierarchical(pin)) + return true; + // Register/latch Q pins are invalid. + LibertyPort *port = network_->libertyPort(pin); + if (port) { + LibertyCell *cell = port->libertyCell(); + LibertyCellTimingArcSetIterator set_iter(cell, nullptr, port); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); + TimingRole *role = set->role(); + if (role->genericRole() == TimingRole::regClkToQ()) + return true; + } + } + return false; +} + +void +Sdc::makeFalsePath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + const char *comment) +{ + checkFromThrusTo(from, thrus, to); + FalsePath *exception = new FalsePath(from, thrus, to, min_max, true, + 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) +{ + checkFromThrusTo(from, thrus, to); + MultiCyclePath *exception = new MultiCyclePath(from, thrus, to, + 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, + float delay, + const char *comment) +{ + checkFromThrusTo(from, thrus, to); + PathDelay *exception = new PathDelay(from, thrus, to, min_max, + ignore_clk_latency, delay, true, + comment); + addException(exception); +} + +void +Sdc::recordPathDelayInternalStartpoints(ExceptionPath *exception) +{ + ExceptionFrom *from = exception->from(); + if (from + && from->hasPins()) { + PinSet::Iterator pin_iter(from->pins()); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + if (!(network_->isRegClkPin(pin) + || network_->isTopLevelPort(pin))) { + if (path_delay_internal_startpoints_ == nullptr) + path_delay_internal_startpoints_ = new PinSet; + path_delay_internal_startpoints_->insert(pin); + } + } + } +} + +void +Sdc::unrecordPathDelayInternalStartpoints(ExceptionFrom *from) +{ + if (from + && from->hasPins() + && path_delay_internal_startpoints_) { + PinSet::Iterator pin_iter(from->pins()); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + if (!(network_->isRegClkPin(pin) + || network_->isTopLevelPort(pin)) + && !pathDelayFrom(pin)) + path_delay_internal_startpoints_->erase(pin); + } + } +} + +bool +Sdc::pathDelayFrom(const Pin *pin) +{ + if (first_from_pin_exceptions_) { + ExceptionPathSet *exceptions = first_from_pin_exceptions_->findKey(pin); + ExceptionPathSet::ConstIterator exception_iter(exceptions); + while (exception_iter.hasNext()) { + ExceptionPath *exception = exception_iter.next(); + if (exception->isPathDelay()) + return true; + } + } + return false; +} + +bool +Sdc::isPathDelayInternalStartpoint(const Pin *pin) const +{ + return path_delay_internal_startpoints_ + && path_delay_internal_startpoints_->hasKey(const_cast(pin)); +} + +PinSet * +Sdc::pathDelayInternalStartpoints() const +{ + return path_delay_internal_startpoints_; +} + +void +Sdc::recordPathDelayInternalEndpoints(ExceptionPath *exception) +{ + ExceptionTo *to = exception->to(); + if (to + && to->hasPins()) { + PinSet::Iterator pin_iter(to->pins()); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + if (!(hasLibertyChecks(pin) + || network_->isTopLevelPort(pin))) { + if (path_delay_internal_endpoints_ == nullptr) + path_delay_internal_endpoints_ = new PinSet; + path_delay_internal_endpoints_->insert(pin); + } + } + } +} + +void +Sdc::unrecordPathDelayInternalEndpoints(ExceptionPath *exception) +{ + ExceptionTo *to = exception->to(); + if (to + && to->hasPins() + && path_delay_internal_endpoints_) { + PinSet::Iterator pin_iter(to->pins()); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + if (!(hasLibertyChecks(pin) + || network_->isTopLevelPort(pin)) + && !pathDelayTo(pin)) + path_delay_internal_endpoints_->erase(pin); + } + } +} + +bool +Sdc::hasLibertyChecks(const Pin *pin) +{ + const Instance *inst = network_->instance(pin); + LibertyCell *cell = network_->libertyCell(inst); + if (cell) { + LibertyPort *port = network_->libertyPort(pin); + if (port) { + LibertyCellTimingArcSetIterator timing_iter(cell, nullptr, port); + while (timing_iter.hasNext()) { + TimingArcSet *arc_set = timing_iter.next(); + if (arc_set->role()->isTimingCheck()) + return true; + } + } + } + return false; +} + +bool +Sdc::pathDelayTo(const Pin *pin) +{ + if (first_to_pin_exceptions_) { + ExceptionPathSet *exceptions = first_to_pin_exceptions_->findKey(pin); + ExceptionPathSet::ConstIterator exception_iter(exceptions); + while (exception_iter.hasNext()) { + ExceptionPath *exception = exception_iter.next(); + if (exception->isPathDelay()) + return true; + } + } + return false; +} + +bool +Sdc::isPathDelayInternalEndpoint(const Pin *pin) const +{ + return path_delay_internal_endpoints_ + && path_delay_internal_endpoints_->hasKey(const_cast(pin)); +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::clearGroupPathMap() +{ + // GroupPath exceptions are deleted with other exceptions. + // Delete group_path name strings. + GroupPathIterator group_path_iter(group_path_map_); + while (group_path_iter.hasNext()) { + const char *name; + GroupPathSet *groups; + group_path_iter.next(name, groups); + stringDelete(name); + delete groups; + } + group_path_map_.clear(); +} + +void +Sdc::makeGroupPath(const char *name, + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const char *comment) +{ + checkFromThrusTo(from, thrus, to); + if (name && is_default) + internalError("group path name and is_default are mutually exclusive."); + else if (name) { + GroupPath *group_path = new GroupPath(name, is_default, from, thrus, to, + true, comment); + addException(group_path); + // A named group path can have multiple exceptions. + GroupPathSet *groups = group_path_map_.findKey(name); + if (groups == nullptr) { + groups = new GroupPathSet; + group_path_map_[stringCopy(name)] = groups; + } + groups->insert(group_path); + } + else { + // is_default + GroupPath *group_path = new GroupPath(name, is_default, from, thrus, to, + true, comment); + addException(group_path); + } +} + +bool +Sdc::isGroupPathName(const char *group_name) +{ + return group_path_map_.hasKey(group_name); +} + +GroupPathIterator * +Sdc::groupPathIterator() +{ + return new GroupPathIterator(group_path_map_); +} + +//////////////////////////////////////////////////////////////// + +FilterPath * +Sdc::makeFilterPath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to) +{ + checkFromThrusTo(from, thrus, to); + FilterPath *exception = new FilterPath(from, thrus, to, true); + addException(exception); + // This is the only type of exception that can be returned. + // There is only one of them, so it shouldn't merge. + return exception; +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::makeLoopExceptions() +{ + GraphLoopSeq::Iterator loop_iter(levelize_->loops()); + while (loop_iter.hasNext()) { + GraphLoop *loop = loop_iter.next(); + makeLoopExceptions(loop); + } +} + +// Make a -thru pin false path from every edge entering the loop +// around the loop and back. +void +Sdc::makeLoopExceptions(GraphLoop *loop) +{ + debugPrint0(debug_, "loop", 2, "Loop false path\n"); + EdgeSeq::Iterator loop_edge_iter(loop->edges()); + while (loop_edge_iter.hasNext()) { + Edge *edge = loop_edge_iter.next(); + Vertex *from_vertex = edge->from(graph_); + Vertex *to_vertex = edge->to(graph_); + Pin *from_pin = from_vertex->pin(); + Pin *to_pin = to_vertex->pin(); + // Find edges entering the loop. + VertexInEdgeIterator in_edge_iter(to_vertex, graph_); + 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); + } + } + } +} + +void +Sdc::makeLoopException(Pin *loop_input_pin, + Pin *loop_pin, + Pin *loop_prev_pin) +{ + ExceptionThruSeq *thrus = new ExceptionThruSeq; + makeLoopExceptionThru(loop_input_pin, thrus); + makeLoopExceptionThru(loop_pin, thrus); + makeLoopExceptionThru(loop_prev_pin, thrus); + makeLoopExceptionThru(loop_pin, thrus); + makeLoopPath(thrus); +} + +void +Sdc::makeLoopPath(ExceptionThruSeq *thrus) +{ + FalsePath *exception = new LoopPath(thrus, true); + addException(exception); +} + +void +Sdc::makeLoopExceptionThru(Pin *pin, + ExceptionThruSeq *thrus) +{ + debugPrint1(debug_, "levelize", 2, " %s\n", network_->pathName(pin)); + PinSet *pins = new PinSet; + pins->insert(pin); + ExceptionThru *thru = makeExceptionThru(pins, nullptr, nullptr, + TransRiseFallBoth::riseFall()); + thrus->push_back(thru); +} + +void +Sdc::deleteLoopExceptions() +{ + ExceptionPathSet::Iterator except_iter(exceptions_); + while (except_iter.hasNext()) { + ExceptionPath *except = except_iter.next(); + if (except->isLoop()) + deleteException(except); + } +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::addException(ExceptionPath *exception) +{ + debugPrint1(debug_, "exception_merge", 1, "add exception for %s\n", + exception->asString(network_)); + + if (exception->isPathDelay()) { + recordPathDelayInternalStartpoints(exception); + recordPathDelayInternalEndpoints(exception); + if (exception->to() == nullptr) + path_delays_without_to_ = true; + } + + // Check to see if the exception has from/to mixed object types. + // If so, the priority of the exception is mixed. + // Split it into separate exceptions that have consistent priority. + ExceptionFrom *from = exception->from(); + if (from + && (from->hasPins() || from->hasInstances()) + && from->hasClocks()) { + PinSet *pins1 = from->pins() ? new PinSet(*from->pins()) : nullptr; + InstanceSet *insts1 = + from->instances() ? new InstanceSet(*from->instances()) : nullptr; + ExceptionFrom *from1 = new ExceptionFrom(pins1, nullptr, insts1, + from->transition(), true); + ExceptionThruSeq *thrus1 = exceptionThrusClone(exception->thrus(), network_); + ExceptionTo *to = exception->to(); + ExceptionTo *to1 = to ? to->clone() : nullptr; + ExceptionPath *exception1 = exception->clone(from1, thrus1, to1, true); + debugPrint1(debug_, "exception_merge", 1, " split exception for %s\n", + exception1->asString(network_)); + addException1(exception1); + + ClockSet *clks2 = new ClockSet(*from->clks()); + ExceptionFrom *from2 = new ExceptionFrom(nullptr, clks2, nullptr, + from->transition(), true); + ExceptionThruSeq *thrus2 = exceptionThrusClone(exception->thrus(), network_); + ExceptionTo *to2 = to ? to->clone() : nullptr; + ExceptionPath *exception2 = exception->clone(from2, thrus2, to2, true); + debugPrint1(debug_, "exception_merge", 1, " split exception for %s\n", + exception2->asString(network_)); + addException1(exception2); + + delete exception; + } + else + addException1(exception); +} + +void +Sdc::addException1(ExceptionPath *exception) +{ + ExceptionTo *to = exception->to(); + if (to + && (to->hasPins() || to->hasInstances()) + && to->hasClocks()) { + ExceptionFrom *from1 = exception->from()->clone(); + ExceptionThruSeq *thrus1 = exceptionThrusClone(exception->thrus(), network_); + 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); + ExceptionPath *exception1 = exception->clone(from1, thrus1, to1, true); + debugPrint1(debug_, "exception_merge", 1, " split exception for %s\n", + exception1->asString(network_)); + addException2(exception1); + + ExceptionFrom *from2 = exception->from()->clone(); + ExceptionThruSeq *thrus2 = exceptionThrusClone(exception->thrus(), network_); + ClockSet *clks2 = new ClockSet(*to->clks()); + ExceptionTo *to2 = new ExceptionTo(nullptr, clks2, nullptr, to->transition(), + to->endTransition(), true); + ExceptionPath *exception2 = exception->clone(from2, thrus2, to2, true); + debugPrint1(debug_, "exception_merge", 1, " split exception for %s\n", + exception2->asString(network_)); + addException2(exception2); + + delete exception; + } + else + addException2(exception); +} + +void +Sdc::addException2(ExceptionPath *exception) +{ + if (exception->isMultiCycle() || exception->isPathDelay()) + deleteMatchingExceptions(exception); + recordException(exception); + mergeException(exception); +} + +// If a path delay/multicycle exception is redefined with a different +// delay/cycle count, the new exception overrides the existing +// exception. Multiple related exceptions are merged to reduce the +// number of tags. To support overrides, relevant merged exceptions must be +// expanded to find and delete or override the new exception. +// For example, the exception +// set_multi_cycle_path -from {A B} -to {C D} 2 +// is a merged representation of the following four exceptions: +// set_multi_cycle_path -from A -to C 2 +// set_multi_cycle_path -from A -to D 2 +// set_multi_cycle_path -from B -to C 2 +// set_multi_cycle_path -from B -to D 2 +// If the following exception is later defined, +// set_multi_cycle_path -from A -to C 3 +// The cycle count of one of the merged exceptions changes. +// This prevents the original four exceptions from merging into one +// exception. +// +// This situation is handled by breaking the original merged exception +// into multiple smaller exceptions that exclude the new subset +// exception. This is NOT done by expanding the merged exception, +// since the number of exception points can be huge leading to serious +// run time problems. +// +// For the example above, the merged exception is broken down into the +// following set of exceptions that exclude the new subset exception. +// +// set_multi_cycle_path -from {B} -to {C D} 2 +// set_multi_cycle_path -from {A} -to {D} 2 +// +// In general, the merged exception is broken down as follows: +// +// -from {merged_from - subset_from} -thru merged_thru... -to merged_to +// -from merged_from -thru {merged_thru - subset_thru}... -to merged_to +// -from merged_from -thru merged_thru... -to {merged_to - subset_to} +// +// Where the {set1 - set2} is the set difference of of the from/thru/to +// objects of the merged/subset exception. If the set difference is empty, +// that group of exceptions matches the subset so it should not be included +// in the expansion. + +void +Sdc::deleteMatchingExceptions(ExceptionPath *exception) +{ + debugPrint1(debug_, "exception_merge", 1, "find matches for %s\n", + exception->asString(network_)); + ExceptionPathSet matches; + findMatchingExceptions(exception, matches); + + ExceptionPathSet expanded_matches; + ExceptionPathSet::Iterator match_iter1(matches); + while (match_iter1.hasNext()) { + ExceptionPath *match = match_iter1.next(); + // 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); + } + + ExceptionPathSet::Iterator match_iter2(matches); + while (match_iter2.hasNext()) { + ExceptionPath *match = match_iter2.next(); + deleteException(match); + } + + ExceptionPathSet::Iterator expanded_iter(expanded_matches); + while (expanded_iter.hasNext()) { + ExceptionPath *expand = expanded_iter.next(); + addException(expand); + } +} + +void +Sdc::findMatchingExceptions(ExceptionPath *exception, + ExceptionPathSet &matches) +{ + if (exception->from()) + findMatchingExceptionsFirstFrom(exception, matches); + else if (exception->thrus()) + findMatchingExceptionsFirstThru(exception, matches); + else if (exception->to()) + findMatchingExceptionsFirstTo(exception, matches); +} + +void +Sdc::findMatchingExceptionsFirstFrom(ExceptionPath *exception, + ExceptionPathSet &matches) +{ + ExceptionFrom *from = exception->from(); + if (first_from_pin_exceptions_) + findMatchingExceptionsPins(exception, from->pins(), + first_from_pin_exceptions_, + matches); + if (first_from_inst_exceptions_) + findMatchingExceptionsInsts(exception, from->instances(), + first_from_inst_exceptions_, matches); + if (first_from_clk_exceptions_) + findMatchingExceptionsClks(exception, from->clks(), + first_from_clk_exceptions_, + matches); +} + +void +Sdc::findMatchingExceptionsFirstThru(ExceptionPath *exception, + ExceptionPathSet &matches) +{ + ExceptionThru *thru = (*exception->thrus())[0]; + findMatchingExceptionsPins(exception, thru->pins(), + first_thru_pin_exceptions_, + matches); + findMatchingExceptionsInsts(exception, thru->instances(), + first_thru_inst_exceptions_, + matches); + if (first_thru_net_exceptions_) { + NetSet::Iterator net_iter(thru->nets()); + if (net_iter.hasNext()) { + Net *net = net_iter.next(); + // Potential matches includes exceptions that match net that are not + // the first exception point. + ExceptionPathSet *potential_matches = + first_thru_net_exceptions_->findKey(net); + if (potential_matches) { + ExceptionPathSet::Iterator match_iter(potential_matches); + while (match_iter.hasNext()) { + ExceptionPath *match = match_iter.next(); + ExceptionThru *match_thru = (*match->thrus())[0]; + if (match_thru->nets()->hasKey(net) + && match->overrides(exception) + && match->intersectsPts(exception)) + matches.insert(match); + } + } + } + } +} + +void +Sdc::findMatchingExceptionsFirstTo(ExceptionPath *exception, + ExceptionPathSet &matches) +{ + ExceptionTo *to = exception->to(); + findMatchingExceptionsPins(exception, to->pins(), first_to_pin_exceptions_, + matches); + findMatchingExceptionsInsts(exception, to->instances(), + first_to_inst_exceptions_, + matches); + findMatchingExceptionsClks(exception, to->clks(), first_to_clk_exceptions_, + matches); +} + +void +Sdc::findMatchingExceptionsClks(ExceptionPath *exception, + ClockSet *clks, + ClockExceptionsMap *exception_map, + ExceptionPathSet &matches) +{ + if (exception_map) { + ExceptionPathSet clks_matches; + ClockSet::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + clks_matches.insertSet(exception_map->findKey(clk)); + } + findMatchingExceptions(exception, &clks_matches, matches); + } +} + +void +Sdc::findMatchingExceptionsPins(ExceptionPath *exception, + PinSet *pins, + PinExceptionsMap *exception_map, + ExceptionPathSet &matches) +{ + if (exception_map) { + ExceptionPathSet pins_matches; + PinSet::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + const Pin *pin = pin_iter.next(); + pins_matches.insertSet(exception_map->findKey(pin)); + } + findMatchingExceptions(exception, &pins_matches, matches); + } +} + +void +Sdc::findMatchingExceptionsInsts(ExceptionPath *exception, + InstanceSet *insts, + InstanceExceptionsMap *exception_map, + ExceptionPathSet &matches) +{ + if (exception_map) { + ExceptionPathSet insts_matches; + InstanceSet::Iterator inst_iter(insts); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + insts_matches.insertSet(exception_map->findKey(inst)); + } + findMatchingExceptions(exception, &insts_matches, matches); + } +} + +void +Sdc::findMatchingExceptions(ExceptionPath *exception, + ExceptionPathSet *potential_matches, + ExceptionPathSet &matches) +{ + if (potential_matches) { + ExceptionPathSet::Iterator match_iter(potential_matches); + while (match_iter.hasNext()) { + ExceptionPath *match = match_iter.next(); + if (match->overrides(exception) + && match->intersectsPts(exception)) + matches.insert(match); + } + } +} + +void +Sdc::expandExceptionExcluding(ExceptionPath *exception, + ExceptionPath *excluding, + ExceptionPathSet &expansions) +{ + ExceptionFrom *from = exception->from(); + ExceptionThruSeq *thrus = exception->thrus(); + ExceptionTo *to = exception->to(); + if (from) { + ExceptionFrom *from_cpy = from->clone(); + from_cpy->deleteObjects(excluding->from()); + if (from_cpy->hasObjects()) { + ExceptionThruSeq *thrus_cpy = nullptr; + if (thrus) + thrus_cpy = clone(thrus, network_); + ExceptionTo *to_cpy = nullptr; + if (to) + to_cpy = to->clone(); + ExceptionPath *expand = exception->clone(from_cpy,thrus_cpy,to_cpy,true); + expansions.insert(expand); + } + else + 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(); + ExceptionThru *thru_cpy = thru->clone(network_); + thru_cpy->deleteObjects(thru2); + if (thru_cpy->hasObjects()) { + ExceptionFrom *from_cpy = nullptr; + if (from) + from_cpy = from->clone(); + ExceptionThruSeq *thrus_cpy = new ExceptionThruSeq; + ExceptionThruSeq::Iterator thru_iter1(thrus); + while (thru_iter1.hasNext()) { + ExceptionThru *thru1 = thru_iter1.next(); + 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(); + ExceptionPath *expand = exception->clone(from_cpy, thrus_cpy, to_cpy, + true); + expansions.insert(expand); + } + else + delete thru_cpy; + } + } + if (to) { + ExceptionTo *to_cpy = to->clone(); + to_cpy->deleteObjects(excluding->to()); + if (to_cpy->hasObjects()) { + ExceptionFrom *from_cpy = nullptr; + if (from) + from_cpy = from->clone(); + ExceptionThruSeq *thrus_cpy = nullptr; + if (thrus) + thrus_cpy = clone(thrus, network_); + ExceptionPath *expand = exception->clone(from_cpy,thrus_cpy,to_cpy,true); + expansions.insert(expand); + } + else + delete to_cpy; + } +} + +static ExceptionThruSeq * +clone(ExceptionThruSeq *thrus, Network *network) +{ + ExceptionThruSeq *thrus_cpy = new ExceptionThruSeq; + ExceptionThruSeq::Iterator thru_iter(thrus); + while (thru_iter.hasNext()) { + ExceptionThru *thru = thru_iter.next(); + ExceptionThru *thru_cpy = thru->clone(network); + thrus_cpy->push_back(thru_cpy); + } + return thrus_cpy; +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::recordException(ExceptionPath *exception) +{ + exceptions_.insert(exception); + recordMergeHashes(exception); + recordExceptionFirstPts(exception); +} + +void +Sdc::recordMergeHashes(ExceptionPath *exception) +{ + ExceptionPtIterator missing_pt_iter(exception); + while (missing_pt_iter.hasNext()) { + ExceptionPt *missing_pt = missing_pt_iter.next(); + recordMergeHash(exception, missing_pt); + } +} + +void +Sdc::recordMergeHash(ExceptionPath *exception, + ExceptionPt *missing_pt) +{ + size_t hash = exception->hash(missing_pt); + debugPrint3(debug_, "exception_merge", 3, + "record merge hash %zu %s missing %s\n", + hash, + exception->asString(network_), + missing_pt->asString(network_)); + ExceptionPathSet *set = exception_merge_hash_.findKey(hash); + if (set == nullptr) { + set = new ExceptionPathSet; + exception_merge_hash_[hash] = set; + } + set->insert(exception); +} + +// Record a mapping from first pin/clock/instance's to a set of exceptions. +// The first exception point is when the exception becomes active. +// After it becomes active, its state changes as the other +// exception points are traversed. +void +Sdc::recordExceptionFirstPts(ExceptionPath *exception) +{ + if (exception->from()) + recordExceptionFirstFrom(exception); + else if (exception->thrus()) + recordExceptionFirstThru(exception); + else if (exception->to()) + recordExceptionFirstTo(exception); +} + +void +Sdc::recordExceptionFirstFrom(ExceptionPath *exception) +{ + ExceptionFrom *from = exception->from(); + recordExceptionPins(exception, from->pins(), first_from_pin_exceptions_); + recordExceptionInsts(exception, from->instances(), + first_from_inst_exceptions_); + recordExceptionClks(exception, from->clks(), first_from_clk_exceptions_); +} + +void +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_); + recordExceptionEdges(exception, thru->edges(), first_thru_edge_exceptions_); + ExceptionThruSeq::Iterator thru_iter(exception->thrus()); + while (thru_iter.hasNext()) { + ExceptionThru *thru = thru_iter.next(); + recordExceptionNets(exception, thru->nets(), first_thru_net_exceptions_); + } +} + +void +Sdc::recordExceptionFirstTo(ExceptionPath *exception) +{ + ExceptionTo *to = exception->to(); + recordExceptionPins(exception, to->pins(), first_to_pin_exceptions_); + recordExceptionInsts(exception, to->instances(), first_to_inst_exceptions_); + recordExceptionClks(exception, to->clks(), first_to_clk_exceptions_); +} + +void +Sdc::recordExceptionClks(ExceptionPath *exception, + ClockSet *clks, + ClockExceptionsMap *&exception_map) +{ + ClockSet::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + ExceptionPathSet *set = nullptr; + if (exception_map == nullptr) + exception_map = new ClockExceptionsMap; + else + set = exception_map->findKey(clk); + if (set == nullptr) { + set = new ExceptionPathSet; + (*exception_map)[clk] = set; + } + set->insert(exception); + } +} + +void +Sdc::recordExceptionEdges(ExceptionPath *exception, + EdgePinsSet *edges, + EdgeExceptionsMap *&exception_map) +{ + EdgePinsSet::Iterator edge_iter(edges); + while (edge_iter.hasNext()) { + EdgePins *edge = edge_iter.next(); + ExceptionPathSet *set = nullptr; + if (exception_map == nullptr) + exception_map = new EdgeExceptionsMap; + else + set = exception_map->findKey(edge); + if (set == nullptr) { + set = new ExceptionPathSet; + // Copy the EdgePins so it is owned by the map. + edge = new EdgePins(*edge); + exception_map->insert(edge, set); + } + set->insert(exception); + } +} + +void +Sdc::recordExceptionPins(ExceptionPath *exception, + PinSet *pins, + PinExceptionsMap *&exception_map) +{ + PinSet::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + const Pin *pin = pin_iter.next(); + ExceptionPathSet *set = nullptr; + if (exception_map == nullptr) + exception_map = new PinExceptionsMap; + else + set = exception_map->findKey(pin); + if (set == nullptr) { + set = new ExceptionPathSet; + exception_map->insert(pin, set); + } + set->insert(exception); + } +} + +void +Sdc::recordExceptionHpin(ExceptionPath *exception, + Pin *pin, + PinExceptionsMap *&exception_map) +{ + ExceptionPathSet *set = nullptr; + if (exception_map == nullptr) + exception_map = new PinExceptionsMap; + else + set = exception_map->findKey(pin); + if (set == nullptr) { + set = new ExceptionPathSet; + exception_map->insert(pin, set); + } + set->insert(exception); +} + +void +Sdc::recordExceptionInsts(ExceptionPath *exception, + InstanceSet *insts, + InstanceExceptionsMap *&exception_map) +{ + InstanceSet::Iterator inst_iter(insts); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + ExceptionPathSet *set = nullptr; + if (exception_map == nullptr) + exception_map = new InstanceExceptionsMap; + else + set = exception_map->findKey(inst); + if (set == nullptr) { + set = new ExceptionPathSet; + (*exception_map)[inst] = set; + } + set->insert(exception); + } +} + +void +Sdc::recordExceptionNets(ExceptionPath *exception, + NetSet *nets, + NetExceptionsMap *&exception_map) +{ + NetSet::Iterator net_iter(nets); + while (net_iter.hasNext()) { + const Net *net = net_iter.next(); + ExceptionPathSet *set = nullptr; + if (exception_map == nullptr) + exception_map = new NetExceptionsMap; + else + set = exception_map->findKey(net); + if (set == nullptr) { + set = new ExceptionPathSet; + (*exception_map)[net] = set; + } + set->insert(exception); + } +} + +// Exceptions of the same type can be merged if they differ in exactly +// one exception point (-from, -thru or -to). +// For example, the following exceptions: +// set_false_path -from {A B} -to C +// set_false_path -from {A B} -to D +// can be merged to form: +// set_false_path -from {A B} -to {C D} +// +// A hash is generated for each exception missing one exception point +// to find potential matches. If a match is found, the exceptions are +// merged. Next we try to merge the surviving exception until we run +// out of merges. +void +Sdc::mergeException(ExceptionPath *exception) +{ + ExceptionPath *merged = findMergeMatch(exception); + while (merged) + merged = findMergeMatch(merged); +} + +// Return the merged result. +ExceptionPath * +Sdc::findMergeMatch(ExceptionPath *exception) +{ + bool first_pt = true; + ExceptionPtIterator missing_pt_iter(exception); + while (missing_pt_iter.hasNext()) { + ExceptionPt *missing_pt = missing_pt_iter.next(); + size_t hash = exception->hash(missing_pt); + ExceptionPathSet *matches = exception_merge_hash_.findKey(hash); + if (matches) { + ExceptionPathSet::Iterator match_iter(matches); + while (match_iter.hasNext()) { + ExceptionPath *match = match_iter.next(); + 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)) { + debugPrint1(debug_, "exception_merge", 1, "merge %s\n", + exception->asString(network_)); + debugPrint1(debug_, "exception_merge", 1, " with %s\n", + match->asString(network_)); + // Unrecord the exception that is being merged away. + unrecordException(exception); + unrecordMergeHashes(match); + missing_pt->mergeInto(match_missing_pt); + 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; + } + } + } + first_pt = false; + } + return nullptr; +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::deleteExceptions() +{ + ExceptionPathSet::Iterator except_iter(exceptions_); + while (except_iter.hasNext()) { + delete except_iter.next(); + } + exceptions_.clear(); + + deleteExceptionMap(first_from_pin_exceptions_); + deleteExceptionMap(first_from_clk_exceptions_); + deleteExceptionMap(first_from_inst_exceptions_); + deleteExceptionMap(first_to_pin_exceptions_); + deleteExceptionMap(first_to_clk_exceptions_); + deleteExceptionMap(first_to_inst_exceptions_); + deleteExceptionMap(first_thru_pin_exceptions_); + deleteExceptionMap(first_thru_inst_exceptions_); + deleteExceptionMap(first_thru_net_exceptions_); + deleteExceptionMap(first_thru_edge_exceptions_); + + delete path_delay_internal_startpoints_; + path_delay_internal_startpoints_ = nullptr; + + delete path_delay_internal_endpoints_; + path_delay_internal_endpoints_ = nullptr; + + deleteExceptionPtHashMapSets(exception_merge_hash_); + exception_merge_hash_.clear(); +} + +void +Sdc::deleteExceptionPtHashMapSets(ExceptionPathPtHash &map) +{ + ExceptionPathPtHash::Iterator set_iter(map); + while (set_iter.hasNext()) + delete set_iter.next(); +} + +void +Sdc::deleteExceptionMap(PinExceptionsMap *&exception_map) +{ + PinExceptionsMap::Iterator set_iter(exception_map); + while (set_iter.hasNext()) { + const Pin *pin; + ExceptionPathSet *set; + set_iter.next(pin, set); + delete set; + } + delete exception_map; + exception_map = nullptr; +} + +void +Sdc::deleteExceptionMap(InstanceExceptionsMap *&exception_map) +{ + InstanceExceptionsMap::Iterator set_iter(exception_map); + while (set_iter.hasNext()) { + const Instance *inst; + ExceptionPathSet *set; + set_iter.next(inst, set); + delete set; + } + delete exception_map; + exception_map = nullptr; +} + +void +Sdc::deleteExceptionMap(NetExceptionsMap *&exception_map) +{ + NetExceptionsMap::Iterator set_iter(exception_map); + while (set_iter.hasNext()) { + const Net *net; + ExceptionPathSet *set; + set_iter.next(net, set); + delete set; + } + delete exception_map; + exception_map = nullptr; +} + +void +Sdc::deleteExceptionMap(ClockExceptionsMap *&exception_map) +{ + ClockExceptionsMap::Iterator set_iter(exception_map); + while (set_iter.hasNext()) { + const Clock *clk; + ExceptionPathSet *set; + set_iter.next(clk, set); + delete set; + } + delete exception_map; + exception_map = nullptr; +} + +void +Sdc::deleteExceptionMap(EdgeExceptionsMap *&exception_map) +{ + EdgeExceptionsMap::Iterator set_iter(exception_map); + while (set_iter.hasNext()) { + const EdgePins *edge_pins; + ExceptionPathSet *set; + set_iter.next(edge_pins, set); + delete set; + delete edge_pins; + } + delete exception_map; + exception_map = nullptr; +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::deleteExceptionsReferencing(Clock *clk) +{ + ExceptionPathSet::ConstIterator exception_iter(exceptions_); + while (exception_iter.hasNext()) { + ExceptionPath *exception = exception_iter.next(); + bool deleted = false; + ExceptionFrom *from = exception->from(); + if (from) { + ClockSet *clks = from->clks(); + if (clks && clks->hasKey(clk)) { + unrecordException(exception); + from->deleteClock(clk); + if (from->hasObjects()) + recordException(exception); + else { + deleteException(exception); + deleted = true; + } + } + } + + if (!deleted) { + ExceptionTo *to = exception->to(); + if (to) { + ClockSet *clks = to->clks(); + if (clks && clks->hasKey(clk)) { + unrecordException(exception); + to->deleteClock(clk); + if (to->hasObjects()) + recordException(exception); + else + deleteException(exception); + } + } + } + } +} + +void +Sdc::deleteException(ExceptionPath *exception) +{ + debugPrint1(debug_, "exception_merge", 2, "delete %s\n", + exception->asString(network_)); + unrecordException(exception); + delete exception; +} + +void +Sdc::unrecordException(ExceptionPath *exception) +{ + unrecordMergeHashes(exception); + unrecordExceptionFirstPts(exception); + exceptions_.erase(exception); +} + +void +Sdc::unrecordMergeHashes(ExceptionPath *exception) +{ + ExceptionPtIterator missing_pt_iter(exception); + while (missing_pt_iter.hasNext()) { + ExceptionPt *missing_pt = missing_pt_iter.next(); + unrecordMergeHash(exception, missing_pt); + } +} + +void +Sdc::unrecordMergeHash(ExceptionPath *exception, + ExceptionPt *missing_pt) +{ + size_t hash = exception->hash(missing_pt); + debugPrint3(debug_, "exception_merge", 3, + "unrecord merge hash %zu %s missing %s\n", + hash, + exception->asString(network_), + missing_pt->asString(network_)); + ExceptionPathSet *matches = exception_merge_hash_.findKey(hash); + if (matches) + matches->erase(exception); +} + +void +Sdc::unrecordExceptionFirstPts(ExceptionPath *exception) +{ + ExceptionFrom *from = exception->from(); + ExceptionThruSeq *thrus = exception->thrus(); + ExceptionTo *to = exception->to(); + if (from) { + unrecordExceptionPins(exception, from->pins(), first_from_pin_exceptions_); + unrecordExceptionClks(exception, from->clks(), first_from_clk_exceptions_); + unrecordExceptionInsts(exception, from->instances(), + first_from_inst_exceptions_); + } + else if (thrus) { + ExceptionThru *thru = (*thrus)[0]; + unrecordExceptionPins(exception, thru->pins(), first_thru_pin_exceptions_); + unrecordExceptionInsts(exception, thru->instances(), + first_thru_inst_exceptions_); + unrecordExceptionNets(exception, thru->nets(), first_thru_net_exceptions_); + unrecordExceptionEdges(exception, thru->edges(), + 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_); + } +} + +void +Sdc::unrecordExceptionClks(ExceptionPath *exception, + ClockSet *clks, + ClockExceptionsMap *exception_map) +{ + ClockSet::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + ExceptionPathSet *set = exception_map->findKey(clk); + if (set) + set->erase(exception); + } +} + +void +Sdc::unrecordExceptionPins(ExceptionPath *exception, + PinSet *pins, + PinExceptionsMap *exception_map) +{ + PinSet::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + const Pin *pin = pin_iter.next(); + ExceptionPathSet *set = exception_map->findKey(pin); + if (set) + set->erase(exception); + } +} + +void +Sdc::unrecordExceptionInsts(ExceptionPath *exception, + InstanceSet *insts, + InstanceExceptionsMap *exception_map) +{ + InstanceSet::Iterator inst_iter(insts); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + ExceptionPathSet *set = exception_map->findKey(inst); + if (set) + set->erase(exception); + } +} + +void +Sdc::unrecordExceptionEdges(ExceptionPath *exception, + EdgePinsSet *edges, + EdgeExceptionsMap *exception_map) +{ + EdgePinsSet::Iterator edge_iter(edges); + while (edge_iter.hasNext()) { + EdgePins *edge = edge_iter.next(); + ExceptionPathSet *set = exception_map->findKey(edge); + if (set) + set->erase(exception); + } +} + +void +Sdc::unrecordExceptionNets(ExceptionPath *exception, + NetSet *nets, + NetExceptionsMap *exception_map) +{ + NetSet::Iterator net_iter(nets); + while (net_iter.hasNext()) { + const Net *net = net_iter.next(); + ExceptionPathSet *set = exception_map->findKey(net); + if (set) + set->erase(exception); + } +} + +void +Sdc::unrecordExceptionHpin(ExceptionPath *exception, + Pin *pin, + PinExceptionsMap *&exception_map) +{ + ExceptionPathSet *set = exception_map->findKey(pin); + if (set) + set->erase(exception); +} + +//////////////////////////////////////////////////////////////// + +class ExpandException : public ExpandedExceptionVisitor +{ +public: + ExpandException(ExceptionPath *exception, + ExceptionPathSet &expansions, + Network *network); + virtual void visit(ExceptionFrom *from, ExceptionThruSeq *thrus, + ExceptionTo *to); + +private: + ExceptionPathSet &expansions_; + +private: + DISALLOW_COPY_AND_ASSIGN(ExpandException); +}; + +ExpandException::ExpandException(ExceptionPath *exception, + ExceptionPathSet &expansions, + Network *network) : + ExpandedExceptionVisitor(exception, network), + expansions_(expansions) +{ +} + +void +ExpandException::visit(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to) +{ + ExceptionFrom *from_clone = nullptr; + if (from) + from_clone = from->clone(); + ExceptionThruSeq *thrus_clone = nullptr; + if (thrus) { + thrus_clone = new ExceptionThruSeq; + ExceptionThruSeq::Iterator thru_iter(thrus); + while (thru_iter.hasNext()) { + ExceptionThru *thru = thru_iter.next(); + thrus_clone->push_back(thru->clone(network_)); + } + } + ExceptionTo *to_clone = nullptr; + if (to) + to_clone = to->clone(); + ExceptionPath *expand = exception_->clone(from_clone, thrus_clone, + to_clone, true); + expansions_.insert(expand); +} + +// Expand exception from/thrus/to sets so there is only one exception +// point in each from/thru/to. +void +Sdc::expandException(ExceptionPath *exception, + ExceptionPathSet &expansions) +{ + ExpandException expander(exception, expansions, network_); + expander.visitExpansions(); +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::resetPath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max) +{ + checkFromThrusTo(from, thrus, to); + ExceptionPathSet::Iterator except_iter(exceptions_); + while (except_iter.hasNext()) { + ExceptionPath *match = except_iter.next(); + if (match->resetMatch(from, thrus, to, min_max)) { + debugPrint1(debug_, "exception_match", 3, "reset match %s\n", + match->asString(network_)); + ExceptionPathSet expansions; + expandException(match, expansions); + deleteException(match); + ExceptionPathSet::Iterator expand_iter(expansions); + while (expand_iter.hasNext()) { + ExceptionPath *expand = expand_iter.next(); + if (expand->resetMatch(from, thrus, to, min_max)) { + unrecordPathDelayInternalStartpoints(expand->from()); + unrecordPathDelayInternalEndpoints(expand); + delete expand; + } + else + addException(expand); + } + } + } +} + +//////////////////////////////////////////////////////////////// + +bool +Sdc::exceptionFromStates(const Pin *pin, + const TransRiseFall *tr, + const Clock *clk, + const TransRiseFall *clk_tr, + const MinMax *min_max, + ExceptionStateSet *&states) const +{ + return exceptionFromStates(pin, tr, clk, clk_tr, min_max, true, states); +} + +bool +Sdc::exceptionFromStates(const Pin *pin, + const TransRiseFall *tr, + const Clock *clk, + const TransRiseFall *clk_tr, + const MinMax *min_max, + bool include_filter, + ExceptionStateSet *&states) const +{ + bool srch_from = true; + if (pin) { + if (srch_from && first_from_pin_exceptions_) + srch_from &= exceptionFromStates(first_from_pin_exceptions_->findKey(pin), + nullptr, tr, min_max, include_filter, + states); + if (srch_from && first_thru_pin_exceptions_) + srch_from &= exceptionFromStates(first_thru_pin_exceptions_->findKey(pin), + nullptr, tr, min_max, include_filter, + states); + + if (srch_from + && (first_from_inst_exceptions_ || first_thru_inst_exceptions_)) { + Instance *inst = network_->instance(pin); + if (srch_from && first_from_inst_exceptions_) + srch_from &= exceptionFromStates(first_from_inst_exceptions_->findKey(inst), + pin, tr, min_max, include_filter, + states); + if (srch_from && first_thru_inst_exceptions_) + srch_from &= exceptionFromStates(first_thru_inst_exceptions_->findKey(inst), + pin, tr, min_max, include_filter, + states); + } + } + if (srch_from && clk && first_from_clk_exceptions_) + srch_from &= exceptionFromStates(first_from_clk_exceptions_->findKey(clk), + pin, clk_tr, min_max, include_filter, + states); + if (!srch_from) { + delete states; + states = nullptr; + } + return srch_from; +} + +bool +Sdc::exceptionFromStates(const ExceptionPathSet *exceptions, + const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max, + bool include_filter, + ExceptionStateSet *&states) const +{ + if (exceptions) { + ExceptionPathSet::ConstIterator exception_iter(exceptions); + while (exception_iter.hasNext()) { + ExceptionPath *exception = exception_iter.next(); + if (exception->matches(min_max, false) + && (exception->from() == nullptr + || exception->from()->transition()->matches(tr)) + && (include_filter || !exception->isFilter())) { + ExceptionState *state = exception->firstState(); + if (state->matchesNextThru(nullptr, pin, tr, 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); + } + } + } + return true; +} + +void +Sdc::exceptionFromClkStates(const Pin *pin, + const TransRiseFall *tr, + const Clock *clk, + const TransRiseFall *clk_tr, + const MinMax *min_max, + ExceptionStateSet *&states) const +{ + if (pin) { + if (first_from_pin_exceptions_) + exceptionFromStates(first_from_pin_exceptions_->findKey(pin), + nullptr, tr, min_max, true, states); + if (first_from_inst_exceptions_) { + Instance *inst = network_->instance(pin); + exceptionFromStates(first_from_inst_exceptions_->findKey(inst), + pin, tr, min_max, true, states); + } + } + if (first_from_clk_exceptions_) + exceptionFromStates(first_from_clk_exceptions_->findKey(clk), + pin, clk_tr, min_max, true, states); +} + +void +Sdc::filterRegQStates(const Pin *to_pin, + const TransRiseFall *to_tr, + const MinMax *min_max, + ExceptionStateSet *&states) const +{ + if (first_from_pin_exceptions_) { + const ExceptionPathSet *exceptions = + first_from_pin_exceptions_->findKey(to_pin); + if (exceptions) { + ExceptionPathSet::ConstIterator exception_iter(exceptions); + while (exception_iter.hasNext()) { + ExceptionPath *exception = exception_iter.next(); + // Hack for filter -from reg/Q. + if (exception->isFilter() + && exception->matchesFirstPt(to_tr, min_max)) { + ExceptionState *state = exception->firstState(); + if (states == nullptr) + states = new ExceptionStateSet; + states->insert(state); + } + } + } + } +} + +void +Sdc::exceptionThruStates(const Pin *from_pin, + const Pin *to_pin, + const TransRiseFall *to_tr, + const MinMax *min_max, + ExceptionStateSet *&states) const +{ + if (first_thru_pin_exceptions_) + exceptionThruStates(first_thru_pin_exceptions_->findKey(to_pin), + to_tr, min_max, states); + if (first_thru_edge_exceptions_) { + EdgePins edge_pins(const_cast(from_pin), const_cast(to_pin)); + exceptionThruStates(first_thru_edge_exceptions_->findKey(&edge_pins), + to_tr, min_max, states); + } + if (first_thru_inst_exceptions_ + && (network_->direction(to_pin)->isAnyOutput() + || network_->isLatchData(to_pin))) { + const Instance *to_inst = network_->instance(to_pin); + exceptionThruStates(first_thru_inst_exceptions_->findKey(to_inst), + to_tr, min_max, states); + } +} + +void +Sdc::exceptionThruStates(const ExceptionPathSet *exceptions, + const TransRiseFall *to_tr, + const MinMax *min_max, + // Return value. + ExceptionStateSet *&states) const +{ + if (exceptions) { + ExceptionPathSet::ConstIterator exception_iter(exceptions); + while (exception_iter.hasNext()) { + ExceptionPath *exception = exception_iter.next(); + if (exception->matchesFirstPt(to_tr, min_max)) { + ExceptionState *state = exception->firstState(); + if (states == nullptr) + states = new ExceptionStateSet; + states->insert(state); + } + } + } +} + +void +Sdc::exceptionTo(ExceptionPathType type, + const Pin *pin, + const TransRiseFall *tr, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority) const +{ + if (first_to_inst_exceptions_) { + Instance *inst = network_->instance(pin); + exceptionTo(first_to_inst_exceptions_->findKey(inst), type, pin, tr, + clk_edge, min_max, match_min_max_exactly, + hi_priority_exception, hi_priority); + } + if (first_to_pin_exceptions_) + exceptionTo(first_to_pin_exceptions_->findKey(pin), type, pin, tr, + clk_edge, min_max, match_min_max_exactly, + hi_priority_exception, hi_priority); + if (clk_edge && first_to_clk_exceptions_) + exceptionTo(first_to_clk_exceptions_->findKey(clk_edge->clock()), + type, pin, tr, clk_edge, min_max, match_min_max_exactly, + hi_priority_exception, hi_priority); +} + +void +Sdc::exceptionTo(const ExceptionPathSet *to_exceptions, + ExceptionPathType type, + const Pin *pin, + const TransRiseFall *tr, + 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) { + ExceptionPathSet::ConstIterator exception_iter(to_exceptions); + while (exception_iter.hasNext()) { + ExceptionPath *exception = exception_iter.next(); + exceptionTo(exception, type, pin, tr, clk_edge, + min_max, match_min_max_exactly, + hi_priority_exception, hi_priority); + } + } +} + +void +Sdc::exceptionTo(ExceptionPath *exception, + ExceptionPathType type, + const Pin *pin, + const TransRiseFall *tr, + 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, tr, clk_edge, min_max, + 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))) { + hi_priority = priority; + hi_priority_exception = exception; + } + } +} + +bool +Sdc::exceptionMatchesTo(ExceptionPath *exception, + const Pin *pin, + const TransRiseFall *tr, + 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, tr, network_))); +} + +bool +Sdc::isCompleteTo(ExceptionState *state, + const Pin *pin, + const TransRiseFall *tr, + 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, tr, clk_edge, + min_max, match_min_max_exactly, require_to_pin); +} + +//////////////////////////////////////////////////////////////// + +Wireload * +Sdc::wireloadDefaulted(const MinMax *min_max) +{ + Wireload *wireload1 = wireload(min_max); + if (wireload1 == nullptr) { + LibertyLibrary *default_lib = network_->defaultLibertyLibrary(); + if (default_lib) + wireload1 = default_lib->defaultWireload(); + } + return wireload1; +} + +Wireload * +Sdc::wireload(const MinMax *min_max) +{ + return wireload_[min_max->index()]; +} + +void +Sdc::setWireload(Wireload *wireload, + const MinMaxAll *min_max) +{ + for (auto mm_index : min_max->rangeIndex()) + wireload_[mm_index] = wireload; +} + +void +Sdc::setWireloadMode(WireloadMode mode) +{ + wireload_mode_ = mode; +} + +WireloadMode +Sdc::wireloadMode() +{ + return wireload_mode_; +} + +const WireloadSelection * +Sdc::wireloadSelection(const MinMax *min_max) +{ + const WireloadSelection *sel = wireload_selection_[min_max->index()]; + if (sel == nullptr) { + // Look for a default. + LibertyLibrary *lib = network_->defaultLibertyLibrary(); + if (lib) { + WireloadSelection *default_sel = lib->defaultWireloadSelection(); + if (default_sel) { + sel = default_sel; + setWireloadSelection(default_sel, MinMaxAll::all()); + } + } + } + return sel; +} + +void +Sdc::setWireloadSelection(WireloadSelection *selection, + const MinMaxAll *min_max) +{ + for (auto mm_index : min_max->rangeIndex()) + wireload_selection_[mm_index] = selection; +} + +//////////////////////////////////////////////////////////////// + +bool +Sdc::crprEnabled() const +{ + return crpr_enabled_; +} + +void +Sdc::setCrprEnabled(bool enabled) +{ + crpr_enabled_ = enabled; +} + +CrprMode +Sdc::crprMode() const +{ + return crpr_mode_; +} + +void +Sdc::setCrprMode(CrprMode mode) +{ + crpr_mode_ = mode; +} + +bool +Sdc::crprActive() const +{ + return analysis_type_ == AnalysisType::ocv + && crpr_enabled_; +} + +bool +Sdc::propagateGatedClockEnable() const +{ + return propagate_gated_clock_enable_; +} + +void +Sdc::setPropagateGatedClockEnable(bool enable) +{ + propagate_gated_clock_enable_ = enable; +} + +bool +Sdc::presetClrArcsEnabled() const +{ + return preset_clr_arcs_enabled_; +} + +void +Sdc::setPresetClrArcsEnabled(bool enable) +{ + preset_clr_arcs_enabled_ = enable; +} + +bool +Sdc::condDefaultArcsEnabled() const +{ + return cond_default_arcs_enabled_; +} + +void +Sdc::setCondDefaultArcsEnabled(bool enabled) +{ + cond_default_arcs_enabled_ = enabled; +} + +bool +Sdc::isDisabledCondDefault(Edge *edge) const +{ + return !cond_default_arcs_enabled_ + && edge->timingArcSet()->isCondDefault(); +} + +bool +Sdc::bidirectInstPathsEnabled() const +{ + return bidirect_inst_paths_enabled_; +} + +void +Sdc::setBidirectInstPathsEnabled(bool enabled) +{ + bidirect_inst_paths_enabled_ = enabled; +} + +// Delay calculation propagates slews from a bidirect driver +// to the bidirect port and back through the bidirect driver when +// sta_bidirect_inst_paths_enabled_ is true. +bool +Sdc::bidirectDrvrSlewFromLoad(const Pin *pin) const +{ + return bidirect_inst_paths_enabled_ + && network_->direction(pin)->isBidirect() + && network_->isTopLevelPort(pin); +} + +bool +Sdc::bidirectNetPathsEnabled() const +{ + return bidirect_inst_paths_enabled_; +} + +void +Sdc::setBidirectNetPathsEnabled(bool enabled) +{ + bidirect_inst_paths_enabled_ = enabled; +} + +bool +Sdc::recoveryRemovalChecksEnabled() const +{ + return recovery_removal_checks_enabled_; +} + +void +Sdc::setRecoveryRemovalChecksEnabled(bool enabled) +{ + recovery_removal_checks_enabled_ = enabled; +} + +bool +Sdc::gatedClkChecksEnabled() const +{ + return gated_clk_checks_enabled_; +} + +void +Sdc::setGatedClkChecksEnabled(bool enabled) +{ + gated_clk_checks_enabled_ = enabled; +} + +bool +Sdc::dynamicLoopBreaking() const +{ + return dynamic_loop_breaking_; +} + +void +Sdc::setDynamicLoopBreaking(bool enable) +{ + if (dynamic_loop_breaking_ != enable) { + if (levelize_->levelized()) { + if (enable) + makeLoopExceptions(); + else + deleteLoopExceptions(); + } + dynamic_loop_breaking_ = enable; + } +} + +bool +Sdc::propagateAllClocks() const +{ + return propagate_all_clks_; +} + +void +Sdc::setPropagateAllClocks(bool prop) +{ + propagate_all_clks_ = prop; +} + +bool +Sdc::clkThruTristateEnabled() const +{ + return clk_thru_tristate_enabled_; +} + +void +Sdc::setClkThruTristateEnabled(bool enable) +{ + clk_thru_tristate_enabled_ = enable; +} + +ClockEdge * +Sdc::defaultArrivalClockEdge() const +{ + return default_arrival_clk_->edge(TransRiseFall::rise()); +} + +bool +Sdc::useDefaultArrivalClock() +{ + return use_default_arrival_clock_; +} + +void +Sdc::setUseDefaultArrivalClock(bool enable) +{ + use_default_arrival_clock_ = enable; +} + +//////////////////////////////////////////////////////////////// + +void +Sdc::connectPinAfter(Pin *pin) +{ + PinSet *drvrs = network_->drivers(pin); + ExceptionPathSet::Iterator except_iter(exceptions_); + while (except_iter.hasNext()) { + ExceptionPath *exception = except_iter.next(); + ExceptionPt *first_pt = exception->firstPt(); + ExceptionThruSeq::Iterator thru_iter(exception->thrus()); + while (thru_iter.hasNext()) { + ExceptionThru *thru = thru_iter.next(); + if (thru->edges()) { + thru->connectPinAfter(drvrs, network_); + if (first_pt == thru) + recordExceptionEdges(exception, thru->edges(), + first_thru_edge_exceptions_); + } + } + } +} + +void +Sdc::disconnectPinBefore(Pin *pin) +{ + ExceptionPathSet::Iterator except_iter(exceptions_); + while (except_iter.hasNext()) { + ExceptionPath *exception = except_iter.next(); + ExceptionPt *first_pt = exception->firstPt(); + ExceptionThruSeq::Iterator thru_iter(exception->thrus()); + while (thru_iter.hasNext()) { + ExceptionThru *thru = thru_iter.next(); + if (thru->edges()) { + thru->disconnectPinBefore(pin, network_); + if (thru == first_pt) + recordExceptionEdges(exception, thru->edges(), + first_thru_edge_exceptions_); + } + } + } +} + +void +Sdc::clkHpinDisablesChanged(Pin *pin) +{ + if (isVertexPinClock(pin)) + clkHpinDisablesInvalid(); +} + +//////////////////////////////////////////////////////////////// + +// Annotate constraints to the timing graph. +void +Sdc::annotateGraph(bool annotate) +{ + Stats stats(debug_); + // 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(annotate); + annotateGraphOutputDelays(annotate); + annotateGraphDataChecks(annotate); + annotateHierClkLatency(annotate); + 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, true); + } + delete pin_iter; +} + +void +Sdc::annotateDisables(bool annotate) +{ + PinSet::Iterator pin_iter(disabled_pins_); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + annotateGraphDisabled(pin, annotate); + } + + Instance *top_inst = network_->topInstance(); + PortSet::Iterator port_iter(disabled_ports_); + while (port_iter.hasNext()) { + Port *port = port_iter.next(); + Pin *pin = network_->findPin(top_inst, port); + annotateGraphDisabled(pin, annotate); + } + + PinPairSet::Iterator pair_iter(disabled_wire_edges_); + while (pair_iter.hasNext()) { + PinPair *pair = pair_iter.next(); + annotateGraphDisabledWireEdge(pair->first, pair->second, annotate, graph_); + } + + EdgeSet::Iterator edge_iter(disabled_edges_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + edge->setIsDisabledConstraint(annotate); + } + + DisabledInstancePortsMap::Iterator disable_inst_iter(disabled_inst_ports_); + while (disable_inst_iter.hasNext()) { + DisabledInstancePorts *disabled_inst = disable_inst_iter.next(); + setEdgeDisabledInstPorts(disabled_inst, annotate); + } +} + +class DisableHpinEdgeVisitor : public HierPinThruVisitor +{ +public: + DisableHpinEdgeVisitor(bool annotate, Graph *graph); + virtual void visit(Pin *from_pin, + Pin *to_pin); + +protected: + bool annotate_; + Graph *graph_; + +private: + DISALLOW_COPY_AND_ASSIGN(DisableHpinEdgeVisitor); +}; + +DisableHpinEdgeVisitor::DisableHpinEdgeVisitor(bool annotate, + Graph *graph) : + HierPinThruVisitor(), + annotate_(annotate), + graph_(graph) +{ +} + +void +DisableHpinEdgeVisitor::visit(Pin *from_pin, + Pin *to_pin) +{ + annotateGraphDisabledWireEdge(from_pin, to_pin, annotate_, graph_); +} + +static void +annotateGraphDisabledWireEdge(Pin *from_pin, + Pin *to_pin, + bool annotate, + 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(annotate); + } + } +} + +void +Sdc::annotateGraphDisabled(const Pin *pin, + bool annotate) +{ + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + vertex->setIsDisabledConstraint(annotate); + if (bidirect_drvr_vertex) + bidirect_drvr_vertex->setIsDisabledConstraint(annotate); +} + +void +Sdc::setEdgeDisabledInstPorts(DisabledInstancePorts *disabled_inst, + bool annotate) +{ + setEdgeDisabledInstPorts(disabled_inst, disabled_inst->instance(), annotate); +} + +void +Sdc::setEdgeDisabledInstPorts(DisabledPorts *disabled_port, + Instance *inst, + bool annotate) +{ + 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, annotate); + } + 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, annotate); + } + + // 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(annotate); + } + } + } + } + } + + // Disable from/to pins. + LibertyPortPairSet::Iterator from_to_iter(disabled_port->fromTo()); + while (from_to_iter.hasNext()) { + LibertyPortPair *pair = from_to_iter.next(); + const LibertyPort *from_port = pair->first; + const LibertyPort *to_port = pair->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(annotate); + } + } + } + } +} + +void +Sdc::setEdgeDisabledInstFrom(Pin *from_pin, + bool disable_checks, + bool annotate) +{ + 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(annotate); + } + } + } +} + +void +Sdc::annotateGraphOutputDelays(bool annotate) +{ + OutputDelayMap::Iterator output_iter(output_delay_map_); + while (output_iter.hasNext()) { + OutputDelay *output_delay = output_iter.next(); + while (output_delay) { + OutputDelayVertexPinIterator vpin_iter(output_delay); + while (vpin_iter.hasNext()) { + Pin *vpin = vpin_iter.next(); + annotateGraphConstrained(vpin, annotate); + } + output_delay = output_delay->next(); + } + } +} + +void +Sdc::annotateGraphDataChecks(bool annotate) +{ + 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(), annotate); + } + } +} + +void +Sdc::annotateGraphConstrained(const PinSet *pins, + bool annotate) +{ + PinSet::ConstIterator pin_iter(pins); + while (pin_iter.hasNext()) { + const Pin *pin = pin_iter.next(); + annotateGraphConstrained(pin, annotate); + } +} + +void +Sdc::annotateGraphConstrained(const InstanceSet *insts, + bool annotate) +{ + InstanceSet::ConstIterator inst_iter(insts); + while (inst_iter.hasNext()) { + const Instance *inst = inst_iter.next(); + annotateGraphConstrained(inst, annotate); + } +} + +void +Sdc::annotateGraphConstrained(const Instance *inst, + bool annotate) +{ + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (network_->direction(pin)->isAnyInput()) + annotateGraphConstrained(pin, annotate); + } + delete pin_iter; +} + +void +Sdc::annotateGraphConstrained(const Pin *pin, + bool annotate) +{ + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + // Pin may be hierarchical and have no vertex. + if (vertex) + vertex->setIsConstrained(annotate); + if (bidirect_drvr_vertex) + bidirect_drvr_vertex->setIsConstrained(annotate); +} + +void +Sdc::annotateHierClkLatency(bool annotate) +{ + if (annotate) { + 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); + } + } + else + edge_clk_latency_.clear(); +} + +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; + } +} + +void +Sdc::deannotateHierClkLatency(const Pin *hpin) +{ + EdgesThruHierPinIterator edge_iter(hpin, network_, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + edge_clk_latency_.erase(edge); + } +} + +ClockLatency * +Sdc::clockLatency(Edge *edge) const +{ + return edge_clk_latency_.findKey(edge); +} + +void +Sdc::clockLatency(Edge *edge, + const TransRiseFall *tr, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const +{ + ClockLatency *latencies = edge_clk_latency_.findKey(edge); + if (latencies) + latencies->delay(tr, min_max, latency, exists); + else { + latency = 0.0; + exists = false; + } +} + +//////////////////////////////////////////////////////////////// + +InputDelayVertexPinsIterator::InputDelayVertexPinsIterator(Sdc *sdc) : + InputDelayVertexPinsIterator(sdc->input_delay_map_) +{ +} + +InputDelayVertexPinsIterator::InputDelayVertexPinsIterator(InputDelayMap + &input_delay_map) : + input_iter_(input_delay_map) +{ +} + +bool +InputDelayVertexPinsIterator::hasNext() +{ + return input_iter_.hasNext(); +} + +const Pin * +InputDelayVertexPinsIterator::next() +{ + const Pin *pin; + InputDelay *input_delay; + input_iter_.next(pin, input_delay); + return pin; +} + +//////////////////////////////////////////////////////////////// + +// Find the graph vertex pins corresponding to pin. +// If the pin is hierarchical, the vertex pins are: +// hierarchical input - load pins inside the hierarchical instance +// hierarchical output - load pins outside the hierarchical instance +void +findVertexLoadPins(Pin *pin, const Network *network, PinSet *vertex_pins) +{ + if (network->isHierarchical(pin)) { + PortDirection *dir = network->direction(pin); + bool is_input = dir->isAnyInput(); + bool is_output = dir->isAnyOutput(); + const Instance *hinst = network->instance(pin); + PinConnectedPinIterator *pin_iter = network->connectedPinIterator(pin); + while (pin_iter->hasNext()) { + Pin *pin1 = pin_iter->next(); + bool is_inside = network->isInside(pin1, hinst); + if (((is_input && is_inside) + || (is_output && !is_inside)) + && network->isLoad(pin1)) + vertex_pins->insert(pin1); + } + delete pin_iter; + } + else + vertex_pins->insert(pin); +} + +// Find the graph vertex pins corresponding to pin. +// If the pin is hierarchical, the vertex pins are: +// hierarchical input - driver pins outside the hierarchical instance +// hierarchical output - driver pins inside the hierarchical instance +void +findVertexDriverPins(Pin *pin, + const Network *network, + PinSet *vertex_pins) +{ + if (network->isHierarchical(pin)) { + PortDirection *dir = network->direction(pin); + bool is_input = dir->isAnyInput(); + bool is_output = dir->isAnyOutput(); + const Instance *hinst = network->instance(pin); + PinConnectedPinIterator *pin_iter = network->connectedPinIterator(pin); + while (pin_iter->hasNext()) { + Pin *pin1 = pin_iter->next(); + bool is_inside = network->isInside(pin1, hinst); + if (((is_input && !is_inside) + || (is_output && is_inside)) + && network->isDriver(pin1)) + vertex_pins->insert(pin1); + } + delete pin_iter; + } + else + vertex_pins->insert(pin); +} + +//////////////////////////////////////////////////////////////// + +InputDelayIterator::InputDelayIterator(Sdc *sdc) : + InputDelayIterator(sdc->input_delay_map_) +{ +} + +InputDelayIterator::InputDelayIterator(InputDelayMap &input_delay_map) : + input_iter_(input_delay_map), + next_(NULL) +{ + findNext(); +} + +bool +InputDelayIterator::hasNext() +{ + return next_ != NULL; +} + +InputDelay * +InputDelayIterator::next() +{ + InputDelay *next = next_; + findNext(); + return next; +} + +void +InputDelayIterator::findNext() +{ + if (next_) + next_ = next_->next(); + if (next_ == NULL && input_iter_.hasNext()) + next_ = input_iter_.next(); +} + +//////////////////////////////////////////////////////////////// + +OutputDelayIterator::OutputDelayIterator(Sdc *sdc) : + OutputDelayIterator(sdc->output_delay_map_) +{ +} + +OutputDelayIterator::OutputDelayIterator(OutputDelayMap &output_delay_map) : + output_iter_(output_delay_map), + next_(NULL) +{ + findNext(); +} + +bool +OutputDelayIterator::hasNext() +{ + return next_ != NULL; +} + +OutputDelay * +OutputDelayIterator::next() +{ + OutputDelay *next = next_; + findNext(); + return next; +} + +void +OutputDelayIterator::findNext() +{ + if (next_) + next_ = next_->next(); + if (next_ == NULL && output_iter_.hasNext()) + next_ = output_iter_.next(); +} + +//////////////////////////////////////////////////////////////// + +ClockIterator::ClockIterator(Sdc *sdc) : + ClockSeq::Iterator(sdc->clocks()) +{ +} + +ClockIterator::ClockIterator(ClockSeq &clocks) : + ClockSeq::Iterator(clocks) +{ +} + +//////////////////////////////////////////////////////////////// + +ClockGroupIterator::ClockGroupIterator(Sdc *sdc) : + ClockGroupsNameMap::Iterator(sdc->clk_groups_name_map_) +{ +} + +ClockGroupIterator::ClockGroupIterator(ClockGroupsNameMap &clk_groups_name_map) : + ClockGroupsNameMap::Iterator(clk_groups_name_map) +{ +} + +//////////////////////////////////////////////////////////////// + +GroupPathIterator::GroupPathIterator(Sdc *sdc) : + GroupPathIterator(sdc->group_path_map_) +{ +} + +GroupPathIterator::GroupPathIterator(GroupPathMap &group_path_map) : + GroupPathMap::Iterator(group_path_map) +{ +} + +} // namespace diff --git a/sdc/Sdc.hh b/sdc/Sdc.hh new file mode 100644 index 0000000..873b0df --- /dev/null +++ b/sdc/Sdc.hh @@ -0,0 +1,1529 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SDC_H +#define STA_SDC_H + +#include +#include "DisallowCopyAssign.hh" +#include "StringUtil.hh" +#include "StringSet.hh" +#include "Map.hh" +#include "HashSet.hh" +#include "UnorderedMap.hh" +#include "MinMax.hh" +#include "RiseFallValues.hh" +#include "StaState.hh" +#include "NetworkClass.hh" +#include "LibertyClass.hh" +#include "GraphClass.hh" +#include "SdcClass.hh" +#include "Clock.hh" +#include "DataCheck.hh" +#include "CycleAccting.hh" + +namespace sta { + +class OperatingConditions; +class PinInputDelayIterator; +class InputDelayVertexPinsIterator; +class PinOutputDelayIterator; +class PortExtCap; +class ClockGatingCheck; +class InputDriveCell; +class DisabledPorts; +class InputDelayIterator; +class OutputDelayIterator; +class GraphLoop; +class DeratingFactors; +class DeratingFactorsGlobal; +class DeratingFactorsNet; +class DeratingFactorsCell; +class PatternMatch; +class FindNetCaps; +class ClkHpinDisable; +class FindClkHpinDisables; +class Corner; +class ClockGroupIterator; +class GroupPathIterator; +class ClockVertexPinIterator; +class ClockPinIterator; +class ClockIterator; + +typedef std::pair PinClockPair; + +class ClockInsertionPinClkLess +{ +public: + bool operator()(const ClockInsertion *insert1, + const ClockInsertion *insert2) const; +}; + +class ClockLatencyPinClkLess +{ +public: + bool operator()(const ClockLatency *latency1, + const ClockLatency *latency2) const; +}; + +// This is symmetric with respect to the clocks +// in the pair so Pair(clk1, clk2) is the same +// as Pair(clk2, clk1). +class ClockPairLess +{ +public: + bool operator()(const ClockPair &pair1, + const ClockPair &pair2) const; +}; + +class PinClockPairLess +{ +public: + PinClockPairLess(const Network *network); + bool operator()(const PinClockPair &pin_clk1, + const PinClockPair &pin_clk2) const; + +protected: + const Network *network_; +}; + +class ClkHpinDisableLess +{ +public: + bool operator()(const ClkHpinDisable *disable1, + const ClkHpinDisable *disable2) const; +}; + +typedef Map ClockNameMap; +typedef UnorderedMap ClockPinMap; +typedef Map InputDelayMap; +typedef Set InputDelaySet; +typedef Map InputDelayRefPinMap; +typedef Map InputDelayInternalPinMap; +typedef Map OutputDelayMap; +// Use HashSet so no read lock is required. +typedef HashSet CycleAcctingSet; +typedef Set InstanceSet; +typedef UnorderedMap PinExceptionsMap; +typedef Map ClockExceptionsMap; +typedef Map InstanceExceptionsMap; +typedef Map 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 PortExtCapMap; +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 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; + +void +findVertexLoadPins(Pin *pin, const + Network *network, + PinSet *vertex_pins); +void +findVertexDriverPins(Pin *pin, + const Network *network, + PinSet *vertex_pins); + +class Sdc : public StaState +{ +public: + explicit Sdc(StaState *sta); + ~Sdc(); + // Note that Search may reference a Filter exception removed by clear(). + void clear(); + // Return true if pin is referenced by any constraint. + bool isConstrained(const Pin *pin) const; + // Return true if inst is referenced by any constraint. + // Does NOT include references by pins connected to the instance. + bool isConstrained(const Instance *inst) const; + // Return true if net is referenced by any constraint. + // Does NOT include references by pins connected to the net. + bool isConstrained(const Net *net) const; + // Build data structures for search. + void searchPreamble(); + + // SWIG sdc interface. + AnalysisType analysisType() { return analysis_type_; } + void setAnalysisType(AnalysisType analysis_type); + void setOperatingConditions(OperatingConditions *op_cond, + const MinMaxAll *min_max); + void setOperatingConditions(OperatingConditions *op_cond, + const MinMax *min_max); + void setTimingDerate(TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate); + // Delay type is always net for net derating. + void setTimingDerate(const Net *net, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate); + void setTimingDerate(const Instance *inst, + TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate); + void setTimingDerate(const LibertyCell *cell, + TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate); + float timingDerateInstance(const Pin *pin, + TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFall *tr, + const EarlyLate *early_late) const; + float timingDerateNet(const Pin *pin, + PathClkOrData clk_data, + const TransRiseFall *tr, + const EarlyLate *early_late) const; + void unsetTimingDerate(); + void setInputSlew(Port *port, const TransRiseFallBoth *tr, + const MinMaxAll *min_max, float slew); + // Set the rise/fall drive resistance on design port. + void setDriveResistance(Port *port, + const TransRiseFallBoth *tr, + 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(LibertyLibrary *library, + LibertyCell *cell, + Port *port, + LibertyPort *from_port, + float *from_slews, + LibertyPort *to_port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max); + void setLatchBorrowLimit(Pin *pin, + float limit); + void setLatchBorrowLimit(Instance *inst, + float limit); + void setLatchBorrowLimit(Clock *clk, + float limit); + // Return the latch borrow limit respecting precidence if multiple + // limits apply. + void latchBorrowLimit(Pin *data_pin, + Pin *enable_pin, + Clock *clk, + // Return values. + float &limit, + bool &exists); + void setMinPulseWidth(const TransRiseFallBoth *tr, + float min_width); + void setMinPulseWidth(const Pin *pin, + const TransRiseFallBoth *tr, + float min_width); + void setMinPulseWidth(const Instance *inst, + const TransRiseFallBoth *tr, + float min_width); + void setMinPulseWidth(const Clock *clk, + const TransRiseFallBoth *tr, + float min_width); + // Return min pulse with respecting precidence. + void minPulseWidth(const Pin *pin, + const Clock *clk, + const TransRiseFall *hi_low, + float &min_width, + bool &exists) const; + void setSlewLimit(Clock *clk, + const TransRiseFallBoth *tr, + const PathClkOrData clk_data, + const MinMax *min_max, + float slew); + bool haveClkSlewLimits() const; + void slewLimit(Clock *clk, + const TransRiseFall *tr, + const PathClkOrData clk_data, + const MinMax *min_max, + float &slew, + bool &exists); + void slewLimit(Port *port, + const MinMax *min_max, + float &slew, + bool &exists); + void setSlewLimit(Port *port, + const MinMax *min_max, + float slew); + void slewLimit(const Pin *pin, + const MinMax *min_max, + // Return values. + float &slew, + bool &exists); + void setSlewLimit(const Pin *pin, + const MinMax *min_max, + float slew); + void slewLimitPins(ConstPinSeq &pins); + void slewLimit(Cell *cell, + const MinMax *min_max, + float &slew, + bool &exists); + void setSlewLimit(Cell *cell, + const MinMax *min_max, + float slew); + void capacitanceLimit(Port *port, + const MinMax *min_max, + float &cap, + bool &exists); + void capacitanceLimit(Pin *pin, + const MinMax *min_max, + float &cap, + bool &exists); + void capacitanceLimit(Cell *cell, + const MinMax *min_max, + float &cap, + bool &exists); + void setCapacitanceLimit(Port *port, + const MinMax *min_max, + float cap); + void setCapacitanceLimit(Pin *pin, + const MinMax *min_max, + float cap); + void setCapacitanceLimit(Cell *cell, + const MinMax *min_max, + float cap); + void fanoutLimit(Port *port, + const MinMax *min_max, + float &fanout, + bool &exists); + void setFanoutLimit(Port *port, + const MinMax *min_max, + float fanout); + void fanoutLimit(Cell *cell, + const MinMax *min_max, + float &fanout, + bool &exists); + void setFanoutLimit(Cell *cell, + const MinMax *min_max, + float fanout); + void setMaxArea(float area); + float maxArea() const; + virtual Clock *makeClock(const char *name, + PinSet *pins, + bool add_to_pins, + float period, + FloatSeq *waveform, + const char *comment); + // edges size must be 3. + virtual Clock *makeGeneratedClock(const char *name, + PinSet *pins, + bool add_to_pins, + Pin *src_pin, + Clock *master_clk, + Pin *pll_out, + Pin *pll_fdbk, + int divide_by, + int multiply_by, + float duty_cycle, + bool invert, + bool combinational, + IntSeq *edges, + FloatSeq *edge_shifts, + const char *comment); + // Invalidate all generated clock waveforms. + void invalidateGeneratedClks() const; + virtual void removeClock(Clock *clk); + virtual void clockDeletePin(Clock *clk, + Pin *pin); + // Clock used for inputs without defined arrivals. + ClockEdge *defaultArrivalClockEdge() const; + Clock *defaultArrivalClock() const { return default_arrival_clk_; } + // Propagated (non-ideal) clocks. + void setPropagatedClock(Clock *clk); + void removePropagatedClock(Clock *clk); + void setPropagatedClock(Pin *pin); + void removePropagatedClock(Pin *pin); + bool isPropagatedClock(const Pin *pin); + void setClockSlew(Clock *clk, + const TransRiseFallBoth *tr, + 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 TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float delay); + void removeClockLatency(const Clock *clk, + const Pin *pin); + ClockLatency *clockLatency(Edge *edge) const; + bool hasClockLatency(const Pin *pin) const; + void clockLatency(Edge *edge, + const TransRiseFall *tr, + 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 TransRiseFall *tr, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const; + void clockLatency(const Clock *clk, + const TransRiseFall *tr, + const MinMax *min_max, + // Return values. + float &latency, + bool &exists) const; + float clockLatency(const Clock *clk, + const TransRiseFall *tr, + 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 TransRiseFallBoth *tr, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay); + void setClockInsertion(const Clock *clk, const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max, + const EarlyLate *early_late, + float delay); + void removeClockInsertion(const Clock *clk, + const Pin *pin); + bool hasClockInsertion(const Pin *pin) const; + float clockInsertion(const Clock *clk, + const TransRiseFall *tr, + 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 TransRiseFall *tr, + const MinMax *min_max, + const EarlyLate *early_late, + // Return values. + float &insertion, + bool &exists) const; + ClockInsertions *clockInsertions() { return clk_insertions_; } + // Clock uncertainty. + virtual void setClockUncertainty(Pin *pin, + const SetupHoldAll *setup_hold, + float uncertainty); + virtual void removeClockUncertainty(Pin *pin, + const SetupHoldAll *setup_hold); + virtual void setClockUncertainty(Clock *from_clk, + const TransRiseFallBoth *from_tr, + Clock *to_clk, + const TransRiseFallBoth *to_tr, + const SetupHoldAll *setup_hold, + float uncertainty); + virtual void removeClockUncertainty(Clock *from_clk, + const TransRiseFallBoth *from_tr, + Clock *to_clk, + const TransRiseFallBoth *to_tr, + const SetupHoldAll *setup_hold); + ClockGroups *makeClockGroups(const char *name, + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + const char *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); + bool sameClockGroup(const Clock *clk1, + const Clock *clk2); + // Clocks explicitly excluded by set_clock_group. + bool sameClockGroupExplicit(const Clock *clk1, + const Clock *clk2); + ClockGroupIterator *clockGroupIterator(); + void setClockSense(PinSet *pins, + ClockSet *clks, + ClockSense sense); + bool clkStopPropagation(const Pin *pin, + const Clock *clk) const; + bool clkStopPropagation(const Clock *clk, + const Pin *from_pin, + const TransRiseFall *from_tr, + const Pin *to_pin, + const TransRiseFall *to_tr) const; + void setClockGatingCheck(const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin); + void setClockGatingCheck(Instance *inst, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin, + LogicValue active_value); + void setClockGatingCheck(Clock *clk, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin); + void setClockGatingCheck(const Pin *pin, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin, + LogicValue active_value); + void setDataCheck(Pin *from, + const TransRiseFallBoth *from_tr, + Pin *to, + const TransRiseFallBoth *to_tr, + Clock *clk, + const SetupHoldAll *setup_hold, + float margin); + void removeDataCheck(Pin *from, + const TransRiseFallBoth *from_tr, + Pin *to, + const TransRiseFallBoth *to_tr, + Clock *clk, + const SetupHoldAll *setup_hold); + DataCheckSet *dataChecksFrom(const Pin *from) const; + DataCheckSet *dataChecksTo(const Pin *to) const; + void setInputDelay(Pin *pin, + const TransRiseFallBoth *tr, + Clock *clk, + const TransRiseFall *clk_tr, + Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, float delay); + void removeInputDelay(Pin *pin, + TransRiseFallBoth *tr, + Clock *clk, + TransRiseFall *clk_tr, + MinMaxAll *min_max); + void setOutputDelay(Pin *pin, + const TransRiseFallBoth *tr, + Clock *clk, + const TransRiseFall *clk_tr, + Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, float delay); + void removeOutputDelay(Pin *pin, + TransRiseFallBoth *tr, + Clock *clk, + TransRiseFall *clk_tr, + MinMaxAll *min_max); + // Set port external pin load (set_load -pin_load port). + void setPortExtPinCap(Port *port, + const TransRiseFall *tr, + const MinMax *min_max, + float cap); + // Set port external wire load (set_load -wire port). + void setPortExtWireCap(Port *port, + bool subtract_pin_cap, + const TransRiseFall *tr, + const Corner *corner, + const MinMax *min_max, + float cap); + // Remove all "set_load" and "set_fanout_load" annotations. + void removeLoadCaps(); + // Remove all "set_load net" annotations. + void removeNetLoadCaps(); + void setNetWireCap(Net *net, + bool subtract_pin_cap, + const Corner *corner, + const MinMax *min_max, + float cap); + bool hasNetWireCap(Net *net) const; + // True if driver pin net has wire capacitance. + bool drvrPinHasWireCap(const Pin *pin); + // 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; + // Pin capacitance derated by operating conditions and instance pvt. + float pinCapacitance(const Pin *pin, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max); + void setResistance(Net *net, + const MinMaxAll *min_max, + float res); + void resistance(Net *net, + const MinMax *min_max, + float &res, + bool &exists); + NetResistanceMap *netResistances() { return &net_res_map_; } + void setPortExtFanout(Port *port, + 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); + void removeDisable(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to); + // set_disable_timing liberty port. + // Bus and bundle ports are NOT supported. + void disable(LibertyPort *port); + void removeDisable(LibertyPort *port); + // set_disable_timing port (top level instance port). + // Bus and bundle ports are NOT supported. + void disable(Port *port); + void removeDisable(Port *port); + // set_disable_timing instance [-from] [-to]. + // 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); + void removeDisable(Instance *inst, + LibertyPort *from, + LibertyPort *to); + // set_disable_timing pin + void disable(Pin *pin); + void removeDisable(Pin *pin); + // set_disable_timing [get_timing_arc -of_objects instance]] + void disable(Edge *edge); + void removeDisable(Edge *edge); + // set_disable_timing [get_timing_arc -of_objects lib_cell]] + void disable(TimingArcSet *arc_set); + 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; + // 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(); + DisabledInstancePortsMap *disabledInstancePorts(); + PinSet *disabledPins() { return &disabled_pins_; } + PortSet *disabledPorts() { return &disabled_ports_; } + LibertyPortSet *disabledLibPorts() { return &disabled_lib_ports_; } + EdgeSet *disabledEdges() { return &disabled_edges_; } + void disableClockGatingCheck(Instance *inst); + void disableClockGatingCheck(Pin *pin); + void removeDisableClockGatingCheck(Instance *inst); + void removeDisableClockGatingCheck(Pin *pin); + bool isDisableClockGatingCheck(const Pin *pin); + bool isDisableClockGatingCheck(const Instance *inst); + // set_LogicValue::zero, set_LogicValue::one, set_logic_dc + void setLogicValue(Pin *pin, + LogicValue value); + // set_case_analysis + void setCaseAnalysis(Pin *pin, + LogicValue value); + void removeCaseAnalysis(Pin *pin); + void logicValue(const Pin *pin, + LogicValue &value, + bool &exists); + void caseLogicValue(const Pin *pin, LogicValue &value, bool &exists); + // Pin has set_case_analysis or set_logic constant value. + bool hasLogicValue(const Pin *pin); + // 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); + // 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 makeMulticyclePath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + const char *comment); + void makePathDelay(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, + float delay, + const char *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); + GroupPathIterator *groupPathIterator(); + bool isGroupPathName(const char *group_name); + 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 TransRiseFallBoth *from_tr); + // Make an exception -through specification. + ExceptionThru *makeExceptionThru(PinSet *pins, + NetSet *nets, + InstanceSet *insts, + const TransRiseFallBoth *tr); + bool exceptionToInvalid(const Pin *pin); + // Make an exception -to specification. + ExceptionTo *makeExceptionTo(PinSet *pins, + ClockSet *clks, + InstanceSet *insts, + const TransRiseFallBoth *tr, + const TransRiseFallBoth *end_tr); + FilterPath *makeFilterPath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to); + Clock *findClock(const char *name) const; + virtual void findClocksMatching(PatternMatch *pattern, + ClockSeq *clks) const; + // Wireload set by set_wire_load_model or default library default_wire_load. + Wireload *wireloadDefaulted(const MinMax *min_max); + Wireload *wireload(const MinMax *min_max); + void setWireload(Wireload *wireload, + const MinMaxAll *min_max); + WireloadMode wireloadMode(); + void setWireloadMode(WireloadMode mode); + const WireloadSelection *wireloadSelection(const MinMax *min_max); + void setWireloadSelection(WireloadSelection *selection, + const MinMaxAll *min_max); + // Common reconvergent clock pessimism. + // TCL variable sta_crpr_enabled. + bool crprEnabled() const; + void setCrprEnabled(bool enabled); + // TCL variable sta_crpr_mode. + CrprMode crprMode() const; + void setCrprMode(CrprMode mode); + // True when analysis type is on chip variation and crpr is enabled. + bool crprActive() const; + // TCL variable sta_propagate_gated_clock_enable. + // Propagate gated clock enable arrivals. + bool propagateGatedClockEnable() const; + void setPropagateGatedClockEnable(bool enable); + // TCL variable sta_preset_clear_arcs_enabled. + // Enable search through preset/clear arcs. + bool presetClrArcsEnabled() const; + void setPresetClrArcsEnabled(bool enable); + // TCL variable sta_cond_default_arcs_enabled. + // Enable/disable default arcs when conditional arcs exist. + bool condDefaultArcsEnabled() const; + void setCondDefaultArcsEnabled(bool enabled); + bool isDisabledCondDefault(Edge *edge) const; + // TCL variable sta_internal_bidirect_instance_paths_enabled. + // 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); + // TCL variable sta_gated_clock_checks_enabled. + bool gatedClkChecksEnabled() const; + void setGatedClkChecksEnabled(bool enabled); + // TCL variable sta_dynamic_loop_breaking. + bool dynamicLoopBreaking() const; + void setDynamicLoopBreaking(bool enable); + // TCL variable sta_propagate_all_clocks. + bool propagateAllClocks() const; + void setPropagateAllClocks(bool prop); + // TCL var sta_clock_through_tristate_enabled. + bool clkThruTristateEnabled() const; + void setClkThruTristateEnabled(bool enable); + // TCL variable sta_input_port_default_clock. + bool useDefaultArrivalClock(); + void setUseDefaultArrivalClock(bool enable); + + // STA interface. + InputDelay *ensureInputDelay(Pin *pin, + ClockEdge *clk_edge, + Pin *ref_pin); + InputDelaySet *refPinInputDelays(const Pin *ref_pin) const; + OutputDelay *ensureOutputDelay(Pin *pin, + ClockEdge *clk_edge, + Pin *ref_pin); + LogicValueMap *logicValues() { return &logic_value_map_; } + LogicValueMap *caseLogicValues() { return &case_value_map_; } + // Returns nullptr if set_operating_conditions has not been called. + OperatingConditions *operatingConditions(const MinMax *min_max); + // Instance specific process/voltage/temperature. + Pvt *pvt(Instance *inst, const MinMax *min_max) const; + // Pvt may be shared among multiple instances. + void setPvt(Instance *inst, + const MinMaxAll *min_max, + Pvt *pvt); + InputDrive *findInputDrive(Port *port); + // True if pin is defined as a clock source (pin may be hierarchical). + bool isClock(const Pin *pin) const; + // True if pin is a clock source vertex. + bool isVertexPinClock(const Pin *pin) const; + // True if pin is a non-generated clock source vertex. + bool isVertexPinNonGeneratedClock(const Pin *pin) const; + // Find the clocks defined for pin. + ClockSet *findClocks(const Pin *pin) const; + ClockSet *findVertexPinClocks(const Pin *pin) const; + ClockIterator *clockIterator(); + void sortedClocks(ClockSeq &clks); + ClockSeq *clocks() { return &clocks_; } + ClockSeq &clks() { return clocks_; } + bool clkDisabledByHpinThru(const Clock *clk, + const Pin *from_pin, + const Pin *to_pin); + void clkHpinDisablesInvalid(); + ClockUncertainties *clockUncertainties(const Pin *pin); + void clockUncertainty(const Pin *pin, + const SetupHold *setup_hold, + float &uncertainty, + bool &exists); + // Inter-clock uncertainty. + void clockUncertainty(const Clock *src_clk, + const TransRiseFall *src_tr, + const Clock *tgt_clk, + const TransRiseFall *tgt_tr, + const SetupHold *setup_hold, + float &uncertainty, bool &exists); + void clockGatingMarginEnablePin(const Pin *enable_pin, + const TransRiseFall *enable_tr, + const SetupHold *setup_hold, + bool &exists, float &margin); + void clockGatingMarginInstance(Instance *inst, + const TransRiseFall *enable_tr, + const SetupHold *setup_hold, + bool &exists, float &margin); + void clockGatingMarginClkPin(const Pin *clk_pin, + const TransRiseFall *enable_tr, + const SetupHold *setup_hold, + bool &exists, float &margin); + void clockGatingMarginClk(const Clock *clk, + const TransRiseFall *enable_tr, + const SetupHold *setup_hold, + bool &exists, float &margin); + void clockGatingMargin(const TransRiseFall *enable_tr, + const SetupHold *setup_hold, + bool &exists, float &margin); + // Gated clock active (non-controlling) logic value. + LogicValue clockGatingActiveValue(const Pin *clk_pin, + const Pin *enable_pin); + // 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); + // Report clock to clock relationships that exceed max_cycle_count. + void reportClkToClkMaxCycleWarnings(); + InputDelayIterator *inputDelayIterator(); + // Iterate over the graph vertex pins that have input arrivals. + // If the pin is hierarchical, the vertex pins are: + // hierarchical input - load pins inside the hierarchical instance. + // hierarchical output - load pins outside the hierarchical instance. + InputDelayVertexPinsIterator *inputDelayVertexPinsIterator(); + // Iterate over the input delays on pin (which may be hierarchical). + PinInputDelayIterator *inputDelayIterator(const Pin *pin) const; + // Iterate over the input delays on vertex_pin. + PinInputDelayIterator *inputDelayVertexIterator(const Pin *vertex_pin) const; + bool hasInputDelay(const Pin *vertex_pin) const; + // Pin is internal (not top level port) and has an input arrival. + bool isInputDelayInternal(const Pin *pin) const; + OutputDelayIterator *outputDelayIterator(); + // Iterate over the output delays on pin (which may be hierarchical). + PinOutputDelayIterator *outputDelayIterator(const Pin *pin) const; + // Iterate over the output delays on vertex_pin. + // If the pin is hierarchical, the vertex pins are: + // hierarchical input - driver pins outside the hierarchical instance. + // hierarchical output - driver pins inside the hierarchical instance. + PinOutputDelayIterator *outputDelayVertexIterator(const Pin *vertex_pin) const; + bool hasOutputDelay(const Pin *vertex_pin) const; + PortExtCap *portExtCap(Port *port) const; + bool hasPortExtCap(Port *port) const; + void portExtCap(Port *port, + const TransRiseFall *tr, + 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(Port *port, + const TransRiseFall *tr, + 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 TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_set_load) const; + void portExtFanout(Port *port, + const MinMax *min_max, + // Return values. + int &fanout, + bool &exists); + int portExtFanout(Port *port, + const MinMax *min_max); + // 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 TransRiseFall *tr, + const Clock *clk, + const TransRiseFall *clk_tr, + const MinMax *min_max, + ExceptionStateSet *&states) const; + bool exceptionFromStates(const Pin *pin, + const TransRiseFall *tr, + const Clock *clk, + const TransRiseFall *clk_tr, + const MinMax *min_max, + bool include_filter, + ExceptionStateSet *&states) const; + void exceptionFromClkStates(const Pin *pin, + const TransRiseFall *tr, + const Clock *clk, + const TransRiseFall *clk_tr, + const MinMax *min_max, + ExceptionStateSet *&states) const; + void filterRegQStates(const Pin *to_pin, + const TransRiseFall *to_tr, + 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 TransRiseFall *to_tr, + const MinMax *min_max, + ExceptionStateSet *&states) const; + // Find the highest priority exception with first exception pt at + // pin/clk end. + void exceptionTo(ExceptionPathType type, + const Pin *pin, + const TransRiseFall *tr, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority) const; + virtual bool exceptionMatchesTo(ExceptionPath *exception, + const Pin *pin, + const TransRiseFall *tr, + 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 TransRiseFall *tr, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + bool require_to_pin) const; + bool isPathDelayInternalStartpoint(const Pin *pin) const; + PinSet *pathDelayInternalStartpoints() const; + bool isPathDelayInternalEndpoint(const Pin *pin) const; + ExceptionPathSet *exceptions() { return &exceptions_; } + void deleteExceptions(); + void deleteException(ExceptionPath *exception); + void recordException(ExceptionPath *exception); + void unrecordException(ExceptionPath *exception); + // Annotate graph from constraints. If the graph exists when the + // constraints are defined it is annotated incrementally. This is + // called after building the graph to annotate any constraints that + // were defined before the graph is built. + void annotateGraph(bool annotate); + + // Network edit before/after methods. + void disconnectPinBefore(Pin *pin); + void connectPinAfter(Pin *pin); + void clkHpinDisablesChanged(Pin *pin); + void makeClkHpinDisable(Clock *clk, + Pin *drvr, + Pin *load); + void ensureClkHpinDisables(); + bool bidirectDrvrSlewFromLoad(const Pin *pin) const; + +protected: + void initVariables(); + void clearCycleAcctings(); + void removeLibertyAnnotations(); + void deleteExceptionMap(PinExceptionsMap *&exception_map); + void deleteExceptionMap(InstanceExceptionsMap *&exception_map); + void deleteExceptionMap(NetExceptionsMap *&exception_map); + void deleteExceptionMap(ClockExceptionsMap *&exception_map); + void deleteExceptionMap(EdgeExceptionsMap *&exception_map); + void deleteExceptionsReferencing(Clock *clk); + void deleteClkPinMappings(Clock *clk); + void deleteExceptionPtHashMapSets(ExceptionPathPtHash &map); + void makeClkPinMappings(Clock *clk); + virtual void deletePinClocks(Clock *defining_clk, + PinSet *pins); + void makeDefaultArrivalClock(); + InputDrive *ensureInputDrive(Port *port); + PortExtCap *ensurePortExtPinCap(Port *port); + ExceptionPath *findMergeMatch(ExceptionPath *exception); + void addException1(ExceptionPath *exception); + void addException2(ExceptionPath *exception); + void recordPathDelayInternalStartpoints(ExceptionPath *exception); + void unrecordPathDelayInternalStartpoints(ExceptionFrom *from); + bool pathDelayFrom(const Pin *pin); + virtual void recordPathDelayInternalEndpoints(ExceptionPath *exception); + virtual void unrecordPathDelayInternalEndpoints(ExceptionPath *exception); + bool pathDelayTo(const Pin *pin); + bool hasLibertyChecks(const Pin *pin); + void deleteMatchingExceptions(ExceptionPath *exception); + void findMatchingExceptions(ExceptionPath *exception, + ExceptionPathSet &matches); + void findMatchingExceptionsFirstFrom(ExceptionPath *exception, + ExceptionPathSet &matches); + void findMatchingExceptionsFirstThru(ExceptionPath *exception, + ExceptionPathSet &matches); + void findMatchingExceptionsFirstTo(ExceptionPath *exception, + ExceptionPathSet &matches); + void findMatchingExceptionsClks(ExceptionPath *exception, + ClockSet *clks, + ClockExceptionsMap *exception_map, + ExceptionPathSet &matches); + void findMatchingExceptionsPins(ExceptionPath *exception, + PinSet *pins, + PinExceptionsMap *exception_map, + ExceptionPathSet &matches); + void findMatchingExceptionsInsts(ExceptionPath *exception, + InstanceSet *insts, + InstanceExceptionsMap *exception_map, + ExceptionPathSet &matches); + void findMatchingExceptions(ExceptionPath *exception, + ExceptionPathSet *potential_matches, + ExceptionPathSet &matches); + void expandExceptionExcluding(ExceptionPath *exception, + ExceptionPath *excluding, + ExceptionPathSet &expanded_matches); + 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); + void recordExceptionInsts(ExceptionPath *exception, + InstanceSet *insts, + InstanceExceptionsMap *&exception_map); + void recordExceptionPins(ExceptionPath *exception, + PinSet *pins, + PinExceptionsMap *&exception_map); + void recordExceptionNets(ExceptionPath *exception, + NetSet *nets, + NetExceptionsMap *&exception_map); + void recordExceptionHpin(ExceptionPath *exception, + Pin *pin, + PinExceptionsMap *&exception_map); + void recordExceptionEdges(ExceptionPath *exception, + EdgePinsSet *edges, + EdgeExceptionsMap *&exception_map); + void recordMergeHash(ExceptionPath *exception, ExceptionPt *missing_pt); + void recordMergeHashes(ExceptionPath *exception); + void unrecordExceptionFirstPts(ExceptionPath *exception); + void unrecordExceptionClks(ExceptionPath *exception, + ClockSet *clks, + ClockExceptionsMap *exception_map); + void unrecordExceptionPins(ExceptionPath *exception, + PinSet *pins, + PinExceptionsMap *exception_map); + void unrecordExceptionInsts(ExceptionPath *exception, + InstanceSet *insts, + InstanceExceptionsMap *exception_map); + void unrecordExceptionEdges(ExceptionPath *exception, + EdgePinsSet *edges, + EdgeExceptionsMap *exception_map); + void unrecordExceptionNets(ExceptionPath *exception, + NetSet *nets, + NetExceptionsMap *exception_map); + void unrecordExceptionHpin(ExceptionPath *exception, + Pin *pin, + PinExceptionsMap *&exception_map); + void unrecordMergeHashes(ExceptionPath *exception); + void unrecordMergeHash(ExceptionPath *exception, + ExceptionPt *missing_pt); + void mergeException(ExceptionPath *exception); + void expandException(ExceptionPath *exception, + ExceptionPathSet &expansions); + bool exceptionFromStates(const ExceptionPathSet *exceptions, + const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max, + bool include_filter, + ExceptionStateSet *&states) const; + void exceptionThruStates(const ExceptionPathSet *exceptions, + const TransRiseFall *to_tr, + const MinMax *min_max, + // Return value. + ExceptionStateSet *&states) const; + void exceptionTo(const ExceptionPathSet *to_exceptions, + ExceptionPathType type, + const Pin *pin, + const TransRiseFall *tr, + 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 TransRiseFall *tr, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + // Return values. + ExceptionPath *&hi_priority_exception, + int &hi_priority) const; + void makeLoopPath(ExceptionThruSeq *thrus); + void makeLoopException(Pin *loop_input_pin, + Pin *loop_pin, + Pin *loop_prev_pin); + void makeLoopExceptionThru(Pin *pin, + ExceptionThruSeq *thrus); + void deleteLoopExceptions(); + void deleteConstraints(); + InputDelay *findInputDelay(const Pin *pin, + ClockEdge *clk_edge, + Pin *ref_pin); + void deleteInputDelays(Pin *pin, + InputDelay *except); + void deleteInputDelaysReferencing(Clock *clk); + void deleteInputDelay(InputDelay *input_delay); + OutputDelay *findOutputDelay(const Pin *pin, + ClockEdge *clk_edge, + Pin *ref_pin); + void deleteOutputDelays(Pin *pin, OutputDelay *except); + void deleteOutputDelaysReferencing(Clock *clk); + void deleteOutputDelay(OutputDelay *output_delay); + void deleteInstancePvts(); + void deleteClockInsertion(ClockInsertion *insertion); + void deleteClockInsertionsReferencing(Clock *clk); + void deleteInterClockUncertainty(InterClockUncertainty *uncertainties); + void deleteInterClockUncertaintiesReferencing(Clock *clk); + void deleteLatchBorrowLimitsReferencing(Clock *clk); + void deleteMinPulseWidthReferencing(Clock *clk); + void deleteMasterClkRefs(Clock *clk); + // Liberty library to look for defaults. + LibertyLibrary *defaultLibertyLibrary(); + void annotateGraphConstrainOutputs(); + void annotateDisables(bool annotate); + void annotateGraphDisabled(const Pin *pin, + bool annotate); + void setEdgeDisabledInstPorts(DisabledInstancePorts *disabled_inst, + bool annotate); + void setEdgeDisabledInstFrom(Pin *from_pin, + bool disable_checks, + bool annotate); + void setEdgeDisabledInstPorts(DisabledPorts *disabled_port, + Instance *inst, + bool annotate); + void deleteClockLatenciesReferencing(Clock *clk); + void deleteClockLatency(ClockLatency *latency); + void deleteDeratingFactors(); + void annotateGraphOutputDelays(bool annotate); + void annotateGraphDataChecks(bool annotate); + void annotateGraphConstrained(const PinSet *pins, + bool annotate); + void annotateGraphConstrained(const InstanceSet *insts, + bool annotate); + void annotateGraphConstrained(const Instance *inst, + bool annotate); + void annotateGraphConstrained(const Pin *pin, + bool annotate); + void annotateHierClkLatency(bool annotate); + void annotateHierClkLatency(const Pin *hpin, + ClockLatency *latency); + void deannotateHierClkLatency(const Pin *hpin); + void initInstancePvtMaps(); + void pinCaps(const Pin *pin, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_ext_cap) const; + void netCaps(const Pin *drvr_pin, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_set_load) const; + // connectedCap pin_cap. + float connectedPinCap(const Pin *pin, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max); + float portCapacitance(Instance *inst, LibertyPort *port, + const TransRiseFall *tr, + const OperatingConditions *op_cond, + const Corner *corner, + const MinMax *min_max) const; + void removeClockGroups(ClockGroups *groups); + void ensureClkGroupExclusions(); + void makeClkGroupExclusions(ClockGroups *clk_groups); + void makeClkGroupExclusions1(ClockGroupSet *groups); + void makeClkGroupExclusions(ClockGroupSet *groups); + void makeClkGroupSame(ClockGroup *group); + void clearClkGroupExclusions(); + char *makeClockGroupsName(); + void setClockSense(const Pin *pin, + const Clock *clk, + ClockSense sense); + bool clkStopSense(const Pin *to_pin, + const Clock *clk, + const TransRiseFall *from_tr, + const TransRiseFall *to_tr) const; + void disconnectPinBefore(Pin *pin, + ExceptionPathSet *exceptions); + void clockGroupsDeleteClkRefs(Clock *clk); + void makeVertexClkHpinDisables(Clock *clk, + Vertex *vertex, + FindClkHpinDisables &visitor); + void clearGroupPathMap(); + + AnalysisType analysis_type_; + OperatingConditions *operating_conditions_[MinMax::index_count]; + InstancePvtMap *instance_pvt_maps_[MinMax::index_count]; + DeratingFactorsGlobal *derating_factors_; + NetDeratingFactorsMap *net_derating_factors_; + InstDeratingFactorsMap *inst_derating_factors_; + CellDeratingFactorsMap *cell_derating_factors_; + // Clock sequence retains clock definition order. + // This is important for getting consistent regression results, + // which iterating over the name map can't provide. + ClockSeq clocks_; + // Clocks are assigned an index. + int clk_index_; + // Default clock used for unclocked input arrivals. + Clock *default_arrival_clk_; + bool use_default_arrival_clock_; + ClockNameMap clock_name_map_; + ClockPinMap clock_pin_map_; + // Clocks on hierarchical pins are indexed by the load pins. + ClockPinMap clock_vertex_pin_map_; + ClkHpinDisables clk_hpin_disables_; + bool clk_hpin_disables_valid_; + PinSet propagated_clk_pins_; + ClockLatencies clk_latencies_; + ClockInsertions *clk_insertions_; + PinClockUncertaintyMap pin_clk_uncertainty_map_; + InterClockUncertaintySet inter_clk_uncertainties_; + // clk_groups name -> clk_groups + ClockGroupsNameMap clk_groups_name_map_; + // clk to clk paths excluded by clock groups. + ClockPairSet *clk_group_exclusions_; + // clks in the same set_clock_group set. + ClockPairSet *clk_group_same_; + ClockSenseMap clk_sense_map_; + ClockGatingCheck *clk_gating_check_; + ClockGatingCheckMap clk_gating_check_map_; + InstanceClockGatingCheckMap inst_clk_gating_check_map_; + PinClockGatingCheckMap pin_clk_gating_check_map_; + CycleAcctingSet cycle_acctings_; + std::mutex cycle_acctings_lock_; + DataChecksMap data_checks_from_map_; + DataChecksMap data_checks_to_map_; + InputDelayMap input_delay_map_; + int input_delay_index_; + InputDelayRefPinMap input_delay_ref_pin_map_; + // Input delays on hierarchical pins are indexed by the load pins. + InputDelayMap input_delay_vertex_map_; + InputDelayInternalPinMap input_delay_internal_pin_map_; + OutputDelayMap output_delay_map_; + // Output delays on hierarchical pins are indexed by the load pins. + OutputDelayMap output_delay_vertex_map_; + PortSlewLimitMap port_slew_limit_map_; + PinSlewLimitMap pin_slew_limit_map_; + CellSlewLimitMap cell_slew_limit_map_; + bool have_clk_slew_limits_; + CellCapLimitMap cell_cap_limit_map_; + PortCapLimitMap port_cap_limit_map_; + PinCapLimitMap pin_cap_limit_map_; + PortFanoutLimitMap port_fanout_limit_map_; + CellFanoutLimitMap cell_fanout_limit_map_; + // External parasitics on top level ports. + // set_load port + // set_fanout_load port + // Indexed by corner_index. + PortExtCapMap *port_cap_map_; + // set_load net + // Indexed by corner_index. + NetWireCapMap *net_wire_cap_map_; + // Indexed by corner_index. + PinWireCapMap *drvr_pin_wire_cap_map_; + NetResistanceMap net_res_map_; + PinSet disabled_pins_; + PortSet disabled_ports_; + LibertyPortSet disabled_lib_ports_; + PinPairSet disabled_wire_edges_; + EdgeSet disabled_edges_; + DisabledCellPortsMap disabled_cell_ports_; + DisabledInstancePortsMap disabled_inst_ports_; + InstanceSet disabled_clk_gating_checks_inst_; + PinSet disabled_clk_gating_checks_pin_; + ExceptionPathSet exceptions_; + + // First pin/clock/instance/net/edge exception point to exception set map. + PinExceptionsMap *first_from_pin_exceptions_; + ClockExceptionsMap *first_from_clk_exceptions_; + InstanceExceptionsMap *first_from_inst_exceptions_; + PinExceptionsMap *first_thru_pin_exceptions_; + InstanceExceptionsMap *first_thru_inst_exceptions_; + // Nets that have exception -thru nets. + NetExceptionsMap *first_thru_net_exceptions_; + PinExceptionsMap *first_to_pin_exceptions_; + ClockExceptionsMap *first_to_clk_exceptions_; + InstanceExceptionsMap *first_to_inst_exceptions_; + // Edges that traverse hierarchical exception pins. + EdgeExceptionsMap *first_thru_edge_exceptions_; + + // Exception hash with one missing from/thru/to point, used for merging. + ExceptionPathPtHash exception_merge_hash_; + // Path delay -from pin internal startpoints. + PinSet *path_delay_internal_startpoints_; + // Path delay -to pin internal endpoints. + PinSet *path_delay_internal_endpoints_; + // There is a path delay exception without a -to. + bool path_delays_without_to_; + // Group path exception names. + GroupPathMap group_path_map_; + InputDriveMap input_drive_map_; + // set_LogicValue::one/zero/dc + LogicValueMap logic_value_map_; + // set_case_analysis + LogicValueMap case_value_map_; + PinLatchBorrowLimitMap pin_latch_borrow_limit_map_; + InstLatchBorrowLimitMap inst_latch_borrow_limit_map_; + ClockLatchBorrowLimitMap clk_latch_borrow_limit_map_; + RiseFallValues min_pulse_width_; + PinMinPulseWidthMap pin_min_pulse_width_map_; + InstMinPulseWidthMap inst_min_pulse_width_map_; + ClockMinPulseWidthMap clk_min_pulse_width_map_; + float max_area_; + Wireload *wireload_[MinMax::index_count]; + WireloadMode wireload_mode_; + WireloadSelection *wireload_selection_[MinMax::index_count]; + bool crpr_enabled_; + CrprMode crpr_mode_; + bool pocv_enabled_; + 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_; + + // Annotations on graph objects that are stored in constraints + // rather on the graph itself. + EdgeClockLatencyMap edge_clk_latency_; + +private: + DISALLOW_COPY_AND_ASSIGN(Sdc); + + friend class WriteSdc; + friend class InputDelayIterator; + friend class OutputDelayIterator; + friend class FindNetCaps; + friend class ClockGroupIterator; + friend class GroupPathIterator; + friend class InputDelayVertexPinsIterator; + friend class PinInputDelayIterator; + friend class VertexPinInputDelayIterator; + friend class PinOutputDelayIterator; + friend class VertexPinOutputDelayIterator; +}; + +class InputDelayVertexPinsIterator : public Iterator +{ +public: + InputDelayVertexPinsIterator(Sdc *sdc); + virtual bool hasNext(); + virtual const Pin *next(); + +private: + InputDelayVertexPinsIterator(InputDelayMap &input_delay_map); + InputDelayMap::ConstIterator input_iter_; + + friend class Sdc; + DISALLOW_COPY_AND_ASSIGN(InputDelayVertexPinsIterator); +}; + +class InputDelayIterator : public Iterator +{ +public: + InputDelayIterator(Sdc *sdc); + virtual bool hasNext(); + virtual InputDelay *next(); + +private: + InputDelayIterator(InputDelayMap &input_delay_map); + void findNext(); + + InputDelayMap::Iterator input_iter_; + InputDelay *next_; + + friend class Sdc; + DISALLOW_COPY_AND_ASSIGN(InputDelayIterator); +}; + +class OutputDelayIterator : public OutputDelayMap::Iterator +{ +public: + OutputDelayIterator(Sdc *sdc); + virtual bool hasNext(); + virtual OutputDelay *next(); + +private: + OutputDelayIterator(OutputDelayMap &output_delay_map); + void findNext(); + + OutputDelayMap::Iterator output_iter_; + OutputDelay *next_; + + friend class Sdc; + DISALLOW_COPY_AND_ASSIGN(OutputDelayIterator); +}; + +class ClockIterator : public ClockSeq::Iterator +{ +public: + ClockIterator(Sdc *sdc); + +private: + ClockIterator(ClockSeq &clocks); + friend class Sdc; + DISALLOW_COPY_AND_ASSIGN(ClockIterator); +}; + +class ClockGroupIterator : public ClockGroupsNameMap::Iterator +{ +public: + ClockGroupIterator(Sdc *sdc); + +private: + ClockGroupIterator(ClockGroupsNameMap &clk_groups_name_map); + + friend class Sdc; + DISALLOW_COPY_AND_ASSIGN(ClockGroupIterator); +}; + +class GroupPathIterator : public GroupPathMap::Iterator +{ +public: + GroupPathIterator(Sdc *sdc); + +private: + GroupPathIterator(GroupPathMap &group_path_map); + + friend class Sdc; + DISALLOW_COPY_AND_ASSIGN(GroupPathIterator); +}; + +} // namespace +#endif diff --git a/sdc/SdcClass.hh b/sdc/SdcClass.hh new file mode 100644 index 0000000..bfba3b6 --- /dev/null +++ b/sdc/SdcClass.hh @@ -0,0 +1,105 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SDC_CLASS_H +#define STA_SDC_CLASS_H + +#include "Map.hh" +#include "Set.hh" +#include "Vector.hh" +#include "LibertyClass.hh" +#include "NetworkClass.hh" +#include "MinMaxValues.hh" +#include "PinPair.hh" + +namespace sta { + +class Sdc; +class Clock; +class ClockEdge; +class CycleAccting; +class CycleAcctingLess; +class InputDelay; +class OutputDelay; +class FalsePath; +class PathDelay; +class MultiCyclePath; +class FilterPath; +class GroupPath; +class ExceptionFromTo; +class ExceptionFrom; +class ExceptionThru; +class ExceptionTo; +class ExceptionPt; +class InputDrive; +class MinMax; +class MinMaxAll; +class RiseFallMinMax; +class DisabledInstancePorts; +class DisabledCellPorts; +class ExceptionPath; +class DataCheck; +class Wireload; +class ClockLatency; +class ClockInsertion; +class ClockGroup; +class ClockGroups; + +enum class AnalysisType { single, bc_wc, ocv }; + +enum class ExceptionPathType { false_path, loop, multi_cycle, path_delay, + group_path, filter, any}; + +enum class ClockSense { positive, negative, stop }; + +typedef std::pair ClockPair; + +typedef Vector FloatSeq; +typedef Vector IntSeq; +typedef Vector ClockSeq; +typedef Set ClockSet; +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 Set ExceptionPathSet; +typedef PinPair EdgePins; +typedef PinPairSet EdgePinsSet; +typedef Map LogicValueMap; +typedef Set ClockGroupSet; + +// For Search. +class ExceptionState; +class ExceptionPath; +typedef Set ExceptionStateSet; + +enum class CrprMode { same_pin, same_transition }; + +// Constraint applies to clock or data paths. +enum class PathClkOrData { clk, data }; + +const int path_clk_or_data_count = 2; + +enum class TimingDerateType { cell_delay, cell_check, net_delay }; +const int timing_derate_type_count = int(TimingDerateType::net_delay) + 1 ; +const int timing_derate_cell_type_count = 2; + +} // namespace +#endif diff --git a/sdc/SdcCmdComment.cc b/sdc/SdcCmdComment.cc new file mode 100644 index 0000000..b6651bb --- /dev/null +++ b/sdc/SdcCmdComment.cc @@ -0,0 +1,49 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "StringUtil.hh" +#include "SdcCmdComment.hh" + +namespace sta { + +SdcCmdComment::SdcCmdComment() : + comment_(nullptr) +{ +} + +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) +{ + if (comment_) + stringDelete(comment_); + comment_ = nullptr; + if (comment && comment[0] != '\0') + comment_ = stringCopy(comment); +} + +} // namespace diff --git a/sdc/SdcCmdComment.hh b/sdc/SdcCmdComment.hh new file mode 100644 index 0000000..65a39ed --- /dev/null +++ b/sdc/SdcCmdComment.hh @@ -0,0 +1,39 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SDC_CMD_COMMENT_H +#define STA_SDC_CMD_COMMENT_H + +namespace sta { + +class SdcCmdComment +{ +public: + SdcCmdComment(); + SdcCmdComment(const char *comment); + const char *comment() { return comment_; } + void setComment(const char *comment); + +protected: + // Destructor is protected to prevent deletion of a derived + // class with a pointer to this base class. + ~SdcCmdComment(); + + char *comment_; +}; + +} // namespace +#endif diff --git a/sdc/WriteSdc.cc b/sdc/WriteSdc.cc new file mode 100644 index 0000000..7a0ee20 --- /dev/null +++ b/sdc/WriteSdc.cc @@ -0,0 +1,2974 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "Report.hh" +#include "Error.hh" +#include "Units.hh" +#include "StaState.hh" +#include "PortDirection.hh" +#include "RiseFallValues.hh" +#include "Transition.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "NetworkCmp.hh" +#include "PortDelay.hh" +#include "ExceptionPath.hh" +#include "PortExtCap.hh" +#include "DisabledPorts.hh" +#include "Wireload.hh" +#include "ClockGroups.hh" +#include "ClockInsertion.hh" +#include "ClockLatency.hh" +#include "InputDrive.hh" +#include "DataCheck.hh" +#include "DeratingFactors.hh" +#include "Sdc.hh" +#include "Graph.hh" +#include "GraphCmp.hh" +#include "WriteSdc.hh" +#include "WriteSdcPvt.hh" + +namespace sta { + +typedef Set ClockSenseSet; +typedef Vector ClockSenseSeq; + +static const char * +transRiseFallFlag(const TransRiseFall *tr); +static const char * +transRiseFallFlag(const TransRiseFallBoth *tr); +static const char * +minMaxFlag(const MinMaxAll *min_max); +static const char * +minMaxFlag(const MinMax *min_max); +static const char * +earlyLateFlag(const MinMax *early_late); +static const char * +setupHoldFlag(const MinMax *min_max); +static const char * +timingDerateTypeKeyword(TimingDerateType type); + +//////////////////////////////////////////////////////////////// + +class WriteSdcObject +{ +public: + WriteSdcObject() {} + virtual ~WriteSdcObject() {} + virtual void write() const = 0; + +private: + DISALLOW_COPY_AND_ASSIGN(WriteSdcObject); +}; + +class WriteGetPort : public WriteSdcObject +{ +public: + WriteGetPort(const Port *port, + const WriteSdc *writer); + virtual void write() const; + +private: + DISALLOW_COPY_AND_ASSIGN(WriteGetPort); + + const Port *port_; + const WriteSdc *writer_; +}; + +WriteGetPort::WriteGetPort(const Port *port, + const WriteSdc *writer) : + port_(port), + writer_(writer) +{ +} + +void +WriteGetPort::write() const +{ + writer_->writeGetPort(port_); +} + +class WriteGetPinAndClkKey : public WriteSdcObject +{ +public: + WriteGetPinAndClkKey(const Pin *pin, + const Clock *clk, + const WriteSdc *writer); + virtual void write() const; + +private: + DISALLOW_COPY_AND_ASSIGN(WriteGetPinAndClkKey); + + const Pin *pin_; + const Clock *clk_; + const WriteSdc *writer_; +}; + +WriteGetPinAndClkKey::WriteGetPinAndClkKey(const Pin *pin, + const Clock *clk, + const WriteSdc *writer) : + pin_(pin), + clk_(clk), + writer_(writer) +{ +} + +void +WriteGetPinAndClkKey::write() const +{ + writer_->writeClockKey(clk_); + fprintf(writer_->stream(), " "); + writer_->writeGetPin(pin_); +} + +class WriteGetPin : public WriteSdcObject +{ +public: + WriteGetPin(const Pin *pin, + const WriteSdc *writer); + virtual void write() const; + +private: + DISALLOW_COPY_AND_ASSIGN(WriteGetPin); + + const Pin *pin_; + const WriteSdc *writer_; +}; + +WriteGetPin::WriteGetPin(const Pin *pin, + const WriteSdc *writer) : + pin_(pin), + writer_(writer) +{ +} + +void +WriteGetPin::write() const +{ + writer_->writeGetPin(pin_); +} + +class WriteGetNet : public WriteSdcObject +{ +public: + WriteGetNet(const Net *net, + const WriteSdc *writer); + virtual void write() const; + +private: + DISALLOW_COPY_AND_ASSIGN(WriteGetNet); + + const Net *net_; + const WriteSdc *writer_; +}; + +WriteGetNet::WriteGetNet(const Net *net, + const WriteSdc *writer) : + net_(net), + writer_(writer) +{ +} + +void +WriteGetNet::write() const +{ + writer_->writeGetNet(net_); +} + +class WriteGetInstance : public WriteSdcObject +{ +public: + WriteGetInstance(const Instance *inst, + const WriteSdc *writer); + virtual void write() const; + +private: + DISALLOW_COPY_AND_ASSIGN(WriteGetInstance); + + const Instance *inst_; + const WriteSdc *writer_; +}; + +WriteGetInstance::WriteGetInstance(const Instance *inst, + const WriteSdc *writer) : + inst_(inst), + writer_(writer) +{ +} + +void +WriteGetInstance::write() const +{ + writer_->writeGetInstance(inst_); +} + +class WriteGetLibCell : public WriteSdcObject +{ +public: + WriteGetLibCell(const LibertyCell *cell, + const WriteSdc *writer); + virtual void write() const; + +private: + DISALLOW_COPY_AND_ASSIGN(WriteGetLibCell); + + const LibertyCell *cell_; + const WriteSdc *writer_; +}; + +WriteGetLibCell::WriteGetLibCell(const LibertyCell *cell, + const WriteSdc *writer) : + cell_(cell), + writer_(writer) +{ +} + +void +WriteGetLibCell::write() const +{ + writer_->writeGetLibCell(cell_); +} + +class WriteGetClock : public WriteSdcObject +{ +public: + WriteGetClock(const Clock *clk, + const WriteSdc *writer); + virtual void write() const; + +private: + DISALLOW_COPY_AND_ASSIGN(WriteGetClock); + + const Clock *clk_; + const WriteSdc *writer_; +}; + +WriteGetClock::WriteGetClock(const Clock *clk, + const WriteSdc *writer) : + clk_(clk), + writer_(writer) +{ +} + +void +WriteGetClock::write() const +{ + writer_->writeGetClock(clk_); +} + +//////////////////////////////////////////////////////////////// + +void +writeSdc(Instance *instance, + const char *filename, + const char *creator, + bool compatible, + bool no_timestamp, + int digits, + Sdc *sdc) +{ + WriteSdc writer(instance, filename, creator, compatible, digits, + no_timestamp, sdc); + writer.write(); +} + +WriteSdc::WriteSdc(Instance *instance, + const char *filename, + const char *creator, + bool compatible, + int digits, + bool no_timestamp, + Sdc *sdc) : + StaState(sdc), + instance_(instance), + filename_(filename), + creator_(creator), + compatible_(compatible), + digits_(digits), + no_timestamp_(no_timestamp), + top_instance_(instance == sdc_network_->topInstance()), + instance_name_length_(strlen(sdc_network_->pathName(instance))), + cell_(sdc_network_->cell(instance)) +{ +} + +WriteSdc::~WriteSdc() +{ +} + +void +WriteSdc::write() +{ + openFile(filename_); + writeHeader(); + writeTiming(); + writeEnvironment(); + writeDesignRules(); + writeVariables(); + closeFile(); +} + +void +WriteSdc::openFile(const char *filename) +{ + stream_ = fopen(filename, "w"); + if (stream_ == nullptr) + throw FileNotWritable(filename); +} + +void +WriteSdc::closeFile() +{ + fclose(stream_); +} + +void +WriteSdc::flush() +{ + fflush(stream_); +} + +void +WriteSdc::writeHeader() const +{ + writeCommentSeparator(); + fprintf(stream_, "# Created by %s\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'; + fprintf(stream_, "# %s\n", time_str); + } + writeCommentSeparator(); + + fprintf(stream_, "current_design %s\n", sdc_network_->name(cell_)); +} + +//////////////////////////////////////////////////////////////// + +void +WriteSdc::writeTiming() const +{ + writeCommentSection("Timing Constraints"); + writeClocks(); + writePropagatedClkPins(); + writeClockUncertaintyPins(); + writeClockLatencies(); + writeClockInsertions(); + writeInterClockUncertainties(); + writeClockSenses(); + writeClockGroups(); + writeInputDelays(); + writeOutputDelays(); + writeDisables(); + writeExceptions(); + writeDataChecks(); +} + +void +WriteSdc::writeClocks() const +{ + // Write clocks in the order they were defined because generated + // clocks depend on master clocks having been previously defined. + for (auto clk : sdc_->clocks_) { + if (clk->isGenerated()) + writeGeneratedClock(clk); + else + writeClock(clk); + writeClockSlews(clk); + writeClockUncertainty(clk); + if (clk->isPropagated()) { + fprintf(stream_, "set_propagated_clock "); + writeGetClock(clk); + fprintf(stream_, "\n"); + } + } +} + +void +WriteSdc::writeClock(Clock *clk) const +{ + fprintf(stream_, "create_clock -name %s", + clk->name()); + if (clk->addToPins()) + fprintf(stream_, " -add"); + fprintf(stream_, " -period "); + writeTime(clk->period()); + fprintf(stream_, " -waveform "); + writeFloatSeq(clk->waveform(), scaleTime(1.0)); + writeCmdComment(clk); + fprintf(stream_, " "); + writeClockPins(clk); + fprintf(stream_, "\n"); +} + +void +WriteSdc::writeGeneratedClock(Clock *clk) const +{ + fprintf(stream_, "create_generated_clock -name %s", + clk->name()); + if (clk->addToPins()) + fprintf(stream_, " -add"); + fprintf(stream_, " -source "); + writeGetPin(clk->srcPin()); + Clock *master = clk->masterClk(); + if (master && !clk->masterClkInfered()) { + fprintf(stream_, " -master_clock "); + writeGetClock(master); + } + Pin *pll_out = clk->pllOut(); + if (pll_out) { + fprintf(stream_, " -pll_out "); + writeGetPin(pll_out); + } + Pin *pll_fdbk = clk->pllFdbk(); + if (pll_fdbk) { + fprintf(stream_, " -pll_feedback "); + writeGetPin(pll_fdbk); + } + if (clk->combinational()) + fprintf(stream_, " -combinational"); + int divide_by = clk->divideBy(); + if (divide_by != 0) + fprintf(stream_, " -divide_by %d", divide_by); + int multiply_by = clk->multiplyBy(); + if (multiply_by != 0) + fprintf(stream_, " -multiply_by %d", multiply_by); + float duty_cycle = clk->dutyCycle(); + if (duty_cycle != 0.0) { + fprintf(stream_, " -duty_cycle "); + writeFloat(duty_cycle); + } + if (clk->invert()) + fprintf(stream_, " -invert"); + IntSeq *edges = clk->edges(); + if (edges && !edges->empty()) { + fprintf(stream_, " -edges "); + writeIntSeq(edges); + FloatSeq *edge_shifts = clk->edgeShifts(); + if (edge_shifts && !edge_shifts->empty()) { + fprintf(stream_, " -edge_shift "); + writeFloatSeq(edge_shifts, scaleTime(1.0)); + } + } + writeCmdComment(clk); + fprintf(stream_, " "); + writeClockPins(clk); + fprintf(stream_, "\n"); +} + +void +WriteSdc::writeClockPins(Clock *clk) const +{ + // Sort pins. + PinSeq pins; + ClockPinIterator pin_iter(clk); + while (pin_iter.hasNext()) + pins.push_back(pin_iter.next()); + + if (!pins.empty()) { + sort(pins, PinPathNameLess(sdc_network_)); + if (pins.size() > 1) + fprintf(stream_, "\\\n "); + writeGetPins(&pins); + } +} + +void +WriteSdc::writeClockSlews(Clock *clk) const +{ + WriteGetClock write_clk(clk, this); + RiseFallMinMax *slews = clk->slews(); + if (slews->hasValue()) + writeRiseFallMinMaxTimeCmd("set_clock_transition", slews, write_clk); +} + +void +WriteSdc::writeClockUncertainty(Clock *clk) const +{ + float setup; + bool setup_exists; + clk->uncertainty(SetupHold::max(), setup, setup_exists); + float hold; + bool hold_exists; + clk->uncertainty(SetupHold::min(), hold, hold_exists); + if (setup_exists && hold_exists && setup == hold) + writeClockUncertainty(clk, "", setup); + else { + if (setup_exists) + writeClockUncertainty(clk, "-setup ", setup); + if (hold_exists) + writeClockUncertainty(clk, "-hold ", hold); + } +} + +void +WriteSdc::writeClockUncertainty(Clock *clk, + const char *setup_hold, + float value) const +{ + fprintf(stream_, "set_clock_uncertainty %s", setup_hold); + writeTime(value); + fprintf(stream_, " %s\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); + writeClockUncertaintyPin(pin, uncertainties); + } +} + +void +WriteSdc::writeClockUncertaintyPin(const Pin *pin, + ClockUncertainties *uncertainties) + const +{ + float setup; + bool setup_exists; + uncertainties->value(SetupHold::max(), setup, setup_exists); + float hold; + bool hold_exists; + uncertainties->value(SetupHold::min(), hold, hold_exists); + if (setup_exists && hold_exists && setup == hold) + writeClockUncertaintyPin(pin, "", setup); + else { + if (setup_exists) + writeClockUncertaintyPin(pin, "-setup ", setup); + if (hold_exists) + writeClockUncertaintyPin(pin, "-hold ", hold); + } +} +void +WriteSdc::writeClockUncertaintyPin(const Pin *pin, + const char *setup_hold, + float value) const +{ + fprintf(stream_, "set_clock_uncertainty %s", setup_hold); + writeTime(value); + fprintf(stream_, " "); + writeGetPin(pin); + fprintf(stream_, "\n"); +} + +void +WriteSdc::writeClockLatencies() const +{ + ClockLatencies::Iterator latency_iter(sdc_->clockLatencies()); + while (latency_iter.hasNext()) { + ClockLatency *latency = latency_iter.next(); + const Pin *pin = latency->pin(); + const Clock *clk = latency->clock(); + if (pin && clk) { + WriteGetPinAndClkKey write_pin(pin, clk, this); + writeRiseFallMinMaxTimeCmd("set_clock_latency", latency->delays(), + write_pin); + } + else if (pin) { + WriteGetPin write_pin(pin, this); + writeRiseFallMinMaxTimeCmd("set_clock_latency", latency->delays(), + write_pin); + } + else if (clk) { + WriteGetClock write_clk(clk, this); + writeRiseFallMinMaxTimeCmd("set_clock_latency", latency->delays(), + write_clk); + } + } +} + +void +WriteSdc::writeClockInsertions() const +{ + ClockInsertions::Iterator insert_iter(sdc_->clockInsertions()); + while (insert_iter.hasNext()) { + ClockInsertion *insert = insert_iter.next(); + const Pin *pin = insert->pin(); + const Clock *clk = insert->clock(); + if (pin && clk) { + WriteGetPinAndClkKey write_pin_clk(pin, clk, this); + writeClockInsertion(insert, write_pin_clk); + } + else if (pin) { + WriteGetPin write_pin(pin, this); + writeClockInsertion(insert, write_pin); + } + else if (clk) { + WriteGetClock write_clk(clk, this); + writeClockInsertion(insert, write_clk); + } + } +} + +void +WriteSdc::writeClockInsertion(ClockInsertion *insert, + 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); + else { + writeRiseFallMinMaxTimeCmd("set_clock_latency -source -early", + early_values, write_obj); + writeRiseFallMinMaxTimeCmd("set_clock_latency -source -late", + 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(); + fprintf(stream_, "set_propagated_clock "); + writeGetPin(pin); + fprintf(stream_, "\n"); + } +} + +void +WriteSdc::writeInterClockUncertainties() const +{ + InterClockUncertaintySet::Iterator + uncertainty_iter(sdc_->inter_clk_uncertainties_); + while (uncertainty_iter.hasNext()) { + InterClockUncertainty *uncertainty = uncertainty_iter.next(); + writeInterClockUncertainty(uncertainty); + } +} + +void +WriteSdc:: +writeInterClockUncertainty(InterClockUncertainty *uncertainty) const +{ + const Clock *src_clk = uncertainty->src(); + const Clock *tgt_clk = uncertainty->target(); + const RiseFallMinMax *src_rise = + uncertainty->uncertainties(TransRiseFall::rise()); + const RiseFallMinMax *src_fall = + uncertainty->uncertainties(TransRiseFall::fall()); + float value; + if (src_rise->equal(src_fall) + && src_rise->isOneValue(value)) { + fprintf(stream_, "set_clock_uncertainty -from "); + writeGetClock(src_clk); + fprintf(stream_, " -to "); + writeGetClock(tgt_clk); + fprintf(stream_, " "); + writeTime(value); + fprintf(stream_, "\n"); + } + else { + for (auto src_tr : TransRiseFall::range()) { + for (auto tgt_tr : TransRiseFall::range()) { + for (auto setup_hold : SetupHold::range()) { + float value; + bool exists; + sdc_->clockUncertainty(src_clk, src_tr, tgt_clk, tgt_tr, + setup_hold, value, exists); + if (exists) { + fprintf(stream_, "set_clock_uncertainty -%s_from ", + src_tr == TransRiseFall::rise() ? "rise" : "fall"); + writeGetClock(uncertainty->src()); + fprintf(stream_, " -%s_to ", + tgt_tr == TransRiseFall::rise() ? "rise" : "fall"); + writeGetClock(uncertainty->target()); + fprintf(stream_, " %s ", + setupHoldFlag(setup_hold)); + writeTime(value); + fprintf(stream_, "\n"); + } + } + } + } + } +} + +void +WriteSdc::writeInputDelays() const +{ + // Sort arrivals by pin and clock name. + PortDelaySeq delays; + InputDelayIterator input_iter(sdc_); + while (input_iter.hasNext()) { + InputDelay *input_delay = input_iter.next(); + delays.push_back(input_delay); + } + + 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(); + writePortDelay(input_delay, "set_input_delay"); + } +} + +void +WriteSdc::writeOutputDelays() const +{ + // Sort departures by pin and clock name. + PortDelaySeq delays; + OutputDelayIterator output_iter(sdc_); + while (output_iter.hasNext()) { + OutputDelay *output_delay = output_iter.next(); + delays.push_back(output_delay); + } + + PortDelayLess port_delay_less(sdc_network_); + sort(delays, port_delay_less); + + PortDelaySeq::Iterator delay_iter(delays); + while (delay_iter.hasNext()) { + PortDelay *output_delay = delay_iter.next(); + writePortDelay(output_delay, "set_output_delay"); + } +} + +void +WriteSdc::writePortDelay(PortDelay *port_delay, + const char *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(TransRiseFall::rise(), MinMax::min(), + rise_min, rise_min_exists); + delays->value(TransRiseFall::rise(), MinMax::max(), + rise_max, rise_max_exists); + delays->value(TransRiseFall::fall(), MinMax::min(), + fall_min, fall_min_exists); + delays->value(TransRiseFall::fall(), MinMax::max(), + fall_max, fall_max_exists); + // Try to compress the four port delays. + if (rise_min_exists + && rise_max_exists + && fall_min_exists + && fall_max_exists + && rise_max == rise_min + && fall_min == rise_min + && fall_max == rise_min) + writePortDelay(port_delay, rise_min, + TransRiseFallBoth::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) { + writePortDelay(port_delay, rise_min, + TransRiseFallBoth::rise(), MinMaxAll::all(), sdc_cmd); + writePortDelay(port_delay, fall_min, + TransRiseFallBoth::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) { + writePortDelay(port_delay, rise_min, + TransRiseFallBoth::riseFall(), MinMaxAll::min(), sdc_cmd); + writePortDelay(port_delay, rise_max, + TransRiseFallBoth::riseFall(), MinMaxAll::max(), sdc_cmd); + } + else { + if (rise_min_exists) + writePortDelay(port_delay, rise_min, + TransRiseFallBoth::rise(), MinMaxAll::min(), sdc_cmd); + if (rise_max_exists) + writePortDelay(port_delay, rise_max, + TransRiseFallBoth::rise(), MinMaxAll::max(), sdc_cmd); + if (fall_min_exists) + writePortDelay(port_delay, fall_min, + TransRiseFallBoth::fall(), MinMaxAll::min(), sdc_cmd); + if (fall_max_exists) + writePortDelay(port_delay, fall_max, + TransRiseFallBoth::fall(), MinMaxAll::max(), sdc_cmd); + } +} + +void +WriteSdc::writePortDelay(PortDelay *port_delay, + float delay, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + const char *sdc_cmd) const +{ + fprintf(stream_, "%s ", sdc_cmd); + writeTime(delay); + ClockEdge *clk_edge = port_delay->clkEdge(); + if (clk_edge) { + writeClockKey(clk_edge->clock()); + if (clk_edge->transition() == TransRiseFall::fall()) + fprintf(stream_, " -clock_fall"); + } + fprintf(stream_, "%s%s -add_delay ", + transRiseFallFlag(tr), + minMaxFlag(min_max)); + Pin *ref_pin = port_delay->refPin(); + if (ref_pin) { + fprintf(stream_, "-reference_pin "); + writeGetPin(ref_pin); + fprintf(stream_, " "); + } + Pin *pin = port_delay->pin(); + if (sdc_network_->instance(pin) == instance_) { + Port *port = sdc_network_->port(pin); + writeGetPort(port); + } + else + writeGetPin(pin); + fprintf(stream_, "\n"); +} + +class PinClockPairNameLess +{ +public: + PinClockPairNameLess(const Network *network); + bool operator()(const PinClockPair &pin_clk1, + const PinClockPair &pin_clk2) const; + +private: + PinPathNameLess pin_less_; +}; + +PinClockPairNameLess::PinClockPairNameLess(const Network *network) : + pin_less_(network) +{ +} + +bool +PinClockPairNameLess::operator()(const PinClockPair &pin_clk1, + 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; + return pin_less_(pin1, pin2) + || (pin1 == pin2 + && ((clk1 == nullptr && clk2) + || (clk1 && clk2 + && clk1->index() < clk2->index()))); +} + +void +WriteSdc::writeClockSenses() const +{ + Vector pin_clks; + for (auto iter : sdc_->clk_sense_map_) + pin_clks.push_back(iter.first); + + // Sort by pin/clk pair so regressions results are stable. + sort(pin_clks, PinClockPairNameLess(sdc_network_)); + + for (auto pin_clk : pin_clks) { + ClockSense sense; + bool exists; + sdc_->clk_sense_map_.findKey(pin_clk, sense, exists); + if (exists) + writeClockSense(pin_clk, sense); + } +} + +void +WriteSdc::writeClockSense(PinClockPair &pin_clk, + ClockSense sense) const +{ + const char *flag = nullptr; + if (sense == ClockSense::positive) + flag = "-positive"; + else if (sense == ClockSense::negative) + flag = "-negative"; + else if (sense == ClockSense::stop) + flag = "-stop_propagation"; + fprintf(stream_, "set_sense -type clock %s ", flag); + const Clock *clk = pin_clk.second; + if (clk) { + fprintf(stream_, "-clock "); + writeGetClock(clk); + fprintf(stream_, " "); + } + writeGetPin(pin_clk.first); + fprintf(stream_, "\n"); +} + +class ClockGroupLess +{ +public: + bool operator()(const ClockGroup *clk_group1, + const ClockGroup *clk_group2) const; +}; + + +bool +ClockGroupLess::operator()(const ClockGroup *clk_group1, + const ClockGroup *clk_group2) const +{ + ClockSet *clk_set1 = clk_group1->clks(); + ClockSet *clk_set2 = clk_group2->clks(); + size_t size1 = clk_set1->size(); + size_t size2 = clk_set2->size(); + if (size1 < size2) + return true; + else if (size1 > size2) + return false; + else { + ClockSeq clks1, clks2; + ClockSet::Iterator clk_iter1(clk_set1); + while (clk_iter1.hasNext()) + clks1.push_back(clk_iter1.next()); + sort(clks1, ClockNameLess()); + + ClockSet::Iterator clk_iter2(clk_set2); + while (clk_iter2.hasNext()) + clks2.push_back(clk_iter2.next()); + 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()); + if (cmp < 0) + return true; + else if (cmp > 0) + return false; + } + return false; + } +} + +void +WriteSdc::writeClockGroups() const +{ + ClockGroupIterator groups_iter(sdc_); + while (groups_iter.hasNext()) { + ClockGroups *clk_groups = groups_iter.next(); + writeClockGroups(clk_groups); + } +} + +void +WriteSdc::writeClockGroups(ClockGroups *clk_groups) const +{ + fprintf(stream_, "set_clock_groups -name %s ", clk_groups->name()); + if (clk_groups->logicallyExclusive()) + fprintf(stream_, "-logically_exclusive \\\n"); + else if (clk_groups->physicallyExclusive()) + fprintf(stream_, "-physically_exclusive \\\n"); + else if (clk_groups->asynchronous()) + fprintf(stream_, "-asynchronous \\\n"); + if (clk_groups->allowPaths()) + fprintf(stream_, "-allow_paths \\\n"); + Vector groups; + ClockGroupSet::Iterator group_iter1(clk_groups->groups()); + while (group_iter1.hasNext()) { + ClockGroup *clk_group = group_iter1.next(); + 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(); + if (!first) + fprintf(stream_, "\\\n"); + fprintf(stream_, " -group "); + writeGetClocks(clk_group->clks()); + first = false; + } + writeCmdComment(clk_groups); + fprintf(stream_, "\n"); +} + +//////////////////////////////////////////////////////////////// + +void +WriteSdc::writeDisables() const +{ + writeDisabledCells(); + writeDisabledPorts(); + writeDisabledLibPorts(); + writeDisabledInstances(); + writeDisabledPins(); + writeDisabledEdges(); +} + +void +WriteSdc::writeDisabledCells() const +{ + DisabledCellPortsSeq disables; + sortDisabledCellPortsMap(sdc_->disabledCellPorts(), disables); + DisabledCellPortsSeq::Iterator disabled_iter(disables); + while (disabled_iter.hasNext()) { + DisabledCellPorts *disable = disabled_iter.next(); + LibertyCell *cell = disable->cell(); + if (disable->all()) { + fprintf(stream_, "set_disable_timing "); + writeGetLibCell(cell); + fprintf(stream_, "\n"); + } + if (disable->fromTo()) { + LibertyPortPairSeq pairs; + sortLibertyPortPairSet(disable->fromTo(), pairs); + LibertyPortPairSeq::Iterator pair_iter(pairs); + while (pair_iter.hasNext()) { + LibertyPortPair *from_to = pair_iter.next(); + const LibertyPort *from = from_to->first; + const LibertyPort *to = from_to->second; + fprintf(stream_, "set_disable_timing -from {%s} -to {%s} ", + from->name(), + to->name()); + writeGetLibCell(cell); + fprintf(stream_, "\n"); + } + } + if (disable->from()) { + LibertyPortSeq from; + sortLibertyPortSet(disable->from(), from); + LibertyPortSeq::Iterator from_iter(from); + while (from_iter.hasNext()) { + LibertyPort *from_port = from_iter.next(); + fprintf(stream_, "set_disable_timing -from {%s} ", + from_port->name()); + writeGetLibCell(cell); + fprintf(stream_, "\n"); + } + } + if (disable->to()) { + LibertyPortSeq to; + sortLibertyPortSet(disable->to(), to); + LibertyPortSeq::Iterator to_iter(to); + while (to_iter.hasNext()) { + LibertyPort *to_port = to_iter.next(); + fprintf(stream_, "set_disable_timing -to {%s} ", + to_port->name()); + writeGetLibCell(cell); + fprintf(stream_, "\n"); + } + } + if (disable->timingArcSets()) { + // The only syntax to disable timing arc sets disables all of the + // cell's timing arc sets. + fprintf(stream_, "set_disable_timing "); + writeGetTimingArcsOfOjbects(cell); + fprintf(stream_, "\n"); + } + } +} + +void +WriteSdc::writeDisabledPorts() const +{ + PortSeq ports; + sortPortSet(sdc_->disabledPorts(), sdc_network_, ports); + PortSeq::Iterator port_iter(ports); + while (port_iter.hasNext()) { + Port *port = port_iter.next(); + fprintf(stream_, "set_disable_timing "); + writeGetPort(port); + fprintf(stream_, "\n"); + } +} + +void +WriteSdc::writeDisabledLibPorts() const +{ + LibertyPortSeq ports; + sortLibertyPortSet(sdc_->disabledLibPorts(), ports); + LibertyPortSeq::Iterator port_iter(ports); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + fprintf(stream_, "set_disable_timing "); + writeGetLibPin(port); + fprintf(stream_, "\n"); + } +} + +void +WriteSdc::writeDisabledInstances() const +{ + DisabledInstancePortsSeq disables; + sortDisabledInstancePortsMap(sdc_->disabledInstancePorts(), + sdc_network_, disables); + DisabledInstancePortsSeq::Iterator disabled_iter(disables); + while (disabled_iter.hasNext()) { + DisabledInstancePorts *disable = disabled_iter.next(); + Instance *inst = disable->instance(); + if (disable->all()) { + fprintf(stream_, "set_disable_timing "); + writeGetInstance(inst); + fprintf(stream_, "\n"); + } + else if (disable->fromTo()) { + LibertyPortPairSeq pairs; + sortLibertyPortPairSet(disable->fromTo(), pairs); + LibertyPortPairSeq::Iterator pair_iter(pairs); + while (pair_iter.hasNext()) { + LibertyPortPair *from_to = pair_iter.next(); + const LibertyPort *from_port = from_to->first; + const LibertyPort *to_port = from_to->second; + fprintf(stream_, "set_disable_timing -from {%s} -to {%s} ", + from_port->name(), + to_port->name()); + writeGetInstance(inst); + fprintf(stream_, "\n"); + } + } + if (disable->from()) { + LibertyPortSeq from; + sortLibertyPortSet(disable->from(), from); + LibertyPortSeq::Iterator from_iter(from); + while (from_iter.hasNext()) { + LibertyPort *from_port = from_iter.next(); + fprintf(stream_, "set_disable_timing -from {%s} ", + from_port->name()); + writeGetInstance(inst); + fprintf(stream_, "\n"); + } + } + if (disable->to()) { + LibertyPortSeq to; + sortLibertyPortSet(disable->to(), to); + LibertyPortSeq::Iterator to_iter(to); + while (to_iter.hasNext()) { + LibertyPort *to_port = to_iter.next(); + fprintf(stream_, "set_disable_timing -to {%s} ", + to_port->name()); + writeGetInstance(inst); + fprintf(stream_, "\n"); + } + } + } +} + +void +WriteSdc::writeDisabledPins() const +{ + PinSeq pins; + sortPinSet(sdc_->disabledPins(), sdc_network_, pins); + PinSeq::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + fprintf(stream_, "set_disable_timing "); + writeGetPin(pin); + fprintf(stream_, "\n"); + } +} + +void +WriteSdc::writeDisabledEdges() const +{ + EdgeSeq edges; + EdgeSet::Iterator edge_iter(sdc_->disabledEdges()); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + edges.push_back(edge); + } + sortEdges(&edges, sdc_network_, graph_); + EdgeSeq::Iterator edge_iter2(edges); + while (edge_iter2.hasNext()) { + Edge *edge = edge_iter2.next(); + EdgeSet matches; + findMatchingEdges(edge, matches); + if (matches.size() == 1) + writeDisabledEdge(edge); + else if (edgeSenseIsUnique(edge, matches)) + writeDisabledEdgeSense(edge); + } +} + +void +WriteSdc::findMatchingEdges(Edge *edge, + EdgeSet &matches) const +{ + Vertex *from_vertex = edge->from(graph_); + Vertex *to_vertex = edge->to(graph_); + Pin *to_pin = to_vertex->pin(); + VertexOutEdgeIterator edge_iter(from_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *out_edge = edge_iter.next(); + if (out_edge->to(graph_)->pin() == to_pin) + matches.insert(out_edge); + } +} + +bool +WriteSdc::edgeSenseIsUnique(Edge *edge, + EdgeSet &matches) const +{ + EdgeSet::Iterator match_iter(matches); + while (match_iter.hasNext()) { + Edge *match = match_iter.next(); + if (match != edge + && match->sense() == edge->sense()) + return false; + } + return true; +} + +void +WriteSdc::writeDisabledEdge(Edge *edge) const +{ + fprintf(stream_, "set_disable_timing "); + writeGetTimingArcs(edge); + fprintf(stream_, "\n"); +} + +void +WriteSdc::writeDisabledEdgeSense(Edge *edge) const +{ + fprintf(stream_, "set_disable_timing "); + const char *sense = timingSenseString(edge->sense()); + string filter; + stringPrint(filter, "sense == %s", sense); + writeGetTimingArcs(edge, filter.c_str()); + fprintf(stream_, "\n"); +} + +//////////////////////////////////////////////////////////////// + +void +WriteSdc::writeExceptions() const +{ + ExceptionPathSeq exceptions; + sortExceptions(sdc_->exceptions(), exceptions, sdc_network_); + ExceptionPathSeq::Iterator except_iter(exceptions); + while (except_iter.hasNext()) { + ExceptionPath *exception = except_iter.next(); + if (!exception->isFilter() + && !exception->isLoop()) + writeException(exception); + } +} + +void +WriteSdc::writeException(ExceptionPath *exception) const +{ + writeExceptionCmd(exception); + if (exception->from()) + writeExceptionFrom(exception->from()); + if (exception->thrus()) { + ExceptionThruSeq::Iterator thru_iter(exception->thrus()); + while (thru_iter.hasNext()) { + ExceptionThru *thru = thru_iter.next(); + writeExceptionThru(thru); + } + } + if (exception->to()) + writeExceptionTo(exception->to()); + writeExceptionValue(exception); + writeCmdComment(exception); + fprintf(stream_, "\n"); +} + +void +WriteSdc::writeExceptionCmd(ExceptionPath *exception) const +{ + if (exception->isFalse()) { + fprintf(stream_, "set_false_path"); + writeSetupHoldFlag(exception->minMax()); + } + else if (exception->isMultiCycle()) { + fprintf(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()) + fprintf(stream_, " -end"); + } + else { + // For setup MCPs default is -end. + if (!exception->useEndClk()) + fprintf(stream_, " -start"); + } + } + else if (exception->isPathDelay()) { + if (exception->minMax() == MinMaxAll::max()) + fprintf(stream_, "set_max_delay"); + else + fprintf(stream_, "set_min_delay"); + if (exception->ignoreClkLatency()) + fprintf(stream_, " -ignore_clock_latency"); + } + else if (exception->isGroupPath()) { + if (exception->isDefault()) + fprintf(stream_, "group_path -default"); + else + fprintf(stream_, "group_path -name %s", exception->name()); + } + else + internalError("unknown exception type"); +} + +void +WriteSdc::writeExceptionValue(ExceptionPath *exception) const +{ + if (exception->isMultiCycle()) + fprintf(stream_, " %d", + exception->pathMultiplier()); + else if (exception->isPathDelay()) { + fprintf(stream_, " "); + writeTime(exception->delay()); + } +} + +void +WriteSdc::writeExceptionFrom(ExceptionFrom *from) const +{ + writeExceptionFromTo(from, "from"); +} + +void +WriteSdc::writeExceptionTo(ExceptionTo *to) const +{ + const TransRiseFallBoth *end_tr = to->endTransition(); + if (end_tr != TransRiseFallBoth::riseFall()) + fprintf(stream_, "%s ", transRiseFallFlag(end_tr)); + if (to->hasObjects()) + writeExceptionFromTo(to, "to"); +} + +void +WriteSdc::writeExceptionFromTo(ExceptionFromTo *from_to, + const char *from_to_key) const +{ + const TransRiseFallBoth *tr = from_to->transition(); + const char *tr_prefix = "-"; + if (tr == TransRiseFallBoth::rise()) + tr_prefix = "-rise_"; + else if (tr == TransRiseFallBoth::fall()) + tr_prefix = "-fall_"; + fprintf(stream_, "\\\n %s%s ", tr_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) + fprintf(stream_, "[list "); + bool first = true; + if (from_to->pins()) { + PinSeq pins; + sortPinSet(from_to->pins(), sdc_network_, pins); + PinSeq::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + if (multi_objs && !first) + fprintf(stream_, "\\\n "); + writeGetPin(pin); + first = false; + } + } + if (from_to->clks()) + writeGetClocks(from_to->clks(), multi_objs, first); + if (from_to->instances()) { + InstanceSeq insts; + sortInstanceSet(from_to->instances(), sdc_network_, insts); + InstanceSeq::Iterator inst_iter(insts); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + if (multi_objs && !first) + fprintf(stream_, "\\\n "); + writeGetInstance(inst); + first = false; + } + } + if (multi_objs) + fprintf(stream_, "]"); +} + +void +WriteSdc::writeExceptionThru(ExceptionThru *thru) const +{ + const TransRiseFallBoth *tr = thru->transition(); + const char *tr_prefix = "-"; + if (tr == TransRiseFallBoth::rise()) + tr_prefix = "-rise_"; + else if (tr == TransRiseFallBoth::fall()) + tr_prefix = "-fall_"; + fprintf(stream_, "\\\n %sthrough ", tr_prefix); + bool multi_objs = + ((thru->pins() ? thru->pins()->size() : 0) + + (thru->nets() ? thru->nets()->size() : 0) + + (thru->instances() ? thru->instances()->size() : 0)) > 1; + if (multi_objs) + fprintf(stream_, "[list "); + bool first = true; + if (thru->pins()) { + PinSeq pins; + sortPinSet(thru->pins(), sdc_network_, pins); + PinSeq::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + if (multi_objs && !first) + fprintf(stream_, "\\\n "); + writeGetPin(pin); + first = false; + } + } + if (thru->nets()) { + NetSeq nets; + sortNetSet(thru->nets(), sdc_network_, nets); + NetSeq::Iterator net_iter(nets); + while (net_iter.hasNext()) { + Net *net = net_iter.next(); + if (multi_objs && !first) + fprintf(stream_, "\\\n "); + writeGetNet(net); + first = false; + } + } + if (thru->instances()) { + InstanceSeq insts; + sortInstanceSet(thru->instances(), sdc_network_, insts); + InstanceSeq::Iterator inst_iter(insts); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + if (multi_objs && !first) + fprintf(stream_, "\\\n "); + writeGetInstance(inst); + first = false; + } + } + if (multi_objs) + fprintf(stream_, "]"); +} + +//////////////////////////////////////////////////////////////// + +void +WriteSdc::writeDataChecks() const +{ + Vector checks; + DataChecksMap::Iterator checks_iter(sdc_->data_checks_to_map_); + while (checks_iter.hasNext()) { + DataCheckSet *checks1 = checks_iter.next(); + DataCheckSet::Iterator check_iter(checks1); + while (check_iter.hasNext()) { + DataCheck *check = check_iter.next(); + checks.push_back(check); + } + } + sort(checks, DataCheckLess(sdc_network_)); + Vector::Iterator check_iter(checks); + while (check_iter.hasNext()) { + DataCheck *check = check_iter.next(); + writeDataCheck(check); + } +} + +void +WriteSdc::writeDataCheck(DataCheck *check) const +{ + for (auto setup_hold : SetupHold::range()) { + float margin; + bool one_value; + check->marginIsOneValue(setup_hold, margin, one_value); + if (one_value) + writeDataCheck(check, TransRiseFallBoth::riseFall(), + TransRiseFallBoth::riseFall(), setup_hold, margin); + else { + for (auto from_tr : TransRiseFall::range()) { + for (auto to_tr : TransRiseFall::range()) { + float margin; + bool margin_exists; + check->margin(from_tr, to_tr, setup_hold, margin, margin_exists); + if (margin_exists) { + writeDataCheck(check, from_tr->asRiseFallBoth(), + to_tr->asRiseFallBoth(), setup_hold, margin); + } + } + } + } + } +} + +void +WriteSdc::writeDataCheck(DataCheck *check, + TransRiseFallBoth *from_tr, + TransRiseFallBoth *to_tr, + SetupHold *setup_hold, + float margin) const +{ + const char *from_key = "-from"; + if (from_tr == TransRiseFallBoth::rise()) + from_key = "-rise_from"; + else if (from_tr == TransRiseFallBoth::fall()) + from_key = "-fall_from"; + fprintf(stream_, "set_data_check %s ", from_key); + writeGetPin(check->from()); + const char *to_key = "-to"; + if (to_tr == TransRiseFallBoth::rise()) + to_key = "-rise_to"; + else if (to_tr == TransRiseFallBoth::fall()) + to_key = "-fall_to"; + fprintf(stream_, " %s ", to_key); + writeGetPin(check->to()); + fprintf(stream_, "%s ", + setupHoldFlag(setup_hold)); + writeTime(margin); + fprintf(stream_, "\n"); +} + +//////////////////////////////////////////////////////////////// + +void +WriteSdc::writeEnvironment() const +{ + writeCommentSection("Environment"); + writeOperatingConditions(); + writeWireload(); + writePinLoads(); + writeDriveResistances(); + writeDrivingCells(); + writeInputTransitions(); + writeNetResistances(); + writeConstants(); + writeCaseAnalysis(); + writeDeratings(); +} + +void +WriteSdc::writeOperatingConditions() const +{ + OperatingConditions *cond = sdc_->operatingConditions(MinMax::max()); + if (cond) + fprintf(stream_, "set_operating_conditions %s\n", cond->name()); +} + +void +WriteSdc::writeWireload() const +{ + WireloadMode wireload_mode = sdc_->wireloadMode(); + if (wireload_mode != WireloadMode::unknown) + fprintf(stream_, "set_wire_load_mode \"%s\"\n", + wireloadModeString(wireload_mode)); +} + +void +WriteSdc::writePinLoads() const +{ + CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_); + while (port_iter->hasNext()) { + Port *port = port_iter->next(); + writePortLoads(port); + } + delete port_iter; +} + +void +WriteSdc::writePortLoads(Port *port) 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); + writeRiseFallMinMaxCapCmd("set_load -wire_load", + ext_cap->wireCap(), + write_port); + writeMinMaxIntValuesCmd("set_port_fanout_number", + ext_cap->fanout(), write_port); + } +} + +void +WriteSdc::writeDriveResistances() const +{ + CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_); + while (port_iter->hasNext()) { + Port *port = port_iter->next(); + InputDrive *drive = sdc_->findInputDrive(port); + if (drive) { + for (auto tr : TransRiseFall::range()) { + if (drive->driveResistanceMinMaxEqual(tr)) { + float res; + bool exists; + drive->driveResistance(tr, MinMax::max(), res, exists); + fprintf(stream_, "set_drive %s ", + transRiseFallFlag(tr)); + writeResistance(res); + fprintf(stream_, " "); + writeGetPort(port); + fprintf(stream_, "\n"); + } + else { + for (auto min_max : MinMax::range()) { + float res; + bool exists; + drive->driveResistance(tr, min_max, res, exists); + if (exists) { + fprintf(stream_, "set_drive %s %s ", + transRiseFallFlag(tr), + minMaxFlag(min_max)); + writeResistance(res); + fprintf(stream_, " "); + writeGetPort(port); + fprintf(stream_, "\n"); + } + } + } + } + } + } + delete port_iter; +} + +void +WriteSdc::writeDrivingCells() const +{ + CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_); + while (port_iter->hasNext()) { + Port *port = port_iter->next(); + InputDrive *drive = sdc_->findInputDrive(port); + if (drive) { + InputDriveCell *drive_rise_min = drive->driveCell(TransRiseFall::rise(), + MinMax::min()); + InputDriveCell *drive_rise_max = drive->driveCell(TransRiseFall::rise(), + MinMax::max()); + InputDriveCell *drive_fall_min = drive->driveCell(TransRiseFall::fall(), + MinMax::min()); + InputDriveCell *drive_fall_max = drive->driveCell(TransRiseFall::fall(), + 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); + else { + if (drive_rise_min + && drive_rise_max + && drive_rise_min->equal(drive_rise_max)) + writeDrivingCell(port, drive_rise_min, TransRiseFall::rise(), nullptr); + else { + if (drive_rise_min) + writeDrivingCell(port, drive_rise_min, TransRiseFall::rise(), + MinMax::min()); + if (drive_rise_max) + writeDrivingCell(port, drive_rise_max, TransRiseFall::rise(), + MinMax::max()); + } + if (drive_fall_min + && drive_fall_max + && drive_fall_min->equal(drive_fall_max)) + writeDrivingCell(port, drive_fall_min, TransRiseFall::fall(), nullptr); + else { + if (drive_fall_min) + writeDrivingCell(port, drive_fall_min, TransRiseFall::fall(), + MinMax::min()); + if (drive_fall_max) + writeDrivingCell(port, drive_fall_max, TransRiseFall::fall(), + MinMax::max()); + } + } + } + } + delete port_iter; +} + +void +WriteSdc::writeDrivingCell(Port *port, + InputDriveCell *drive_cell, + const TransRiseFall *tr, + const MinMax *min_max) const +{ + LibertyCell *cell = drive_cell->cell(); + LibertyPort *from_port = drive_cell->fromPort(); + LibertyPort *to_port = drive_cell->toPort(); + float *from_slews = drive_cell->fromSlews(); + LibertyLibrary *lib = drive_cell->library(); + fprintf(stream_, "set_driving_cell"); + if (tr) + fprintf(stream_, " %s", transRiseFallFlag(tr)); + if (min_max) + fprintf(stream_, " %s", minMaxFlag(min_max)); + // Only write -library if it was specified in the sdc. + if (lib) + fprintf(stream_, " -library %s", lib->name()); + fprintf(stream_, " -lib_cell %s", cell->name()); + if (from_port) + fprintf(stream_, " -from_pin {%s}", + from_port->name()); + fprintf(stream_, + " -pin {%s} -input_transition_rise ", + to_port->name()); + writeTime(from_slews[TransRiseFall::riseIndex()]); + fprintf(stream_, " -input_transition_fall "); + writeTime(from_slews[TransRiseFall::fallIndex()]); + fprintf(stream_, " "); + writeGetPort(port); + fprintf(stream_, "\n"); +} + +void +WriteSdc::writeInputTransitions() const +{ + CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_); + while (port_iter->hasNext()) { + Port *port = port_iter->next(); + InputDrive *drive = sdc_->findInputDrive(port); + if (drive) { + RiseFallMinMax *slews = drive->slews(); + WriteGetPort write_port(port, this); + writeRiseFallMinMaxTimeCmd("set_input_transition", slews, write_port); + } + } + delete port_iter; +} + +void +WriteSdc::writeNetResistances() const +{ + NetResistanceMap *net_res_map = sdc_->netResistances(); + NetSeq nets; + NetResistanceMap::Iterator res_iter(net_res_map); + while (res_iter.hasNext()) { + Net *net; + MinMaxFloatValues values; + res_iter.next(net, values); + nets.push_back(net); + } + sort(nets, NetPathNameLess(sdc_network_)); + NetSeq::Iterator net_iter(nets); + while (net_iter.hasNext()) { + Net *net = net_iter.next(); + float min_res, max_res; + bool min_exists, max_exists; + 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) + writeNetResistance(net, MinMaxAll::all(), min_res); + else { + if (min_exists) + writeNetResistance(net, MinMaxAll::min(), min_res); + if (max_exists) + writeNetResistance(net, MinMaxAll::max(), max_res); + } + } +} + +void +WriteSdc::writeNetResistance(Net *net, + const MinMaxAll *min_max, + float res) const +{ + fprintf(stream_, "set_resistance "); + writeResistance(res); + fprintf(stream_, "%s ", minMaxFlag(min_max)); + writeGetNet(net); + fprintf(stream_, "\n"); +} + +void +WriteSdc::writeConstants() const +{ + PinSeq pins; + sortedLogicValuePins(sdc_->logicValues(), pins); + PinSeq::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + writeConstant(pin); + } +} + +void +WriteSdc::writeConstant(Pin *pin) const +{ + const char *cmd = setConstantCmd(pin); + fprintf(stream_, "%s ", cmd); + writeGetPin(pin); + fprintf(stream_, "\n"); +} + +const char * +WriteSdc::setConstantCmd(Pin *pin) const +{ + LogicValue value; + bool exists; + sdc_->logicValue(pin, value, exists); + switch (value) { + case LogicValue::zero: + return "set_LogicValue::zero"; + case LogicValue::one: + return "set_logic_one"; + case LogicValue::unknown: + return "set_logic_dc"; + case LogicValue::rise: + case LogicValue::fall: + default: + internalError("illegal set_logic value"); + } +} + +void +WriteSdc::writeCaseAnalysis() const +{ + PinSeq pins; + sortedLogicValuePins(sdc_->caseLogicValues(), pins); + PinSeq::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + writeCaseAnalysis(pin); + } +} + + +void +WriteSdc::writeCaseAnalysis(Pin *pin) const +{ + const char *value_str = caseAnalysisValueStr(pin); + fprintf(stream_, "set_case_analysis %s ", value_str); + writeGetPin(pin); + fprintf(stream_, "\n"); +} + +const char * +WriteSdc::caseAnalysisValueStr(Pin *pin) const +{ + LogicValue value; + bool exists; + sdc_->caseLogicValue(pin, value, exists); + switch (value) { + case LogicValue::zero: + return "0"; + case LogicValue::one: + return "1"; + case LogicValue::rise: + return "rising"; + case LogicValue::fall: + return "falling"; + case LogicValue::unknown: + default: + internalError("invalid set_case_analysis value"); + } +} + +void +WriteSdc::sortedLogicValuePins(LogicValueMap *value_map, + PinSeq &pins) const +{ + LogicValueMap::ConstIterator value_iter(value_map); + while (value_iter.hasNext()) { + LogicValue value; + const Pin *pin; + value_iter.next(pin, value); + pins.push_back(const_cast(pin)); + } + // Sort pins. + sort(pins, PinPathNameLess(sdc_network_)); +} + +//////////////////////////////////////////////////////////////// + +void +WriteSdc::writeDeratings() const +{ + DeratingFactorsGlobal *factors = sdc_->derating_factors_; + if (factors) + writeDerating(factors); + + NetDeratingFactorsMap::Iterator net_iter(sdc_->net_derating_factors_); + while (net_iter.hasNext()) { + const Net *net; + DeratingFactorsNet *factors; + net_iter.next(net, factors); + WriteGetNet write_net(net, this); + for (auto early_late : EarlyLate::range()) { + writeDerating(factors, TimingDerateType::net_delay, early_late, + &write_net); + } + } + + InstDeratingFactorsMap::Iterator inst_iter(sdc_->inst_derating_factors_); + while (inst_iter.hasNext()) { + const Instance *inst; + DeratingFactorsCell *factors; + inst_iter.next(inst, factors); + WriteGetInstance write_inst(inst, this); + writeDerating(factors, &write_inst); + } + + CellDeratingFactorsMap::Iterator cell_iter(sdc_->cell_derating_factors_); + while (cell_iter.hasNext()) { + const LibertyCell *cell; + DeratingFactorsCell *factors; + cell_iter.next(cell, factors); + WriteGetLibCell write_cell(cell, this); + writeDerating(factors, &write_cell); + } +} + +void +WriteSdc::writeDerating(DeratingFactorsGlobal *factors) const +{ + for (auto early_late : EarlyLate::range()) { + 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); + factors->factors(TimingDerateType::net_delay)->isOneValue(early_late, + 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))) { + if (delay_value != 1.0) { + fprintf(stream_, "set_timing_derate %s ", earlyLateFlag(early_late)); + writeFloat(delay_value); + fprintf(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); + } + } + } +} + +void +WriteSdc::writeDerating(DeratingFactorsCell *factors, + WriteSdcObject *write_obj) const +{ + for (auto early_late : EarlyLate::range()) { + DeratingFactors *delay_factors=factors->factors(TimingDerateType::cell_delay); + writeDerating(delay_factors, TimingDerateType::cell_delay, early_late, write_obj); + DeratingFactors *check_factors=factors->factors(TimingDerateType::cell_check); + writeDerating(check_factors, TimingDerateType::cell_check, early_late, write_obj); + } +} + +void +WriteSdc::writeDerating(DeratingFactors *factors, + TimingDerateType type, + const MinMax *early_late, + WriteSdcObject *write_obj) const +{ + const char *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) { + fprintf(stream_, "set_timing_derate %s %s ", + type_key, + earlyLateFlag(early_late)); + writeFloat(value); + if (write_obj) { + fprintf(stream_, " "); + write_obj->write(); + } + fprintf(stream_, "\n"); + } + } + else { + for (int 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]; + factors->isOneValue(clk_data, early_late, is_one_value, value); + if (is_one_value) { + if (value != 1.0) { + fprintf(stream_, "set_timing_derate %s %s %s ", + type_key, + earlyLateFlag(early_late), + clk_data_key); + writeFloat(value); + if (write_obj) { + fprintf(stream_, " "); + write_obj->write(); + } + fprintf(stream_, "\n"); + } + } + else { + for (auto tr : TransRiseFall::range()) { + float factor; + bool exists; + factors->factor(clk_data, tr, early_late, factor, exists); + if (exists) { + fprintf(stream_, "set_timing_derate %s %s %s %s ", + type_key, + clk_data_key, + transRiseFallFlag(tr), + earlyLateFlag(early_late)); + writeFloat(factor); + if (write_obj) { + fprintf(stream_, " "); + write_obj->write(); + } + fprintf(stream_, "\n"); + } + } + } + } + } +} + +static const char * +timingDerateTypeKeyword(TimingDerateType type) +{ + static const char *type_keys[] = {"-cell_delay","-cell_check","-net_delay"}; + return type_keys[static_cast(type)]; +} + +//////////////////////////////////////////////////////////////// + +void +WriteSdc::writeDesignRules() const +{ + writeCommentSection("Design Rules"); + writeMinPulseWidths(); + writeLatchBorowLimits(); + writeSlewLimits(); + writeCapLimits(); + writeFanoutLimits(); + writeMaxArea(); +} + +void +WriteSdc::writeMinPulseWidths() const +{ + PinMinPulseWidthMap::Iterator + pin_iter(sdc_->pin_min_pulse_width_map_); + while (pin_iter.hasNext()) { + const Pin *pin; + RiseFallValues *min_widths; + pin_iter.next(pin, min_widths); + WriteGetPin write_obj(pin, this); + writeMinPulseWidths(min_widths, write_obj); + } + InstMinPulseWidthMap::Iterator + inst_iter(sdc_->inst_min_pulse_width_map_); + while (inst_iter.hasNext()) { + const Instance *inst; + RiseFallValues *min_widths; + inst_iter.next(inst, min_widths); + WriteGetInstance write_obj(inst, this); + writeMinPulseWidths(min_widths, write_obj); + } + ClockMinPulseWidthMap::Iterator + clk_iter(sdc_->clk_min_pulse_width_map_); + while (clk_iter.hasNext()) { + const Clock *clk; + RiseFallValues *min_widths; + clk_iter.next(clk, min_widths); + WriteGetClock write_obj(clk, this); + writeMinPulseWidths(min_widths, write_obj); + } +} + +void +WriteSdc::writeMinPulseWidths(RiseFallValues *min_widths, + WriteSdcObject &write_obj) const +{ + bool hi_exists, low_exists; + float hi, low; + min_widths->value(TransRiseFall::rise(), hi, hi_exists); + min_widths->value(TransRiseFall::fall(), low, low_exists); + if (hi_exists && low_exists + && hi == low) + writeMinPulseWidth("", hi, write_obj); + else { + if (hi_exists) + writeMinPulseWidth("-high ", hi, write_obj); + if (low_exists) + writeMinPulseWidth("-low ", low, write_obj); + } +} + +void +WriteSdc::writeMinPulseWidth(const char *hi_low, + float value, + WriteSdcObject &write_obj) const +{ + fprintf(stream_, "set_min_pulse_width %s", hi_low); + writeTime(value); + fprintf(stream_, " "); + write_obj.write(); + fprintf(stream_, "\n"); +} + +//////////////////////////////////////////////////////////////// + +void +WriteSdc::writeLatchBorowLimits() const +{ + PinLatchBorrowLimitMap::Iterator + pin_iter(sdc_->pin_latch_borrow_limit_map_); + while (pin_iter.hasNext()) { + const Pin *pin; + float limit; + pin_iter.next(pin, limit); + fprintf(stream_, "set_max_time_borrow "); + writeTime(limit); + fprintf(stream_, " "); + writeGetPin(pin); + fprintf(stream_, "\n"); + } + InstLatchBorrowLimitMap::Iterator + inst_iter(sdc_->inst_latch_borrow_limit_map_); + while (inst_iter.hasNext()) { + const Instance *inst; + float limit; + inst_iter.next(inst, limit); + fprintf(stream_, "set_max_time_borrow "); + writeTime(limit); + fprintf(stream_, " "); + writeGetInstance(inst); + fprintf(stream_, "\n"); + } + ClockLatchBorrowLimitMap::Iterator + clk_iter(sdc_->clk_latch_borrow_limit_map_); + while (clk_iter.hasNext()) { + const Clock *clk; + float limit; + clk_iter.next(clk, limit); + fprintf(stream_, "set_max_time_borrow "); + writeTime(limit); + fprintf(stream_, " "); + writeGetClock(clk); + fprintf(stream_, "\n"); + } +} + +//////////////////////////////////////////////////////////////// + +void +WriteSdc::writeSlewLimits() const +{ + const MinMax *min_max = MinMax::max(); + float slew; + bool exists; + sdc_->slewLimit(cell_, min_max, slew, exists); + if (exists) { + fprintf(stream_, "set_max_transition "); + writeTime(slew); + fprintf(stream_, " [current_design]\n"); + } + + CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_); + while (port_iter->hasNext()) { + Port *port = port_iter->next(); + sdc_->slewLimit(port, min_max, slew, exists); + if (exists) { + fprintf(stream_, "set_max_transition "); + writeTime(slew); + fprintf(stream_, " "); + writeGetPort(port); + fprintf(stream_, "\n"); + } + } + delete port_iter; + + ConstPinSeq pins; + sdc_->slewLimitPins(pins); + sort(pins, PinPathNameLess(network_)); + ConstPinSeq::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + const Pin *pin = pin_iter.next(); + float slew; + bool exists; + sdc_->slewLimit(pin, min_max, slew, exists); + if (exists) { + fprintf(stream_, "set_max_transition "); + writeTime(slew); + fprintf(stream_, " "); + writeGetPin(pin); + fprintf(stream_, "\n"); + } + } + + writeClkSlewLimits(); +} + +void +WriteSdc::writeClkSlewLimits() const +{ + const MinMax *min_max = MinMax::max(); + ClockSeq clks; + sdc_->sortedClocks(clks); + ClockSeq::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + 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(TransRiseFall::rise(), PathClkOrData::clk, min_max, + rise_clk_limit, rise_clk_exists); + clk->slewLimit(TransRiseFall::fall(), PathClkOrData::clk, min_max, + fall_clk_limit, fall_clk_exists); + clk->slewLimit(TransRiseFall::rise(), PathClkOrData::data, min_max, + rise_data_limit, rise_data_exists); + clk->slewLimit(TransRiseFall::fall(), PathClkOrData::data, min_max, + 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) + 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); + 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_data_exists && fall_data_exists + && 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); + } + } + } + } +} + +void +WriteSdc::writeClkSlewLimit(const char *clk_data, + const char *rise_fall, + const Clock *clk, + float limit) const +{ + fprintf(stream_, "set_max_transition %s%s", clk_data, rise_fall); + writeTime(limit); + fprintf(stream_, " "); + writeGetClock(clk); + fprintf(stream_, "\n"); +} + +void +WriteSdc::writeCapLimits() const +{ + writeCapLimits(MinMax::min(), "set_min_capacitance"); + writeCapLimits(MinMax::max(), "set_max_capacitance"); +} + +void +WriteSdc::writeCapLimits(const MinMax *min_max, + const char *cmd) const +{ + float cap; + bool exists; + sdc_->capacitanceLimit(cell_, min_max, cap, exists); + if (exists) { + fprintf(stream_, "%s ", cmd); + writeCapacitance(cap); + fprintf(stream_, " [current_design]\n"); + } + + PortCapLimitMap::Iterator port_iter(sdc_->port_cap_limit_map_); + while (port_iter.hasNext()) { + Port *port; + MinMaxFloatValues values; + port_iter.next(port, values); + float cap; + bool exists; + values.value(min_max, cap, exists); + if (exists) { + fprintf(stream_, "%s ", cmd); + writeCapacitance(cap); + fprintf(stream_, " "); + writeGetPort(port); + fprintf(stream_, "\n"); + } + } + + PinCapLimitMap::Iterator pin_iter(sdc_->pin_cap_limit_map_); + while (pin_iter.hasNext()) { + Pin *pin; + MinMaxFloatValues values; + pin_iter.next(pin, values); + float cap; + bool exists; + values.value(min_max, cap, exists); + if (exists) { + fprintf(stream_, "%s ", cmd); + writeCapacitance(cap); + fprintf(stream_, " "); + writeGetPin(pin); + fprintf(stream_, "\n"); + } + } +} + +void +WriteSdc::writeMaxArea() const +{ + float max_area = sdc_->maxArea(); + if (max_area > 0.0) { + fprintf(stream_, "set_max_area "); + writeFloat(max_area); + fprintf(stream_, "\n"); + } +} + +void +WriteSdc::writeFanoutLimits() const +{ + writeFanoutLimits(MinMax::min(), "set_min_fanout"); + writeFanoutLimits(MinMax::max(), "set_max_fanout"); +} + +void +WriteSdc::writeFanoutLimits(const MinMax *min_max, + const char *cmd) const +{ + float fanout; + bool exists; + sdc_->fanoutLimit(cell_, min_max, fanout, exists); + if (exists) { + fprintf(stream_, "%s ", cmd); + writeFloat(fanout); + fprintf(stream_, " [current_design]\n"); + } + else { + CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_); + while (port_iter->hasNext()) { + Port *port = port_iter->next(); + sdc_->fanoutLimit(port, min_max, fanout, exists); + if (exists) { + fprintf(stream_, "%s ", cmd); + writeFloat(fanout); + fprintf(stream_, " "); + writeGetPort(port); + fprintf(stream_, "\n"); + } + } + delete port_iter; + } +} + +//////////////////////////////////////////////////////////////// + +void +WriteSdc::writeVariables() const +{ + if (sdc_->propagateAllClocks()) { + if (compatible_) + fprintf(stream_, "set timing_all_clocks_propagated true\n"); + else + fprintf(stream_, "set sta_propagate_all_clocks 1\n"); + } + if (sdc_->presetClrArcsEnabled()) { + if (compatible_) + fprintf(stream_, "set timing_enable_preset_clear_arcs true\n"); + else + fprintf(stream_, "set sta_preset_clear_arcs_enabled 1\n"); + } +} + +//////////////////////////////////////////////////////////////// + +void +WriteSdc::writeGetTimingArcsOfOjbects(LibertyCell *cell) const +{ + fprintf(stream_, "[%s -of_objects ", getTimingArcsCmd()); + writeGetLibCell(cell); + fprintf(stream_, "]"); +} + +void +WriteSdc::writeGetTimingArcs(Edge *edge) const +{ + writeGetTimingArcs(edge, nullptr); +} + +void +WriteSdc::writeGetTimingArcs(Edge *edge, + const char *filter) const +{ + fprintf(stream_, "[%s -from ", getTimingArcsCmd()); + Vertex *from_vertex = edge->from(graph_); + writeGetPin(from_vertex->pin()); + fprintf(stream_, " -to "); + Vertex *to_vertex = edge->to(graph_); + writeGetPin(to_vertex->pin()); + if (filter) + fprintf(stream_, " -filter {%s}", filter); + fprintf(stream_, "]"); +} + +const char * +WriteSdc::getTimingArcsCmd() const +{ + return compatible_ ? "get_timing_arcs" : "get_timing_edges"; +} + +//////////////////////////////////////////////////////////////// + +void +WriteSdc::writeGetLibCell(const LibertyCell *cell) const +{ + fprintf(stream_, "[get_lib_cells {%s/%s}]", + cell->libertyLibrary()->name(), + cell->name()); +} + +void +WriteSdc::writeGetLibPin(const LibertyPort *port) const +{ + LibertyCell *cell = port->libertyCell(); + LibertyLibrary *lib = cell->libertyLibrary(); + fprintf(stream_, "[get_lib_pins {%s/%s/%s}]", + lib->name(), + cell->name(), + port->name()); +} + +void +WriteSdc::writeGetClocks(ClockSet *clks) const +{ + bool first = true; + bool multiple = clks->size() > 1; + if (multiple) + fprintf(stream_, "[list "); + writeGetClocks(clks, multiple, first); + if (multiple) + fprintf(stream_, "]"); +} + +void +WriteSdc::writeGetClocks(ClockSet *clks, + bool multiple, + bool &first) const +{ + ClockSeq clks1; + sortClockSet(clks, clks1); + ClockSeq::Iterator clk_iter(clks1); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + if (multiple && !first) + fprintf(stream_, "\\\n "); + writeGetClock(clk); + first = false; + } +} + +void +WriteSdc::writeGetClock(const Clock *clk) const +{ + fprintf(stream_, "[get_clocks {%s}]", + clk->name()); +} + +void +WriteSdc::writeGetPort(const Port *port) const +{ + fprintf(stream_, "[get_ports {%s}]", sdc_network_->name(port)); +} + +void +WriteSdc::writeGetPins(PinSet *pins) const +{ + PinSeq pins1; + sortPinSet(pins, sdc_network_, pins1); + writeGetPins(&pins1); +} + +void +WriteSdc::writeGetPins(PinSeq *pins) const +{ + bool multiple = pins->size() > 1; + if (multiple) + fprintf(stream_, "[list "); + PinSeq::Iterator pin_iter(pins); + bool first = true; + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + if (multiple && !first) + fprintf(stream_, "\\\n "); + writeGetPin(pin); + first = false; + } + if (multiple) + fprintf(stream_, "]"); +} + +void +WriteSdc::writeGetPin(const Pin *pin) const +{ + if (sdc_network_->instance(pin) == instance_) + fprintf(stream_, "[get_ports {%s}]", sdc_network_->portName(pin)); + else + fprintf(stream_, "[get_pins {%s}]", pathName(pin)); +} + +void +WriteSdc::writeGetNet(const Net *net) const +{ + fprintf(stream_, "[get_nets {%s}]", pathName(net)); +} + +void +WriteSdc::writeGetInstance(const Instance *inst) const +{ + fprintf(stream_, "[get_cells {%s}]", pathName(inst)); +} + +const char * +WriteSdc::pathName(const Pin *pin) const +{ + const char *pin_path = sdc_network_->pathName(pin); + if (top_instance_) + return pin_path; + else + return pin_path + instance_name_length_ + 1; +} + +const char * +WriteSdc::pathName(const Net *net) const +{ + const char *net_path = sdc_network_->pathName(net); + if (top_instance_) + return net_path; + else + return net_path + instance_name_length_ + 1; +} + +const char * +WriteSdc::pathName(const Instance *inst) const +{ + const char *inst_path = sdc_network_->pathName(inst); + if (top_instance_) + return inst_path; + else + return inst_path + instance_name_length_ + 1; +} + +void +WriteSdc::writeCommentSection(const char *line) const +{ + writeCommentSeparator(); + fprintf(stream_, "# %s\n", line); + writeCommentSeparator(); +} + +void +WriteSdc::writeCommentSeparator() const +{ + fprintf(stream_, "###############################################################################\n"); +} + +//////////////////////////////////////////////////////////////// + +void +WriteSdc::writeRiseFallMinMaxTimeCmd(const char *sdc_cmd, + RiseFallMinMax *values, + WriteSdcObject &write_object) const +{ + writeRiseFallMinMaxCmd(sdc_cmd, values, units_->timeUnit()->scale(), + write_object); +} + +void +WriteSdc::writeRiseFallMinMaxCapCmd(const char *sdc_cmd, + RiseFallMinMax *values, + WriteSdcObject &write_object) const +{ + writeRiseFallMinMaxCmd(sdc_cmd, values, units_->capacitanceUnit()->scale(), + write_object); +} + +void +WriteSdc::writeRiseFallMinMaxCmd(const char *sdc_cmd, + 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(TransRiseFall::fall(), MinMax::min(), + fall_min, fall_min_exists); + values->value(TransRiseFall::fall(), MinMax::max(), + fall_max, fall_max_exists); + values->value(TransRiseFall::rise(), MinMax::min(), + rise_min, rise_min_exists); + values->value(TransRiseFall::rise(), MinMax::max(), + 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/fall/min/max match. + writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale, + TransRiseFallBoth::riseFall(), MinMaxAll::all(), + write_object); + } + else if (rise_min == fall_min + && rise_max == fall_max) { + // rise/fall match. + writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale, + TransRiseFallBoth::riseFall(), MinMaxAll::min(), + write_object); + writeRiseFallMinMaxCmd(sdc_cmd, rise_max, scale, + TransRiseFallBoth::riseFall(), MinMaxAll::max(), + write_object); + } + else if (rise_min == rise_max + && fall_min == fall_max) { + // min/max match. + writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale, + TransRiseFallBoth::rise(), MinMaxAll::all(), + write_object); + writeRiseFallMinMaxCmd(sdc_cmd, fall_min, scale, + TransRiseFallBoth::fall(), MinMaxAll::all(), + write_object); + } + } + else { + if (rise_min_exists) + writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale, + TransRiseFallBoth::rise(), MinMaxAll::min(), + write_object); + if (rise_max_exists) + writeRiseFallMinMaxCmd(sdc_cmd, rise_max, scale, + TransRiseFallBoth::rise(), MinMaxAll::max(), + write_object); + if (fall_min_exists) + writeRiseFallMinMaxCmd(sdc_cmd, fall_min, scale, + TransRiseFallBoth::fall(), MinMaxAll::min(), + write_object); + if (fall_max_exists) + writeRiseFallMinMaxCmd(sdc_cmd, fall_max, scale, + TransRiseFallBoth::fall(), MinMaxAll::max(), + write_object); + } +} + +void +WriteSdc::writeRiseFallMinMaxCmd(const char *sdc_cmd, + float value, + float scale, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const +{ + fprintf(stream_, "%s%s%s ", + sdc_cmd, + transRiseFallFlag(tr), + minMaxFlag(min_max)); + writeFloat(value / scale); + fprintf(stream_, " "); + write_object.write(); + fprintf(stream_, "\n"); +} + +void +WriteSdc::writeClockKey(const Clock *clk) const +{ + fprintf(stream_, " -clock "); + writeGetClock(clk); +} + +//////////////////////////////////////////////////////////////// + +void +WriteSdc::writeMinMaxFloatValuesCmd(const char *sdc_cmd, + MinMaxFloatValues *values, + float scale, + WriteSdcObject &write_object) const +{ + float min, max; + bool min_exists, max_exists; + values->value(MinMax::min(), min, min_exists); + values->value(MinMax::max(), max, max_exists); + if (min_exists && max_exists + && min == max) { + // min/max match. + writeMinMaxFloatCmd(sdc_cmd, min, scale, MinMaxAll::all(), write_object); + } + else { + if (min_exists) + writeMinMaxFloatCmd(sdc_cmd, min, scale, MinMaxAll::min(), write_object); + if (max_exists) + writeMinMaxFloatCmd(sdc_cmd, max, scale, MinMaxAll::max(), write_object); + } +} + +void +WriteSdc::writeMinMaxFloatCmd(const char *sdc_cmd, + float value, + float scale, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const +{ + fprintf(stream_, "%s%s ", + sdc_cmd, + minMaxFlag(min_max)); + writeFloat(value / scale); + fprintf(stream_, " "); + write_object.write(); + fprintf(stream_, "\n"); +} + +void +WriteSdc::writeMinMaxIntValuesCmd(const char *sdc_cmd, + MinMaxIntValues *values, + WriteSdcObject &write_object) const +{ + int min, max; + bool min_exists, max_exists; + values->value(MinMax::min(), min, min_exists); + values->value(MinMax::max(), max, max_exists); + if (min_exists && max_exists + && min == max) { + // min/max match. + writeMinMaxIntCmd(sdc_cmd, min, MinMaxAll::all(), write_object); + } + else { + if (min_exists) + writeMinMaxIntCmd(sdc_cmd, min, MinMaxAll::min(), write_object); + if (max_exists) + writeMinMaxIntCmd(sdc_cmd, max, MinMaxAll::max(), write_object); + } +} + +void +WriteSdc::writeMinMaxIntCmd(const char *sdc_cmd, + int value, + const MinMaxAll *min_max, + WriteSdcObject &write_object) const +{ + fprintf(stream_, "%s%s ", + sdc_cmd, + minMaxFlag(min_max)); + fprintf(stream_, "%d ", value); + write_object.write(); + fprintf(stream_, "\n"); +} + +//////////////////////////////////////////////////////////////// + +float +WriteSdc::scaleTime(float time) const +{ + return time / units_->timeUnit()->scale(); +} + +float +WriteSdc::scaleCapacitance(float cap) const +{ + return cap / units_->capacitanceUnit()->scale(); +} + +float +WriteSdc::scaleResistance(float res) const +{ + return res / units_->resistanceUnit()->scale(); +} + +void +WriteSdc::writeFloat(float value) const +{ + fprintf(stream_, "%.*f", digits_, value); +} + +void +WriteSdc::writeTime(float time) const +{ + fprintf(stream_, "%.*f", digits_, scaleTime(time)); +} + +void +WriteSdc::writeCapacitance(float cap) const +{ + fprintf(stream_, "%.*f", digits_, scaleCapacitance(cap)); +} + +void +WriteSdc::writeResistance(float res) const +{ + fprintf(stream_, "%.*f", digits_, scaleResistance(res)); +} + +void +WriteSdc::writeFloatSeq(FloatSeq *floats, + float scale) const +{ + fprintf(stream_, "{"); + FloatSeq::ConstIterator iter(floats); + bool first = true; + while (iter.hasNext()) { + float flt = iter.next(); + if (!first) + fprintf(stream_, " "); + writeFloat(flt * scale); + first = false; + } + fprintf(stream_, "}"); +} + +void +WriteSdc::writeIntSeq(IntSeq *ints) const +{ + fprintf(stream_, "{"); + IntSeq::ConstIterator iter(ints); + bool first = true; + while (iter.hasNext()) { + int i = iter.next(); + if (!first) + fprintf(stream_, " "); + fprintf(stream_, "%d", i); + first = false; + } + fprintf(stream_, "}"); +} + + +//////////////////////////////////////////////////////////////// + +static const char * +transRiseFallFlag(const TransRiseFall *tr) +{ + return (tr == TransRiseFall::rise()) ? "-rise" : "-fall"; +} + +static const char * +transRiseFallFlag(const TransRiseFallBoth *tr) +{ + if (tr == TransRiseFallBoth::rise()) + return " -rise"; + else if (tr == TransRiseFallBoth::fall()) + return " -fall"; + else if (tr == TransRiseFallBoth::riseFall()) + return ""; + else { + internalError("unknown transition"); + } + return nullptr; +} + +static const char * +minMaxFlag(const MinMaxAll *min_max) +{ + if (min_max == MinMaxAll::all()) + return ""; + else if (min_max == MinMaxAll::min()) + return " -min"; + else if (min_max == MinMaxAll::max()) + return " -max"; + else { + internalError("unknown MinMaxAll"); + return nullptr; + } +} + +static const char * +minMaxFlag(const MinMax *min_max) +{ + if (min_max == MinMax::min()) + return " -min"; + else if (min_max == MinMax::max()) + return " -max"; + else { + internalError("unknown MinMax"); + return nullptr; + } +} + +static const char * +earlyLateFlag(const MinMax *early_late) +{ + if (early_late == MinMax::min()) + return "-early"; + else if (early_late == MinMax::max()) + return "-late"; + else { + internalError("unknown EarlyLate"); + return nullptr; + } +} + +void +WriteSdc::writeSetupHoldFlag(const MinMaxAll *min_max) const +{ + if (min_max == MinMaxAll::min()) + fprintf(stream_, " -hold"); + else if (min_max == MinMaxAll::max()) + fprintf(stream_, " -setup"); +} + +static const char * +setupHoldFlag(const MinMax *min_max) +{ + if (min_max == MinMax::min()) + return " -hold"; + else if (min_max == MinMax::max()) + return " -setup"; + else { + internalError("unknown MinMax"); + return nullptr; + } +} + +void +WriteSdc::writeCmdComment(SdcCmdComment *cmd) const +{ + const char *comment = cmd->comment(); + if (comment) { + fprintf(stream_, " -comment {%s}", comment); + } +} + +} // namespace diff --git a/sdc/WriteSdc.hh b/sdc/WriteSdc.hh new file mode 100644 index 0000000..776e204 --- /dev/null +++ b/sdc/WriteSdc.hh @@ -0,0 +1,37 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_WRITE_SDC_H +#define STA_WRITE_SDC_H + +#include "NetworkClass.hh" +#include "SdcClass.hh" + +namespace sta { + +// Write constraints to a file. +// Allow contraints to apply to an instance to support write_context. +void +writeSdc(Instance *instance, + const char *filename, + const char *creator, + bool compatible, + bool no_timestamp, + int digits, + Sdc *sdc); + +} // namespace +#endif diff --git a/sdc/WriteSdcPvt.hh b/sdc/WriteSdcPvt.hh new file mode 100644 index 0000000..b719aa1 --- /dev/null +++ b/sdc/WriteSdcPvt.hh @@ -0,0 +1,255 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_WRITE_SDC_PVT_H +#define STA_WRITE_SDC_PVT_H + +#include "DisallowCopyAssign.hh" + +namespace sta { + +class WriteSdcObject; + +class WriteSdc : public StaState +{ +public: + WriteSdc(Instance *instance, + const char *filename, + const char *creator, + bool compatible, + int digits, + bool no_timestamp, + Sdc *sdc); + virtual ~WriteSdc(); + void write(); + + void openFile(const char *filename); + void closeFile(); + void flush(); + virtual void writeHeader() const; + void writeTiming() const; + void writeDisables() const; + void writeDisabledCells() const; + void writeDisabledPorts() const; + void writeDisabledLibPorts() const; + void writeDisabledInstances() const; + void writeDisabledPins() const; + void writeDisabledEdges() const; + void writeDisabledEdge(Edge *edge) const; + void findMatchingEdges(Edge *edge, + EdgeSet &matches) const; + bool edgeSenseIsUnique(Edge *edge, + EdgeSet &matches) const; + void writeDisabledEdgeSense(Edge *edge) const; + void writeClocks() const; + void writeClock(Clock *clk) const; + void writeGeneratedClock(Clock *clk) const; + void writeClockPins(Clock *clk) const; + void writeFloatSeq(FloatSeq *floats, + float scale) const; + void writeIntSeq(IntSeq *ints) const; + void writeClockSlews(Clock *clk) const; + void writeClockUncertainty(Clock *clk) const; + void writeClockUncertainty(Clock *clk, + const char *setup_hold, + float value) const; + void writeClockUncertaintyPins() const; + void writeClockUncertaintyPin(const Pin *pin, + ClockUncertainties *uncertainties) const; + void writeClockUncertaintyPin(const Pin *pin, + const char *setup_hold, + float value) const; + void writeClockLatencies() const; + void writeClockInsertions() const; + void writeClockInsertion(ClockInsertion *insert, + 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, + const char *sdc_cmd) const; + void writePortDelay(PortDelay *port_delay, + float delay, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + const char *sdc_cmd) const; + void writeClockSenses() const; + void writeClockSense(PinClockPair &pin_clk, + ClockSense sense) const; + void writeClockGroups() const; + void writeClockGroups(ClockGroups *clk_groups) const; + void writeExceptions() const; + void writeException(ExceptionPath *exception) const; + void writeExceptionCmd(ExceptionPath *exception) const; + void writeExceptionValue(ExceptionPath *exception) const; + void writeExceptionFrom(ExceptionFrom *from) const; + void writeExceptionTo(ExceptionTo *to) const; + void writeExceptionFromTo(ExceptionFromTo *from_to, + const char *from_to_key) const; + void writeExceptionThru(ExceptionThru *thru) const; + void writeDataChecks() const; + void writeDataCheck(DataCheck *check) const; + void writeDataCheck(DataCheck *check, + TransRiseFallBoth *from_tr, + TransRiseFallBoth *to_tr, + SetupHold *setup_hold, + float margin) const; + void writeEnvironment() const; + void writeOperatingConditions() const; + void writeWireload() const; + void writePinLoads() const; + void writePortLoads(Port *port) const; + void writePortFanout(Port *port) const; + void writeDriveResistances() const; + void writeDrivingCells() const; + void writeInputTransitions() const; + void writeDrivingCell(Port *port, + InputDriveCell *drive_cell, + const TransRiseFall *tr, + const MinMax *min_max) const; + void writeConstants() const; + virtual void writeConstant(Pin *pin) const; + const char *setConstantCmd(Pin *pin) const; + void writeCaseAnalysis() const; + virtual void writeCaseAnalysis(Pin *pin) const; + const char *caseAnalysisValueStr(Pin *pin) const; + void sortedLogicValuePins(LogicValueMap *value_map, + PinSeq &pins) const; + void writeNetResistances() const; + void writeNetResistance(Net *net, + 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; + void writeSlewLimits() const; + void writeCapLimits() const; + void writeCapLimits(const MinMax *min_max, + const char *cmd) const; + void writeMaxArea() const; + void writeFanoutLimits() const; + void writeFanoutLimits(const MinMax *min_max, + const char *cmd) const; + void writeLatchBorowLimits() const; + void writeDeratings() const; + void writeDerating(DeratingFactorsGlobal *factors) const; + void writeDerating(DeratingFactorsCell *factors, + WriteSdcObject *write_obj) const; + void writeDerating(DeratingFactors *factors, + TimingDerateType type, + const MinMax *early_late, + WriteSdcObject *write_obj) 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; + void writeCommentSeparator() const; + + void writeGetTimingArcsOfOjbects(LibertyCell *cell) const; + void writeGetTimingArcs(Edge *edge) const; + void writeGetTimingArcs(Edge *edge, + const char *filter) const; + const char *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; + virtual void writeGetPort(const Port *port) const; + virtual void writeGetPin(const Pin *pin) const; + virtual void writeGetNet(const Net *net) const; + virtual void writeGetInstance(const Instance *inst) const; + void writeGetPins(PinSet *pins) const; + void writeGetPins(PinSeq *pins) const; + void writeClockKey(const Clock *clk) const; + float scaleTime(float time) const; + float scaleCapacitance(float cap) const; + float scaleResistance(float res) const; + void writeFloat(float value) const; + void writeTime(float time) const; + void writeCapacitance(float cap) const; + 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, + RiseFallMinMax *values, + WriteSdcObject &write_object) const; + void writeRiseFallMinMaxCapCmd(const char *sdc_cmd, + RiseFallMinMax *values, + WriteSdcObject &write_object) const; + void writeRiseFallMinMaxCmd(const char *sdc_cmd, + RiseFallMinMax *values, + float scale, + WriteSdcObject &write_object) const; + void writeRiseFallMinMaxCmd(const char *sdc_cmd, + float value, + float scale, + const TransRiseFallBoth *tr, + 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 writeSetupHoldFlag(const MinMaxAll *min_max) const; + void writeVariables() const; + void writeCmdComment(SdcCmdComment *cmd) const; + + FILE *stream() const { return stream_; } + +protected: + Instance *instance_; + const char *filename_; + const char *creator_; + bool compatible_; + int digits_; + bool no_timestamp_; + bool top_instance_; + size_t instance_name_length_; + Cell *cell_; + FILE *stream_; + +private: + DISALLOW_COPY_AND_ASSIGN(WriteSdc); +}; + +} // namespace +#endif diff --git a/sdf/Makefile.am b/sdf/Makefile.am new file mode 100644 index 0000000..af3a394 --- /dev/null +++ b/sdf/Makefile.am @@ -0,0 +1,59 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +lib_LTLIBRARIES = libsdf.la + +include_HEADERS = \ + ReportAnnotation.hh \ + Sdf.hh \ + SdfReader.hh \ + SdfWriter.hh + +libsdf_la_SOURCES = \ + ReportAnnotation.cc \ + SdfReader.cc \ + SdfParse.yy \ + SdfLex.ll \ + SdfWriter.cc + +SdfLex.ll: SdfParse.hh + +SdfLex.cc: SdfLex.ll + $(LEX) $(LFLAGS) -o SdfLex.cc --prefix=SdfLex_ --header-file=SdfLex.hh SdfLex.ll + +# Rules to support automake pre 1.12 that name header .h instead of .hh +SdfParse.hh: SdfParse.cc + if test -f SdfParse.h; then \ + cp SdfParse.h SdfParse.hh; \ + fi + +TCL_SRCS = \ + Sdf.i \ + Sdf.tcl + +EXTRA_DIST = \ + $(TCL_SRCS) \ + SdfParse.hh + +MAINTAINERCLEANFILES = \ + SdfParse.hh \ + SdfParse.cc \ + SdfLex.cc + +libs: $(lib_LTLIBRARIES) + +xtags: $(SOURCES) $(HEADERS) + etags -a -o ../TAGS $(SOURCES) $(HEADERS) $(TCL_SRCS) diff --git a/sdf/ReportAnnotation.cc b/sdf/ReportAnnotation.cc new file mode 100644 index 0000000..a1d4908 --- /dev/null +++ b/sdf/ReportAnnotation.cc @@ -0,0 +1,579 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "StringUtil.hh" +#include "Report.hh" +#include "TimingRole.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Graph.hh" +#include "GraphCmp.hh" +#include "Sdc.hh" +#include "DcalcAnalysisPt.hh" +#include "ReportAnnotation.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); + void reportDelayAnnotation(); + void reportCheckAnnotation(); + +protected: + enum CountIndex { + count_internal_net = TimingRole::index_max, + count_input_net, + count_output_net, + count_index_max + }; + static int count_delay; + + void init(); + void findCounts(); + void findWidthPeriodCount(Pin *pin); + void reportDelayCounts(); + void reportCheckCounts(); + void reportArcs(); + void reportArcs(const char *header, + bool report_annotated, + PinSet &pins); + void reportArcs(Vertex *vertex, + bool report_annotated, + int &i); + void reportWidthPeriodArcs(Pin *pin, + bool report_annotated, + int &i); + void reportCount(const char *title, + int index, + int &total, + int &annotated_total); + void reportCheckCount(TimingRole *role, + int &total, + int &annotated_total); + int roleIndex(const TimingRole *role, + const Pin *from_pin, + const Pin *to_pin); + + int max_lines_; + bool list_annotated_; + bool list_unannotated_; + bool report_constant_arcs_; + + int edge_count_[count_index_max]; + int edge_annotated_count_[count_index_max]; + int edge_constant_count_[count_index_max]; + int edge_constant_annotated_count_[count_index_max]; + bool report_role_[count_index_max]; + PinSet unannotated_pins_; + PinSet annotated_pins_; + +private: + DISALLOW_COPY_AND_ASSIGN(ReportAnnotated); +}; + + +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) +{ + 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::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) : + StaState(sta), + max_lines_(max_lines), + list_annotated_(list_annotated), + list_unannotated_(list_unannotated), + report_constant_arcs_(report_constant_arcs) +{ + 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; +} + +void +ReportAnnotated::reportDelayAnnotation() +{ + findCounts(); + reportDelayCounts(); + reportArcs(); +} + +void +ReportAnnotated::reportDelayCounts() +{ + report_->print(" Not \n"); + report_->print("Delay type Total Annotated Annotated\n"); + report_->print("----------------------------------------------------------------\n"); + + 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_->print("----------------------------------------------------------------\n"); + report_->print("%-28s %10u %10u %10u\n", + " ", + 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) + +{ + 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::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) : + StaState(sta), + max_lines_(max_lines), + list_annotated_(list_annotated), + list_unannotated_(list_unannotated), + report_constant_arcs_(report_constant_arcs) +{ + init(); + report_role_[TimingRole::setup()->index()] = report_setup; + report_role_[TimingRole::hold()->index()] = report_hold; + report_role_[TimingRole::recovery()->index()] = report_recovery; + report_role_[TimingRole::removal()->index()] = report_removal; + report_role_[TimingRole::nochange()->index()] = report_nochange; + report_role_[TimingRole::width()->index()] = report_width; + report_role_[TimingRole::period()->index()] = report_period; + report_role_[TimingRole::skew()->index()] = report_max_skew; +} + +void +ReportAnnotated::reportCheckAnnotation() +{ + findCounts(); + reportCheckCounts(); + reportArcs(); +} + +void +ReportAnnotated::reportCheckCounts() +{ + report_->print(" Not \n"); + report_->print("Check type Total Annotated Annotated\n"); + report_->print("----------------------------------------------------------------\n"); + + int total = 0; + int annotated_total = 0; + reportCheckCount(TimingRole::setup(), total, annotated_total); + reportCheckCount(TimingRole::hold(), total, annotated_total); + reportCheckCount(TimingRole::recovery(), total, annotated_total); + reportCheckCount(TimingRole::removal(), total, annotated_total); + reportCheckCount(TimingRole::nochange(), total, annotated_total); + reportCheckCount(TimingRole::width(), total, annotated_total); + reportCheckCount(TimingRole::period(), total, annotated_total); + reportCheckCount(TimingRole::skew(), total, annotated_total); + + report_->print("----------------------------------------------------------------\n"); + report_->print("%-28s %10u %10u %10u\n", + " ", + total, + annotated_total, + total - annotated_total); +} + +void +ReportAnnotated::reportCheckCount(TimingRole *role, + int &total, + int &annotated_total) +{ + int index = role->index(); + if (edge_count_[index] > 0) { + const char *role_name = role->asString(); + string title; + stringPrint(title, "cell %s arcs", role_name); + reportCount(title.c_str(), index, total, annotated_total); + } +} + +//////////////////////////////////////////////////////////////// + +void +ReportAnnotated::init() +{ + count_delay = TimingRole::sdfIopath()->index(); + for (int i = 0; i < count_index_max; i++) { + edge_count_[i] = 0; + edge_annotated_count_[i] = 0; + edge_constant_count_[i] = 0; + edge_constant_annotated_count_[i] = 0; + report_role_[i] = false; + } +} + +void +ReportAnnotated::findCounts() +{ + 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); + VertexOutEdgeIterator edge_iter(from_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + const TimingRole *role = edge->role(); + Vertex *to_vertex = edge->to(graph_); + Pin *to_pin = to_vertex->pin(); + 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); + + edge_count_[index]++; + + if (from_logic_value_exists || to_logic_value_exists) + 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); + } + } + } + findWidthPeriodCount(from_pin); + } +} + +int +ReportAnnotated::roleIndex(const TimingRole *role, + const Pin *from_pin, + const Pin *to_pin) +{ + if (role == TimingRole::wire()) { + if (network_->isTopLevelPort(from_pin)) + return count_input_net; + else if (network_->isTopLevelPort(to_pin)) + return count_output_net; + else + return count_internal_net; + } + else if (role->sdfRole() == TimingRole::sdfIopath()) + return count_delay; + else { + if (role->isTimingCheck() + && (role == TimingRole::latchSetup() + || role == TimingRole::latchHold())) + role = role->genericRole(); + return role->index(); + } +} + +// Width and period checks are not edges in the graph so +// they require special handling. +void +ReportAnnotated::findWidthPeriodCount(Pin *pin) +{ + LibertyPort *port = network_->libertyPort(pin); + if (port) { + DcalcAPIndex ap_index = 0; + float value; + bool exists, annotated; + int period_index = TimingRole::period()->index(); + 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); + } + } + } + + int width_index = TimingRole::width()->index(); + if (report_role_[width_index]) { + for (auto hi_low : TransRiseFall::range()) { + port->minPulseWidth(hi_low, value, exists); + if (exists) { + edge_count_[width_index]++; + graph_->widthCheckAnnotation(pin, hi_low, ap_index, + value, annotated); + if (annotated) { + edge_annotated_count_[width_index]++; + if (list_annotated_) + annotated_pins_.insert(pin); + } + else { + if (list_unannotated_) + unannotated_pins_.insert(pin); + } + } + } + } + } +} + +void +ReportAnnotated::reportCount(const char *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_->print("%-28s %10u %10u %10u\n", + 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_->print("%-28s %10s %10u %10u\n", + "constant arcs", + "", + const_annotated_count, + const_count - const_annotated_count); + } + total += count; + annotated_total += annotated_count; + } +} + +void +ReportAnnotated::reportArcs() +{ + if (list_annotated_) + reportArcs("Annotated Arcs", true, annotated_pins_); + if (list_unannotated_) + reportArcs("Unannotated Arcs", false, unannotated_pins_); +} + +void +ReportAnnotated::reportArcs(const char *header, + bool report_annotated, + PinSet &pins) +{ + report_->print("\n"); + report_->print(header); + report_->print("\n"); + PinSeq sorted_pins; + sortPinSet(&pins, network_, sorted_pins); + int i = 0; + PinSeq::Iterator pin_iter(sorted_pins); + while (pin_iter.hasNext() + && (max_lines_ == 0 || i < max_lines_)) { + Pin *pin = pin_iter.next(); + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + reportArcs(vertex, report_annotated, i); + if (bidirect_drvr_vertex) + reportArcs(bidirect_drvr_vertex, report_annotated, i); + reportWidthPeriodArcs(pin, report_annotated, i); + } +} + +void +ReportAnnotated::reportArcs(Vertex *vertex, + 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_)) { + Edge *edge = edge_iter.next(); + 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 (role->isTimingCheck()) + role_name = role->asString(); + 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"; + } + else + role_name = "delay"; + report_->print(" %-18s %s -> %s", + role_name, + network_->pathName(from_pin), + network_->pathName(to_pin)); + const char *cond = edge->timingArcSet()->sdfCond(); + if (cond) + report_->print(" %s", cond); + report_->print("\n"); + i++; + } + } +} + +void +ReportAnnotated::reportWidthPeriodArcs(Pin *pin, + bool report_annotated, + int &i) +{ + LibertyPort *port = network_->libertyPort(pin); + if (port) { + DcalcAPIndex ap_index = 0; + float value; + bool exists, annotated; + int period_index = TimingRole::period()->index(); + 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_->print(" %-18s %s\n", + "period", + network_->pathName(pin)); + i++; + } + } + } + + int width_index = TimingRole::width()->index(); + if (report_role_[width_index] + && (max_lines_ == 0 || i < max_lines_)) { + bool report = false; + for (auto hi_low : TransRiseFall::range()) { + port->minPulseWidth(hi_low, value, exists); + if (exists) { + edge_count_[width_index]++; + graph_->widthCheckAnnotation(pin, hi_low, ap_index, + value, annotated); + report |= (annotated == report_annotated); + } + } + if (report) { + report_->print(" %-18s %s\n", + "min width", + network_->pathName(pin)); + i++; + } + } + } +} + +} // namespace diff --git a/sdf/ReportAnnotation.hh b/sdf/ReportAnnotation.hh new file mode 100644 index 0000000..9945be5 --- /dev/null +++ b/sdf/ReportAnnotation.hh @@ -0,0 +1,48 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_REPORT_ANNOTATION_H +#define STA_REPORT_ANNOTATION_H + +namespace sta { + +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); +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); + +} // namespace +#endif diff --git a/sdf/Sdf.hh b/sdf/Sdf.hh new file mode 100644 index 0000000..ce297ba --- /dev/null +++ b/sdf/Sdf.hh @@ -0,0 +1,225 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SDF_H +#define STA_SDF_H + +#include "DisallowCopyAssign.hh" +#include "Zlib.hh" +#include "Vector.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "LibertyClass.hh" +#include "NetworkClass.hh" +#include "GraphClass.hh" +#include "SdcClass.hh" +#include "StaState.hh" + +// Header for ReadSdf.cc to communicate with SdfLex.cc, SdfParse.cc + +// global namespace + +#define YY_INPUT(buf,result,max_size) \ + sta::sdf_reader->getChars(buf, result, max_size) +int +SdfParse_error(const char *msg); + +namespace sta { + +class Report; +class SdfTriple; +class SdfPortSpec; + +typedef Vector SdfTripleSeq; + +class SdfReader : public StaState +{ +public: + SdfReader(const char *filename, + const char *path, + int arc_min_index, + int triple_min_index, + int arc_max_index, + int triple_max_index, + AnalysisType analysis_type, + bool unescaped_dividers, + bool is_incremental_only, + MinMaxAll *cond_use, + StaState *sta); + ~SdfReader(); + bool read(); + // Arc/Triple index passed to read() to ignore arg. + static int nullIndex() { return null_index_; } + + void setDivider(char divider); + void setTimescale(float multiplier, const char *units); + void setPortDeviceDelay(Edge *edge, + SdfTripleSeq *triples, + bool from_trans); + void setEdgeArcDelays(Edge *edge, + TimingArc *arc, + SdfTriple *triple); + void setEdgeArcDelays(Edge *edge, + TimingArc *arc, + SdfTriple *triple, + int triple_index, + int arc_delay_index); + void setEdgeArcDelaysCondUse(Edge *edge, + 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 char *instance_name); + void setInstanceWildcard(); + void cellFinish(); + void setCell(const char *cell_name); + void interconnect(const char *from_pin_name, + const char *to_pin_name, + SdfTripleSeq *triples); + void iopath(SdfPortSpec *from_edge, + const char *to_port_name, + SdfTripleSeq *triples, + const char *cond, + bool condelse); + void timingCheck(TimingRole *role, + SdfPortSpec *data_edge, + SdfPortSpec *clk_edge, + SdfTriple *triple); + void timingCheckWidth(SdfPortSpec *edge, + SdfTriple *triple); + void timingCheckPeriod(SdfPortSpec *edge, + SdfTriple *triple); + void timingCheckSetupHold(SdfPortSpec *data_edge, + SdfPortSpec *clk_edge, + SdfTriple *setup_triple, + SdfTriple *hold_triple); + void timingCheckRecRem(SdfPortSpec *data_edge, + SdfPortSpec *clk_edge, + SdfTriple *rec_triple, + SdfTriple *rem_triple); + void timingCheckNochange(SdfPortSpec *data_edge, + SdfPortSpec *clk_edge, + SdfTriple *before_triple, + SdfTriple *after_triple); + void port(const char *to_pin_name, + SdfTripleSeq *triples); + void device(SdfTripleSeq *triples); + void device(const char *to_pin_name, + SdfTripleSeq *triples); + + SdfTriple *makeTriple(); + SdfTriple *makeTriple(float value); + SdfTriple *makeTriple(float *min, + float *typ, + float *max); + void deleteTriple(SdfTriple *triple); + SdfTripleSeq *makeTripleSeq(); + void deleteTripleSeq(SdfTripleSeq *triples); + SdfPortSpec *makePortSpec(Transition *tr, + const char *port, + const char *cond); + SdfPortSpec *makeCondPortSpec(char *cond_port); + const char *unescaped(const char *s); + // 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); + + // flex YY_INPUT yy_n_chars arg changed definition from int to size_t, + // so provide both forms. + void getChars(char *buf, + size_t &result, + size_t max_size); + void getChars(char *buf, + int &result, + size_t max_size); + void incrLine(); + const char *filename() { return filename_; } + int line() { return line_; } + void sdfError(const char *fmt, ...); + void notSupported(const char *feature); + +private: + DISALLOW_COPY_AND_ASSIGN(SdfReader); + int readSdfFile1(Network *network, + Graph *graph, + const char *filename); + Edge *findCheckEdge(Pin *from_pin, + Pin *to_pin, + TimingRole *sdf_role, + const char *cond_start, + const char *cond_end); + Edge *findWireEdge(Pin *from_pin, + Pin *to_pin); + bool condMatch(const char *sdf_cond, + const char *lib_cond); + void timingCheck1(TimingRole *role, + SdfPortSpec *data_edge, + SdfPortSpec *clk_edge, + SdfTriple *triple, + bool warn); + bool annotateCheckEdges(Pin *data_pin, + SdfPortSpec *data_edge, + Pin *clk_pin, + SdfPortSpec *clk_edge, + TimingRole *sdf_role, + SdfTriple *triple, + bool match_generic); + void deletePortSpec(SdfPortSpec *edge); + void portNotFound(const char *port_name); + Pin *findPin(const char *name); + Instance *findInstance(const char *name); + void setEdgeDelays(Edge *edge, + SdfTripleSeq *triples, + const char *sdf_cmd); + void setDevicePinDelays(Pin *to_pin, + SdfTripleSeq *triples); + + const char *filename_; + const char *path_; + // Which values to pull out of the sdf triples. + int triple_min_index_; + int triple_max_index_; + // Which arc delay value to deposit the sdf values into. + int arc_delay_min_index_; + int arc_delay_max_index_; + AnalysisType analysis_type_; + bool unescaped_dividers_; + bool is_incremental_only_; + MinMaxAll *cond_use_; + + int line_; + gzFile stream_; + char divider_; + char escape_; + Instance *instance_; + const char *cell_name_; + bool in_timing_check_; + bool in_incremental_; + float timescale_; + + static const int null_index_ = -1; +}; + +extern SdfReader *sdf_reader; + +} // namespace +#endif diff --git a/sdf/Sdf.i b/sdf/Sdf.i new file mode 100644 index 0000000..4687ebc --- /dev/null +++ b/sdf/Sdf.i @@ -0,0 +1,161 @@ +%module sdf + +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "SdfReader.hh" +#include "ReportAnnotation.hh" +#include "SdfWriter.hh" +#include "Search.hh" +#include "Sta.hh" + +using sta::Sta; +using sta::cmdLinkedNetwork; +using sta::AnalysisType; +using sta::MinMax; +using sta::MinMaxAllNull; +using sta::stringEq; +using sta::readSdfSingle; +using sta::readSdfMinMax; +using sta::reportAnnotatedDelay; +using sta::reportAnnotatedCheck; + +%} + +%inline %{ + +// min/max index is: +// sdf_min = 0 +// sdf_typ = 1 +// sdf_max = 2 + +// If unescaped_dividers is true path names do not have to escape +// hierarchy dividers when the path name is quoted. +// For example verilog "\mod1/mod2 " can be referenced as "mod1/mod2" +// instead of the correct "mod1\/mod2". + +// Read sdf_index value from sdf triples. +// Return true if successful. +bool +read_sdf_file_single(const char *filename, + const char *path, + Corner *corner, + int sdf_index, + AnalysisType analysis_type, + bool unescaped_dividers, + bool incremental_only, + MinMaxAllNull *cond_use) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + sta->ensureGraph(); + if (stringEq(path, "")) + path = NULL; + bool success = readSdfSingle(filename, path, corner, sdf_index, + analysis_type, unescaped_dividers, + incremental_only, cond_use, sta); + sta->search()->arrivalsInvalid(); + return success; +} + +// Read sdf_min_index and sdf_max_index values from sdf triples. +// Return true if successful. +bool +read_sdf_file_min_max(const char *filename, + const char *path, + Corner *corner, + int sdf_min_index, + int sdf_max_index, + AnalysisType analysis_type, + bool unescaped_dividers, + bool incremental_only, + MinMaxAllNull *cond_use) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + sta->ensureGraph(); + if (stringEq(path, "")) + path = NULL; + bool success = readSdfMinMax(filename, path, corner, sdf_min_index, + sdf_max_index, analysis_type, + unescaped_dividers, incremental_only, + cond_use, sta); + sta->search()->arrivalsInvalid(); + return success; +} + +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) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + sta->ensureGraph(); + reportAnnotatedDelay(report_cells, report_nets, + report_in_ports, report_out_ports, + max_lines, list_annotated, list_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) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + 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); +} + +void +write_sdf_cmd(char *filename, + Corner *corner, + char sdf_divider, + int digits, + bool gzip, + bool no_timestamp, + bool no_version) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + sta->writeSdf(filename, corner, sdf_divider, digits, gzip, + no_timestamp, no_version); +} + +%} // inline diff --git a/sdf/Sdf.tcl b/sdf/Sdf.tcl new file mode 100644 index 0000000..26dd75c --- /dev/null +++ b/sdf/Sdf.tcl @@ -0,0 +1,230 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +namespace eval sta { + +define_cmd_args "read_sdf" \ + {[-path path] [-corner corner_name]\ + [-analysis_type single|bc_wc|on_chip_variation]\ + [-type sdf_min|sdf_typ|sdf_max]\ + [-min_type sdf_min|sdf_typ|sdf_max]\ + [-max_type sdf_min|sdf_typ|sdf_max]\ + [-cond_use min|max|min_max]\ + [-unescaped_dividers] filename} + +proc_redirect read_sdf { + parse_key_args "read_sdf" args \ + keys {-path -corner -analysis_type -type -min_type -max_type -cond_use} \ + flags {-unescaped_dividers -incremental_only} + check_argc_eq1 "read_sdf" $args + set filename $args + set path "" + if [info exists keys(-path)] { + set path $keys(-path) + } + set corner [parse_corner keys] + 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 is an implicit set_operating_conditions + set_analysis_type_cmd $analysis_type + } else { + sta_error "-analysis_type must be single, bc_wc or on_chip_variation" + } + } + + set cond_use "NULL" + if [info exists keys(-cond_use)] { + set cond_use $keys(-cond_use) + if { $cond_use != "min" && $cond_use != "max" && $cond_use != "min_max" } { + sta_warn "-cond_use must be min, max or min_max." + set cond_use "NULL" + } + if { $cond_use == "min_max" \ + && { [operating_condition_analysis_type] == "single" }} { + sta_error "-cond_use min_max cannot be used with analysis type single." + } + } + + set unescaped_dividers [info exists flags(-unescaped_dividers)] + set analysis_type [operating_condition_analysis_type] + set incremental_only [info exists flags(-incremental_only)] + if { $analysis_type == "single" } { + # default sdf_max + set index 2 + if [info exists keys(-type)] { + set index [parse_sdf_index "-type" $keys(-type)] + } + if [info exists keys(-min_type)] { + sta_warn "-min_type ignored by analysis_type single." + } + if [info exists keys(-max_type)] { + sta_warn "-max_type ignored by analysis_type single." + } + read_sdf_file_single $filename $path $corner $index $analysis_type \ + $unescaped_dividers $incremental_only $cond_use + } elseif { $analysis_type == "bc_wc" \ + || $analysis_type == "on_chip_variation" } { + # default sdf_min, sdf_max + set min_index 0 + set max_index 2 + if [info exists keys(-min_type)] { + set min_index [parse_sdf_index "-min_type" $keys(-min_type)] + } + if [info exists keys(-max_type)] { + set max_index [parse_sdf_index "-max_type" $keys(-max_type)] + } + if [info exists keys(-type)] { + sta_warn "-type ignored by analysis_type $analysis_type." + } + read_sdf_file_min_max $filename $path $corner $min_index $max_index \ + $analysis_type $unescaped_dividers $incremental_only $cond_use + } +} + +proc parse_sdf_index { key index } { + if { $index == "sdf_min" } { + return 0 + } elseif { $index == "sdf_typ" } { + return 1 + } elseif { $index == "sdf_max" } { + return 2 + } else { + sta_error "$key must be sdf_min, sdf_typ, or sdf_max." + } +} + +################################################################ + +define_cmd_args "report_annotated_delay" \ + {[-cell] [-net] [-from_in_ports] [-to_out_ports] [-max_line lines]\ + [-list_annotated] [-list_not_annotated] [-constant_arcs]} + +proc_redirect report_annotated_delay { + parse_key_args "report_annotated_delay" args keys {-max_line} \ + flags {-cell -net -from_in_ports -to_out_ports -list_annotated \ + -list_not_annotated -constant_arcs} + if { [info exists flags(-cell)] || [info exists flags(-net)] \ + || [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)] + set report_out_nets [info exists flags(-to_out_ports)] + } else { + set report_cells 1 + set report_nets 1 + set report_in_nets 1 + set report_out_nets 1 + } + + set max_line 0 + if { [info exists keys(-max_line)] } { + set max_line $keys(-max_line) + check_positive_integer "-max_line" $max_line + } + + report_annotated_delay_cmd $report_cells $report_nets \ + $report_in_nets $report_out_nets \ + $max_line [info exists flags(-list_annotated)] \ + [info exists flags(-list_not_annotated)] \ + [info exists flags(-constant_arcs)] +} + +define_cmd_args "report_annotated_check" \ + {[-setup] [-hold] [-recovery] [-removal] [-nochange] [-width] [-period]\ + [-max_skew] [-max_line lines] [-list_annotated] [-list_not_annotated]\ + [-constant_arcs]} + +proc_redirect report_annotated_check { + parse_key_args "report_annotated_check" args keys {-max_line} \ + flags {-setup -hold -recovery -removal -nochange -width -period \ + -max_skew -list_annotated -list_not_annotated -constant_arcs} + 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)] } { + set report_setup [info exists flags(-setup)] + set report_hold [info exists flags(-hold)] + set report_recovery [info exists flags(-recovery)] + set report_removal [info exists flags(-removal)] + set report_nochange [info exists flags(-nochange)] + set report_width [info exists flags(-width)] + set report_period [info exists flags(-period)] + set report_max_skew [info exists flags(-max_skew)] + } else { + set report_setup 1 + set report_hold 1 + set report_recovery 1 + set report_removal 1 + set report_nochange 1 + set report_width 1 + set report_period 1 + set report_max_skew 1 + } + + set max_line 0 + if { [info exists keys(-max_line)] } { + set max_line $keys(-max_line) + check_positive_integer "-max_line" $max_line + } + + report_annotated_check_cmd $report_setup $report_hold \ + $report_recovery $report_removal $report_nochange \ + $report_width $report_period $report_max_skew \ + $max_line [info exists flags(-list_annotated)] \ + [info exists flags(-list_not_annotated)] \ + [info exists flags(-constant_arcs)] +} + +define_cmd_args "write_sdf" \ + {[-corner corner_name] [-divider /|.] [-digits digits]\ + [-gzip] [-no_timestamp] [-no_version] filename} + +proc_redirect write_sdf { + parse_key_args "write_sdf" args \ + keys {-corner -divider -digits -significant_digits} \ + flags {-gzip -no_timestamp -no_version} + check_argc_eq1 "write_sdf" $args + set corner [parse_corner keys] + set filename $args + set divider "/" + if [info exists keys(-divider)] { + set divider $keys(-divider) + if { !($divider == "/" || $divider == ".") } { + sta_error "SDF -divider must be / or ." + } + } + set digits 3 + if [info exists keys(-digits)] { + set digits $keys(-digits) + } + if [info exists keys(-significant_digits)] { + set digits $keys(-significant_digits) + } + check_positive_integer "-digits" $digits + + 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 $digits $gzip \ + $no_timestamp $no_version +} + +# sta namespace end +} diff --git a/sdf/SdfLex.ll b/sdf/SdfLex.ll new file mode 100644 index 0000000..a2ab281 --- /dev/null +++ b/sdf/SdfLex.ll @@ -0,0 +1,197 @@ +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Sdf.hh" +#include "SdfParse.hh" + +#define YY_NO_INPUT + +static std::string sdf_token; + +void +sdfFlushBuffer() +{ + YY_FLUSH_BUFFER; +} + +%} + +/* %option debug */ +%option noyywrap +%option nounput +%option never-interactive + +%x COMMENT +%x QUOTE +%x COND_EXPR + +ID ([A-Za-z_]|\\.)([A-Za-z0-9_\[\]]|\\.)* +HCHAR "."|"/" +BLANK [ \n\t\r\b] +EOL \r?\n + +%% + +"/*" { BEGIN COMMENT; } +{ + +"*/" { BEGIN INITIAL; } + +. +{EOL} { sta::sdf_reader->incrLine(); } + +<> { + SdfParse_error("unterminated comment"); + BEGIN(INITIAL); + yyterminate(); + } +} + +"\"" { BEGIN QUOTE; sdf_token.erase(); } +{ + +"\\". { sdf_token += yytext[1]; } + +"\"" { + BEGIN INITIAL; + SdfParse_lval.string = sta::stringCopy(sdf_token.c_str()); + return QSTRING; + } + +. { sdf_token += yytext[0]; } + +<> { + SdfParse_error("unterminated quoted string"); + BEGIN(INITIAL); + yyterminate(); + } +} + +"//"[^\n]*{EOL} { sta::sdf_reader->incrLine(); } + +("-"|"+")?([0-9]*)("."[0-9]+)?([eE]("-"|"+")?[0-9]+)? { + SdfParse_lval.number = static_cast(atof(yytext)); + return NUMBER; + } + +":"|"{"|"}"|"["|"]"|","|"*"|";"|"="|"-"|"+"|"|"|"("|")"|{HCHAR} { + return ((int) yytext[0]); + } + +ABSOLUTE { return ABSOLUTE; } +CELL { return CELL; } +CELLTYPE { return CELLTYPE; } +DATE { return DATE; } +DELAY { return DELAY; } +DELAYFILE { return DELAYFILE; } +DESIGN { return DESIGN; } +DEVICE { return DEVICE; } +DIVIDER { return DIVIDER; } +HOLD { return HOLD; } +INCREMENTAL|INCREMENT { return INCREMENTAL; } +INSTANCE { return INSTANCE; } +INTERCONNECT { return INTERCONNECT; } +IOPATH { return IOPATH; } +NOCHANGE { return NOCHANGE; } +PERIOD { return PERIOD; } +PORT { return PORT; } +PROCESS { return PROCESS; } +PROGRAM { return PROGRAM; } +RECOVERY { return RECOVERY; } +RECREM { return RECREM; } +REMOVAL { return REMOVAL; } +RETAIN { return RETAIN; } +SDFVERSION { return SDFVERSION; } +SETUP { return SETUP; } +SETUPHOLD { return SETUPHOLD; } +SKEW { return SKEW; } +TEMPERATURE { return TEMPERATURE; } +TIMESCALE { return TIMESCALE; } +TIMINGCHECK { return TIMINGCHECK; } +VENDOR { return VENDOR; } +VERSION { return PVERSION; } +VOLTAGE { return VOLTAGE; } +WIDTH { return WIDTH; } +negedge { return NEGEDGE; } +posedge { return POSEDGE; } + +CONDELSE { return CONDELSE; } + +COND { + BEGIN COND_EXPR; + sdf_token.erase(); + return COND; + } + +"("{BLANK}*IOPATH { + BEGIN INITIAL; + SdfParse_lval.string = sta::stringCopy(sdf_token.c_str()); + return EXPR_OPEN_IOPATH; + } + +"(" { + /* Timing check conditions don't allow parens, + * so use the paren as a marker for the end of the expr. + */ + if (sta::sdf_reader->inTimingCheck()) { + BEGIN INITIAL; + SdfParse_lval.string= sta::stringCopy(sdf_token.c_str()); + return EXPR_OPEN; + } + else + sdf_token += yytext[0]; + } + +{BLANK}+{ID}{BLANK}*")" { + /* (COND expr port) */ + if (sta::sdf_reader->inTimingCheck()) { + BEGIN INITIAL; + /* remove trailing ")" */ + yytext[strlen(yytext)-1] = '\0'; + sdf_token += yytext; + SdfParse_lval.string= sta::stringCopy(sdf_token.c_str()); + /* No way to pass expr and id separately, so pass them together. */ + return EXPR_ID_CLOSE; + } + else + sdf_token += yytext; + } + +{BLANK} {} + +. { sdf_token += yytext[0]; } + +{ID} { + SdfParse_lval.string = sta::stringCopy(sta::sdf_reader->unescaped(yytext)); + return ID; + } + +{ID}({HCHAR}{ID})* { + SdfParse_lval.string = sta::stringCopy(sta::sdf_reader->unescaped(yytext)); + return PATH; + } + +{EOL} { sta::sdf_reader->incrLine(); } + +{BLANK} { /* Ignore blanks. */ } + + /* Send out of bound characters to parser. */ +. { return ((int) yytext[0]); } + +%% diff --git a/sdf/SdfParse.yy b/sdf/SdfParse.yy new file mode 100644 index 0000000..043c305 --- /dev/null +++ b/sdf/SdfParse.yy @@ -0,0 +1,316 @@ +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "Sdf.hh" + +int SdfLex_lex(); +#define SdfParse_lex SdfLex_lex +// use yacc generated parser errors +#define YYERROR_VERBOSE + +%} + +// expected shift/reduce conflicts +%expect 4 + +%union { + char character; + char *string; + float number; + float *number_ptr; + sta::SdfTriple *triple; + sta::SdfTripleSeq *delval_list; + sta::SdfPortSpec *port_spec; + sta::Transition *transition; +} + +%token DELAYFILE SDFVERSION DESIGN DATE VENDOR PROGRAM PVERSION +%token DIVIDER VOLTAGE PROCESS TEMPERATURE TIMESCALE +%token CELL CELLTYPE INSTANCE DELAY ABSOLUTE INCREMENTAL +%token INTERCONNECT PORT DEVICE RETAIN +%token IOPATH TIMINGCHECK +%token SETUP HOLD SETUPHOLD RECOVERY REMOVAL RECREM WIDTH PERIOD SKEW NOCHANGE +%token POSEDGE NEGEDGE COND CONDELSE +%token QSTRING ID PATH NUMBER EXPR_OPEN_IOPATH EXPR_OPEN EXPR_ID_CLOSE + +%type NUMBER +%type number_opt +%type value triple +%type delval_list +%type QSTRING ID PATH path port_instance +%type EXPR_OPEN_IOPATH EXPR_OPEN EXPR_ID_CLOSE +%type port_spec port_tchk +%type port_transition +%type hchar + +%start file + +%{ +%} + +%% + +file: + '(' DELAYFILE header cells ')' {} +; + +header: + header_stmt +| header header_stmt +; + +// technically the ordering of these statements is fixed by the spec +header_stmt: + '(' SDFVERSION QSTRING ')' { sta::stringDelete($3); } +| '(' DESIGN QSTRING ')' { sta::stringDelete($3); } +| '(' DATE QSTRING ')' { sta::stringDelete($3); } +| '(' VENDOR QSTRING ')' { sta::stringDelete($3); } +| '(' PROGRAM QSTRING ')' { sta::stringDelete($3); } +| '(' PVERSION QSTRING ')' { sta::stringDelete($3); } +| '(' DIVIDER hchar ')' { sta::sdf_reader->setDivider($3); } +| '(' VOLTAGE triple ')' { sta::sdf_reader->deleteTriple($3); } +| '(' VOLTAGE NUMBER ')' +| '(' VOLTAGE ')' // Illegal SDF (from OC). +| '(' PROCESS QSTRING ')' { sta::stringDelete($3); } +| '(' PROCESS ')' // Illegal SDF (from OC). +| '(' TEMPERATURE NUMBER ')' +| '(' TEMPERATURE triple ')' { sta::sdf_reader->deleteTriple($3); } +| '(' TEMPERATURE ')' // Illegal SDF (from OC). +| '(' TIMESCALE NUMBER ID ')' + { sta::sdf_reader->setTimescale($3, $4); } +; + +hchar: + '/' + { $$ = '/'; } +| '.' + { $$ = '.'; } +; + +number_opt: { $$ = NULL; } +| NUMBER { $$ = new float($1); } +; + +cells: + cell +| cells cell +; + +cell: + '(' CELL celltype cell_instance timing_specs ')' + { sta::sdf_reader->cellFinish(); } +; + +celltype: + '(' CELLTYPE QSTRING ')' + { sta::sdf_reader->setCell($3); } +; + +cell_instance: + '(' INSTANCE ')' { sta::sdf_reader->setInstance(NULL); } +| '(' INSTANCE '*' ')' { sta::sdf_reader->setInstanceWildcard(); } +| '(' INSTANCE path ')' + { sta::sdf_reader->setInstance($3); } +; + +timing_specs: + /* empty */ +| timing_specs timing_spec +; + +timing_spec: + '(' DELAY deltypes ')' +| '(' TIMINGCHECK tchk_defs ')' +; + +deltypes: +| deltypes deltype +; + +deltype: + '(' ABSOLUTE + { sta::sdf_reader->setInIncremental(false); } + del_defs ')' +| '(' INCREMENTAL + { sta::sdf_reader->setInIncremental(true); } + del_defs ')' +; + +del_defs: +| del_defs del_def +; + +path: + ID +| PATH +; + +del_def: + '(' IOPATH port_spec port_instance retains delval_list ')' + { sta::sdf_reader->iopath($3, $4, $6, NULL, false); } +| '(' CONDELSE '(' IOPATH port_spec port_instance + retains delval_list ')' ')' + { sta::sdf_reader->iopath($5, $6, $8, NULL, true); } +| '(' COND EXPR_OPEN_IOPATH port_spec port_instance + retains delval_list ')' ')' + { sta::sdf_reader->iopath($4, $5, $7, $3, false); } +| '(' INTERCONNECT port_instance port_instance delval_list ')' + { sta::sdf_reader->interconnect($3, $4, $5); } +| '(' PORT port_instance delval_list ')' + { sta::sdf_reader->port($3, $4); } +| '(' DEVICE delval_list ')' + { sta::sdf_reader->device($3); } +| '(' DEVICE port_instance delval_list ')' + { sta::sdf_reader->device($3, $4); } +; + +retains: + /* empty */ +| retains retain +; + +retain: + '(' RETAIN delval_list ')' + { sta::sdf_reader->deleteTripleSeq($3); } +; + +delval_list: + value + { $$ = sta::sdf_reader->makeTripleSeq(); $$->push_back($1); } +| delval_list value + { $1->push_back($2); $$ = $1; } +; + +tchk_defs: +| tchk_defs tchk_def +; + +tchk_def: + '(' SETUP { sta::sdf_reader->setInTimingCheck(true); } + port_tchk port_tchk value ')' + { sta::sdf_reader->timingCheck(sta::TimingRole::setup(), $4, $5, $6); + sta::sdf_reader->setInTimingCheck(false); + } +| '(' HOLD { sta::sdf_reader->setInTimingCheck(true); } + port_tchk port_tchk value ')' + { sta::sdf_reader->timingCheck(sta::TimingRole::hold(), $4, $5, $6); + sta::sdf_reader->setInTimingCheck(false); + } +| '(' SETUPHOLD { sta::sdf_reader->setInTimingCheck(true); } + port_tchk port_tchk value value ')' + { sta::sdf_reader->timingCheckSetupHold($4, $5, $6, $7); + sta::sdf_reader->setInTimingCheck(false); + } +//| '(' SETUPHOLD port_spec port_spec value value scond? ccond? ')' +| '(' RECOVERY { sta::sdf_reader->setInTimingCheck(true); } + port_tchk port_tchk value ')' + { sta::sdf_reader->timingCheck(sta::TimingRole::recovery(),$4,$5,$6); + sta::sdf_reader->setInTimingCheck(false); + } +| '(' REMOVAL { sta::sdf_reader->setInTimingCheck(true); } + port_tchk port_tchk value ')' + { sta::sdf_reader->timingCheck(sta::TimingRole::removal(),$4,$5,$6); + sta::sdf_reader->setInTimingCheck(false); + } +| '(' RECREM { sta::sdf_reader->setInTimingCheck(true); } + port_tchk port_tchk value value ')' + { sta::sdf_reader->timingCheckRecRem($4, $5, $6, $7); + sta::sdf_reader->setInTimingCheck(false); + } +//| '(' RECREM port_spec port_spec value value scond? ccond? ')' +| '(' SKEW { sta::sdf_reader->setInTimingCheck(true); } + port_tchk port_tchk value ')' + // Sdf skew clk/ref are reversed from liberty. + { sta::sdf_reader->timingCheck(sta::TimingRole::skew(),$5,$4,$6); + sta::sdf_reader->setInTimingCheck(false); + } +| '(' WIDTH { sta::sdf_reader->setInTimingCheck(true); } + port_tchk value ')' + { sta::sdf_reader->timingCheckWidth($4, $5); + sta::sdf_reader->setInTimingCheck(false); + } +| '(' PERIOD { sta::sdf_reader->setInTimingCheck(true); } + port_tchk value ')' + { sta::sdf_reader->timingCheckPeriod($4, $5); + sta::sdf_reader->setInTimingCheck(false); + } +| '(' NOCHANGE { sta::sdf_reader->setInTimingCheck(true); } + port_tchk port_tchk value value ')' + { sta::sdf_reader->timingCheckNochange($4, $5, $6, $7); + sta::sdf_reader->setInTimingCheck(false); + } +; + +port_instance: + ID +| PATH +; + +port_spec: + ID + { $$=sta::sdf_reader->makePortSpec(sta::Transition::riseFall(),$1,NULL); } +| '(' port_transition ID ')' + { $$ = sta::sdf_reader->makePortSpec($2, $3, NULL); } +; + +port_transition: + POSEDGE { $$ = sta::Transition::rise(); } +| NEGEDGE { $$ = sta::Transition::fall(); } +; + +port_tchk: + port_spec +| '(' COND EXPR_ID_CLOSE + { $$ = sta::sdf_reader->makeCondPortSpec($3); } +| '(' COND EXPR_OPEN port_transition ID ')' ')' + { $$ = sta::sdf_reader->makePortSpec($4, $5, $3); } +; + +value: + '(' ')' + { + $$ = sta::sdf_reader->makeTriple(); + } +| '(' NUMBER ')' + { + $$ = sta::sdf_reader->makeTriple($2); + } +| '(' triple ')' { $$ = $2; } +; + +triple: + NUMBER ':' number_opt ':' number_opt + { + float *fp = new float($1); + $$ = sta::sdf_reader->makeTriple(fp, $3, $5); + } +| number_opt ':' NUMBER ':' number_opt + { + float *fp = new float($3); + $$ = sta::sdf_reader->makeTriple($1, fp, $5); + } +| number_opt ':' number_opt ':' NUMBER + { + float *fp = new float($5); + $$ = sta::sdf_reader->makeTriple($1, $3, fp); + } +; + +%% diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc new file mode 100644 index 0000000..b0fa545 --- /dev/null +++ b/sdf/SdfReader.cc @@ -0,0 +1,1108 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "Error.hh" +#include "Report.hh" +#include "MinMax.hh" +#include "TimingArc.hh" +#include "Network.hh" +#include "SdcNetwork.hh" +#include "Graph.hh" +#include "Corner.hh" +#include "DcalcAnalysisPt.hh" +#include "Sdf.hh" +#include "SdfReader.hh" + +extern int +SdfParse_parse(); + +namespace sta { + +class SdfTriple +{ +public: + SdfTriple(float *min, + float *typ, + float *max); + ~SdfTriple(); + float **values() { return values_; } + bool hasValue() const; + +private: + DISALLOW_COPY_AND_ASSIGN(SdfTriple); + + float *values_[3]; +}; + +class SdfPortSpec +{ +public: + SdfPortSpec(Transition *tr, + const char *port, + const char *cond = nullptr) : + tr_(tr), port_(port), cond_(cond) {} + ~SdfPortSpec() + { + stringDelete(port_); + stringDelete(cond_); + } + const char *port() const { return port_; } + Transition *transition() const { return tr_; } + const char *cond() const { return cond_; } + +private: + DISALLOW_COPY_AND_ASSIGN(SdfPortSpec); + + Transition *tr_; + const char *port_; + const char *cond_; // timing checks only +}; + +SdfReader *sdf_reader = nullptr; + +bool +readSdfSingle(const char *filename, + const char *path, + Corner *corner, + int sdf_index, + AnalysisType analysis_type, + bool unescaped_dividers, + bool incremental_only, + MinMaxAll *cond_use, + StaState *sta) +{ + int arc_index = corner->findDcalcAnalysisPt(MinMax::max())->index(); + SdfReader reader(filename, path, arc_index, sdf_index, + SdfReader::nullIndex(), SdfReader::nullIndex(), + analysis_type, unescaped_dividers, incremental_only, + cond_use, sta); + sdf_reader = &reader; + return reader.read(); +} + +bool +readSdfMinMax(const char *filename, + const char *path, + Corner *corner, + int sdf_min_index, + int sdf_max_index, + AnalysisType analysis_type, + 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, sdf_min_index, + arc_max_index, sdf_max_index, + analysis_type, unescaped_dividers, incremental_only, + cond_use, sta); + sdf_reader = &reader; + return reader.read(); +} + +SdfReader::SdfReader(const char *filename, + const char *path, + int arc_min_index, + int triple_min_index, + int arc_max_index, + int triple_max_index, + AnalysisType analysis_type, + bool unescaped_dividers, + bool is_incremental_only, + MinMaxAll *cond_use, + StaState *sta) : + StaState(sta), + filename_(filename), + path_(path), + triple_min_index_(triple_min_index), + triple_max_index_(triple_max_index), + 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), + line_(1), + escape_('\\'), + instance_(nullptr), + cell_name_(nullptr), + in_timing_check_(false), + in_incremental_(false), + timescale_(1.0E-9F) // default units of ns +{ + if (unescaped_dividers) + network_ = makeSdcNetwork(network_); +} + +SdfReader::~SdfReader() +{ + if (unescaped_dividers_) + delete network_; +} + +bool +SdfReader::read() +{ + // Use zlib to uncompress gzip'd files automagically. + stream_ = gzopen(filename_, "rb"); + if (stream_) { + // yyparse returns 0 on success. + bool success = (::SdfParse_parse() == 0); + gzclose(stream_); + return success; + } + else + throw FileNotReadable(filename_); +} + +void +SdfReader::setDivider(char divider) +{ + divider_ = divider; +} + +void +SdfReader::setTimescale(float multiplier, + const char *units) +{ + if (multiplier == 1.0 + || multiplier == 10.0 + || multiplier == 100.0) { + if (stringEq(units, "us")) + timescale_ = multiplier * 1E-6F; + else if (stringEq(units, "ns")) + timescale_ = multiplier * 1E-9F; + else if (stringEq(units, "ps")) + timescale_ = multiplier * 1E-12F; + else + sdfError("TIMESCALE units not us, ns, or ps.\n"); + } + else + sdfError("TIMESCALE multiplier not 1, 10, or 100.\n"); + stringDelete(units); +} + +void +SdfReader::interconnect(const char *from_pin_name, + const char *to_pin_name, + SdfTripleSeq *triples) +{ + // Ignore non-incremental annotations in incremental only mode. + if (!(is_incremental_only_ && !in_incremental_)) { + Pin *from_pin = findPin(from_pin_name); + Pin *to_pin = findPin(to_pin_name); + if (from_pin && to_pin) { + // Assume the pins are non-hierarchical and on the same net. + Edge *edge = findWireEdge(from_pin, to_pin); + if (edge) + 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("pin %s is a hierarchical pin.\n", from_pin_name); + if (to_is_hier) + sdfError("pin %s is a hierarchical pin.\n", to_pin_name); + } + else + sdfError("INTERCONNECT from %s to %s not found.\n", + from_pin_name, to_pin_name); + } + } + else { + if (from_pin == nullptr) + sdfError("pin %s not found.\n", from_pin_name); + if (to_pin == nullptr) + sdfError("pin %s not found.\n", to_pin_name); + } + } + stringDelete(from_pin_name); + stringDelete(to_pin_name); + deleteTripleSeq(triples); +} + +void +SdfReader::port(const char *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) + : network_->findPin(to_pin_name); + if (to_pin == nullptr) + sdfError("pin %s not found.\n", 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"); + } + } + } + stringDelete(to_pin_name); + deleteTripleSeq(triples); +} + +Edge * +SdfReader::findWireEdge(Pin *from_pin, + Pin *to_pin) +{ + Vertex *to_vertex, *to_vertex_bidirect_drvr; + graph_->pinVertices(to_pin, to_vertex, to_vertex_bidirect_drvr); + // Fanin < fanout, so search for driver from load. + VertexInEdgeIterator edge_iter(to_vertex, graph_); + 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()) + return edge; + } + return nullptr; +} + +void +SdfReader::setEdgeDelays(Edge *edge, + SdfTripleSeq *triples, + const char *sdf_cmd) +{ + // Rise/fall triples. + size_t triple_count = triples->size(); + if (triple_count == 1 + || triple_count == 2) { + TimingArcSet *arc_set = edge->timingArcSet(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + size_t triple_index; + if (triple_count == 1) + triple_index = 0; + else + triple_index = arc->toTrans()->sdfTripleIndex(); + SdfTriple *triple = (*triples)[triple_index]; + setEdgeArcDelays(edge, arc, triple); + } + } + else if (triple_count == 0) + sdfError("%s with no triples.\n", sdf_cmd); + else + sdfError("%s with more than 2 triples.\n", sdf_cmd); +} + +void +SdfReader::setCell(const char *cell_name) +{ + cell_name_ = cell_name; +} + +void +SdfReader::setInstance(const char *instance_name) +{ + if (instance_name) { + if (stringEq(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_ && !stringEqual(inst_cell_name, cell_name_)) + sdfError("instance %s cell %s does not match enclosing cell %s.\n", + instance_name, + inst_cell_name, + cell_name_); + } + } + stringDelete(instance_name); + } + else + instance_ = nullptr; +} + +void +SdfReader::setInstanceWildcard() +{ + notSupported("INSTANCE wildcards"); + instance_ = nullptr; +} + +void +SdfReader::cellFinish() +{ + stringDelete(cell_name_); + cell_name_ = nullptr; + instance_ = nullptr; +} + +void +SdfReader::iopath(SdfPortSpec *from_edge, + const char *to_port_name, + SdfTripleSeq *triples, + const char *cond, + bool condelse) +{ + if (instance_) { + const char *from_port_name = from_edge->port(); + Cell *cell = network_->cell(instance_); + Port *from_port = network_->findPort(cell, from_port_name); + Port *to_port = network_->findPort(cell, to_port_name); + if (from_port == nullptr) + portNotFound(from_port_name); + if (to_port == nullptr) + portNotFound(to_port_name); + if (from_port && to_port) { + Pin *from_pin = network_->findPin(instance_, from_port_name); + Pin *to_pin = network_->findPin(instance_, to_port_name); + // 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); + size_t triple_count = triples->size(); + bool matched = false; + // Fanin < fanout, so search for driver from load. + // Search for multiple matching edges because of + // tristate enable/disable. + VertexInEdgeIterator edge_iter(to_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + TimingArcSet *arc_set = edge->timingArcSet(); + const char *lib_cond = arc_set->sdfCond(); + const TimingRole *edge_role = arc_set->role(); + bool cond_use_flag = cond_use_ && cond && lib_cond == nullptr + && !(!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))) { + matched = true; + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + if ((from_edge->transition() == Transition::riseFall()) + || (arc->fromTrans() == from_edge->transition())) { + size_t triple_index = arc->toTrans()->sdfTripleIndex(); + SdfTriple *triple = nullptr; + if (triple_index < triple_count) + triple = (*triples)[triple_index]; + if (triple_count == 1) + triple = (*triples)[0]; + // Rules for matching when triple is missing not implemented. + // See SDF pg 3-17. + if (triple) { + if (cond_use_flag) + setEdgeArcDelaysCondUse(edge, arc, triple); + else + setEdgeArcDelays(edge, arc, triple); + } + } + } + } + } + if (!matched) + sdfError("cell %s IOPATH %s -> %s not found.\n", + network_->cellName(instance_), + from_port_name, + to_port_name); + } + } + } + deletePortSpec(from_edge); + stringDelete(to_port_name); + deleteTripleSeq(triples); + if (cond) + stringDelete(cond); +} + +void +SdfReader::timingCheck(TimingRole *role, SdfPortSpec *data_edge, + SdfPortSpec *clk_edge, SdfTriple *triple) +{ + timingCheck1(role, data_edge, clk_edge, triple, true); + deletePortSpec(data_edge); + deletePortSpec(clk_edge); + deleteTriple(triple); +} + +void +SdfReader::timingCheck1(TimingRole *role, + SdfPortSpec *data_edge, + SdfPortSpec *clk_edge, + SdfTriple *triple, + bool warn) +{ + // Ignore non-incremental annotations in incremental only mode. + if (!(is_incremental_only_ && !in_incremental_) + && instance_) { + const char *data_port_name = data_edge->port(); + const char *clk_port_name = clk_edge->port(); + Cell *cell = network_->cell(instance_); + Port *data_port = network_->findPort(cell, data_port_name); + Port *clk_port = network_->findPort(cell, clk_port_name); + if (data_port == nullptr && warn) + portNotFound(data_port_name); + if (clk_port == nullptr && warn) + portNotFound(clk_port_name); + if (data_port && clk_port) { + Pin *data_pin = network_->findPin(instance_, data_port_name); + Pin *clk_pin = network_->findPin(instance_, clk_port_name); + if (data_pin && clk_pin) { + // Hack to match PT; always use triple max value for check. + if (triple_min_index_ != null_index_ + && triple_max_index_ != null_index_) { + float **values = triple->values(); + float *value_min = values[triple_min_index_]; + 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()) + *value_min = *value_max; + else + *value_max = *value_min; + break; + case AnalysisType::ocv: + *value_min = *value_max; + break; + } + } + } + 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, + triple, true); + if (!matched + // Only warn when non-null values are present. + && triple->hasValue()) + sdfError("cell %s %s -> %s %s check not found.\n", + network_->cellName(instance_), + data_port_name, + clk_port_name, + role->asString()); + } + } + } +} + +// Return true if matched. +bool +SdfReader::annotateCheckEdges(Pin *data_pin, + SdfPortSpec *data_edge, + Pin *clk_pin, + SdfPortSpec *clk_edge, + TimingRole *sdf_role, + SdfTriple *triple, + bool match_generic) +{ + bool matched = false; + const char *cond_start = data_edge->cond(); + const char *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. + VertexInEdgeIterator edge_iter(to_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + 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); + if (((!match_generic && edge_role->sdfRole() == sdf_role) + || (match_generic + && edge_role->genericRole() == sdf_role->genericRole())) + && cond_matches) { + TimingArcSet *arc_set = edge->timingArcSet(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + if (((data_edge->transition() == Transition::riseFall()) + || (arc->toTrans() == data_edge->transition())) + && ((clk_edge->transition() == Transition::riseFall()) + || (arc->fromTrans() == clk_edge->transition()))) { + setEdgeArcDelays(edge, arc, triple); + } + } + matched = true; + } + } + } + return matched; +} + +void +SdfReader::timingCheckWidth(SdfPortSpec *edge, + SdfTriple *triple) +{ + // Ignore non-incremental annotations in incremental only mode. + if (!(is_incremental_only_ && !in_incremental_) + && instance_) { + const char *port_name = edge->port(); + Cell *cell = network_->cell(instance_); + Port *port = network_->findPort(cell, port_name); + if (port == nullptr) + portNotFound(port_name); + else { + Pin *pin = network_->findPin(instance_, port_name); + if (pin) { + const TransRiseFall *tr = edge->transition()->asRiseFall(); + float **values = triple->values(); + float *value_ptr = values[triple_min_index_]; + if (value_ptr) { + float value = *value_ptr; + graph_->setWidthCheckAnnotation(pin, tr, 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_->setWidthCheckAnnotation(pin, tr, arc_delay_max_index_, + value); + } + } + } + } + } + deletePortSpec(edge); + deleteTriple(triple); +} + +void +SdfReader::timingCheckPeriod(SdfPortSpec *edge, + SdfTriple *triple) +{ + // Ignore non-incremental annotations in incremental only mode. + if (!(is_incremental_only_ && !in_incremental_) + && instance_) { + const char *port_name = edge->port(); + Cell *cell = network_->cell(instance_); + Port *port = network_->findPort(cell, port_name); + if (port == nullptr) + portNotFound(port_name); + else { + // Edge specifier is ignored for period checks. + 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); + } + } + } + } + } + deletePortSpec(edge); + deleteTriple(triple); +} + +void +SdfReader::timingCheckSetupHold(SdfPortSpec *data_edge, + SdfPortSpec *clk_edge, + SdfTriple *setup_triple, + SdfTriple *hold_triple) +{ + timingCheck1(TimingRole::setup(), data_edge, clk_edge, setup_triple, true); + timingCheck1(TimingRole::hold(), data_edge, clk_edge, hold_triple, false); + deletePortSpec(data_edge); + deletePortSpec(clk_edge); + deleteTriple(setup_triple); + deleteTriple(hold_triple); +} + +void +SdfReader::timingCheckRecRem(SdfPortSpec *data_edge, + SdfPortSpec *clk_edge, + SdfTriple *rec_triple, + SdfTriple *rem_triple) +{ + timingCheck1(TimingRole::recovery(), data_edge, clk_edge, rec_triple, true); + timingCheck1(TimingRole::removal(), data_edge, clk_edge, rem_triple, false); + deletePortSpec(data_edge); + deletePortSpec(clk_edge); + deleteTriple(rec_triple); + deleteTriple(rem_triple); +} + +void +SdfReader::timingCheckNochange(SdfPortSpec *data_edge, + SdfPortSpec *clk_edge, + SdfTriple *before_triple, + SdfTriple *after_triple) +{ + notSupported("NOCHANGE"); + deletePortSpec(data_edge); + deletePortSpec(clk_edge); + deleteTriple(before_triple); + deleteTriple(after_triple); +} + +void +SdfReader::device(SdfTripleSeq *triples) +{ + // Ignore non-incremental annotations in incremental only mode. + if (!(is_incremental_only_ && !in_incremental_) + && instance_) { + InstancePinIterator *pin_iter = network_->pinIterator(instance_); + while (pin_iter->hasNext()) { + Pin *to_pin = pin_iter->next(); + setDevicePinDelays(to_pin, triples); + } + delete pin_iter; + } + deleteTripleSeq(triples); +} + +void +SdfReader::device(const char *to_port_name, + SdfTripleSeq *triples) +{ + // Ignore non-incremental annotations in incremental only mode. + if (!(is_incremental_only_ && !in_incremental_) + && instance_) { + Cell *cell = network_->cell(instance_); + Port *to_port = network_->findPort(cell, to_port_name); + if (to_port == nullptr) + portNotFound(to_port_name); + else { + Pin *to_pin = network_->findPin(instance_, to_port_name); + setDevicePinDelays(to_pin, triples); + } + } + stringDelete(to_port_name); + deleteTripleSeq(triples); +} + +void +SdfReader::setDevicePinDelays(Pin *to_pin, + SdfTripleSeq *triples) +{ + Vertex *vertex = graph_->pinDrvrVertex(to_pin); + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role()->sdfRole() == TimingRole::sdfIopath()) + setEdgeDelays(edge, triples, "DEVICE"); + } +} + +void +SdfReader::setEdgeArcDelays(Edge *edge, + 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_); +} + +void +SdfReader::setEdgeArcDelays(Edge *edge, + TimingArc *arc, + SdfTriple *triple, + int triple_index, + int arc_delay_index) +{ + if (triple_index != null_index_) { + float **values = triple->values(); + float *value_ptr = values[triple_index]; + if (value_ptr) { + ArcDelay delay; + if (in_incremental_) + delay = *value_ptr + graph_->arcDelay(edge, arc, arc_delay_index); + else + delay = *value_ptr; + graph_->setArcDelay(edge, arc, arc_delay_index, delay); + graph_->setArcDelayAnnotated(edge, arc, arc_delay_index, true); + edge->setDelayAnnotationIsIncremental(is_incremental_only_); + } + } +} + +void +SdfReader::setEdgeArcDelaysCondUse(Edge *edge, + TimingArc *arc, + SdfTriple *triple) +{ + float **values = triple->values(); + float *value_min = nullptr; + if (triple_min_index_ != null_index_) + value_min = values[triple_min_index_]; + float *value_max = nullptr; + if (triple_max_index_ != null_index_) + value_max = values[triple_max_index_]; + MinMax *min, *max; + if (cond_use_ == MinMaxAll::min()) { + min = MinMax::min(); + max = MinMax::min(); + } + else if (cond_use_ == MinMaxAll::max()) { + min = MinMax::max(); + max = MinMax::max(); + } + else { + min = MinMax::min(); + max = MinMax::max(); + } + setEdgeArcDelaysCondUse(edge, arc, value_min, triple_min_index_, + arc_delay_min_index_, min); + setEdgeArcDelaysCondUse(edge, arc, value_max, triple_max_index_, + 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) +{ + if (value + && triple_index != null_index_) { + ArcDelay delay(*value); + if (!is_incremental_only_ && in_incremental_) + delay = graph_->arcDelay(edge, arc, arc_delay_index) + *value; + else if (graph_->arcDelayAnnotated(edge, arc, arc_delay_index)) { + ArcDelay prev_value = graph_->arcDelay(edge, arc, arc_delay_index); + if (fuzzyGreater(prev_value, delay, min_max)) + delay = prev_value; + } + graph_->setArcDelay(edge, arc, arc_delay_index, delay); + graph_->setArcDelayAnnotated(edge, arc, arc_delay_index, true); + edge->setDelayAnnotationIsIncremental(is_incremental_only_); + } +} + +bool +SdfReader::condMatch(const char *sdf_cond, + const char *lib_cond) +{ + // If the sdf is not conditional it matches any library condition. + if (sdf_cond == nullptr) + return true; + else if (sdf_cond && lib_cond) { + // Match sdf_cond and lib_cond ignoring blanks. + const char *c1 = sdf_cond; + 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++; + if (ch1 != ch2) + return false; + } while (ch1 && ch2); + return (ch1 == '\0' && ch2 == '\0'); + } + else + return false; +} + +SdfPortSpec * +SdfReader::makePortSpec(Transition *tr, + const char *port, + const char *cond) +{ + return new SdfPortSpec(tr, port, cond); +} + +SdfPortSpec * +SdfReader::makeCondPortSpec(char *cond_port) +{ + char *cond = cond_port; + // Search from end to find port name because condition may contain spaces. + char *p = &cond_port[strlen(cond_port) - 1]; + // Trim trailing port spaces. + while (*p != '\0' && isspace(*p)) + p--; + p[1] = '\0'; + while (*p != '\0' && !isspace(*p)) + p--; + char *port = &p[1]; + // Trim trailing cond spaces. + while (*p != '\0' && isspace(*p)) + p--; + p[1] = '\0'; + SdfPortSpec *port_spec = makePortSpec(Transition::riseFall(), + stringCopy(port), + stringCopy(cond)); + stringDelete(cond_port); + return port_spec; +} + +void +SdfReader::deletePortSpec(SdfPortSpec *edge) +{ + delete edge; +} + +SdfTripleSeq * +SdfReader::makeTripleSeq() +{ + return new SdfTripleSeq; +} + +void +SdfReader::deleteTripleSeq(SdfTripleSeq *triples) +{ + SdfTripleSeq::Iterator iter(triples); + while (iter.hasNext()) { + SdfTriple *triple = iter.next(); + delete triple; + } + delete triples; +} + +SdfTriple * +SdfReader::makeTriple() +{ + return new SdfTriple(0, 0, 0); +} + +SdfTriple * +SdfReader::makeTriple(float value) +{ + value *= timescale_; + float *fp = new float(value); + return new SdfTriple(fp, fp, fp); +} + +SdfTriple * +SdfReader::makeTriple(float *min, + float *typ, + float *max) +{ + if (min) *min *= timescale_; + if (typ) *typ *= timescale_; + if (max) *max *= timescale_; + return new SdfTriple(min, typ, max); +} + +void +SdfReader::deleteTriple(SdfTriple *triple) +{ + delete triple; +} + +void +SdfReader::setInTimingCheck(bool in) +{ + in_timing_check_ = in; +} + +void +SdfReader::setInIncremental(bool incr) +{ + in_incremental_ = incr; +} + +const char * +SdfReader::unescaped(const char *token) +{ + char path_escape = network_->pathEscape(); + char path_divider = network_->pathDivider(); + char *unescaped = makeTmpString(strlen(token) + 1); + char *u = unescaped; + for (const char *s = token; *s ; s++) { + char ch = *s; + + if (ch == escape_) { + char next_ch = s[1]; + + if (next_ch == divider_) { + // Escaped divider. + // Translate sdf escape to network escape. + *u++ = path_escape; + // Translate sdf divider to network divider. + *u++ = path_divider; + } + else if (next_ch == '[' + || next_ch == ']' + || next_ch == escape_) { + // Escaped bus bracket or escape. + // Translate sdf escape to network escape. + *u++ = path_escape; + *u++ = next_ch; + } + else + // Escaped non-divider/bracket character. + *u++ = next_ch; + s++; + } + else if (ch == divider_) + // Translate sdf divider to network divider. + *u++ = path_divider; + else + // Just the normal noises. + *u++ = ch; + } + *u = '\0'; + return unescaped; +} + +void +SdfReader::incrLine() +{ + line_++; +} + +void +SdfReader::getChars(char *buf, + size_t &result, + size_t max_size) +{ + char *status = gzgets(stream_, buf, max_size); + if (status == Z_NULL) + result = 0; // YY_nullptr + else + result = strlen(buf); +} + +void +SdfReader::getChars(char *buf, + int &result, + size_t max_size) +{ + char *status = gzgets(stream_, buf, max_size); + if (status == Z_NULL) + result = 0; // YY_nullptr + else + result = strlen(buf); +} + +void +SdfReader::notSupported(const char *feature) +{ + sdfError("%s not supported.\n", feature); +} + +void +SdfReader::portNotFound(const char *port_name) +{ + sdfError("instance %s port %s not found.\n", + network_->pathName(instance_), + port_name); +} + +void +SdfReader::sdfError(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + report_->vfileError(filename_, line_, fmt, args); + va_end(args); +} + +Pin * +SdfReader::findPin(const char *name) +{ + if (path_) { + string path_name; + stringPrint(path_name, path_, divider_, name); + Pin *pin = network_->findPin(path_name.c_str()); + return pin; + } + else + return network_->findPin(name); +} + +Instance * +SdfReader::findInstance(const char *name) +{ + string inst_name = name; + if (path_) + stringPrint(inst_name, "%s%c%s", path_, divider_, name); + Instance *inst = network_->findInstance(inst_name.c_str()); + if (inst == nullptr) + sdfError("instance %s not found.\n", inst_name.c_str()); + return inst; +} + +//////////////////////////////////////////////////////////////// + +SdfTriple::SdfTriple(float *min, + float *typ, + float *max) +{ + values_[0] = min; + values_[1] = typ; + values_[2] = max; +} + +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]; + } +} + +bool +SdfTriple::hasValue() const +{ + return values_[0] || values_[1] || values_[2]; +} + +} // namespace + +// Global namespace + +void sdfFlushBuffer(); + +int +SdfParse_error(const char *msg) +{ + sta::sdf_reader->sdfError("%s.\n", msg); + sdfFlushBuffer(); + return 0; +} diff --git a/sdf/SdfReader.hh b/sdf/SdfReader.hh new file mode 100644 index 0000000..f7e4ee3 --- /dev/null +++ b/sdf/SdfReader.hh @@ -0,0 +1,80 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_READ_SDF_H +#define STA_READ_SDF_H + +namespace sta { + +class Report; +class MinMax; +class Network; +class Graph; +class Corner; + +// Sdf index is: +// sdf_min = 0 +// sdf_typ = 1 +// sdf_max = 2 +// +// If unescaped_dividers is true, path names in the SDF do not have to +// escape hierarchy dividers when the path name is escaped. For +// example, the escaped Verilog instance 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. +// +// 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. +// +// The cond_use option is used when the SDF file contains conditional +// delays and the library does not have conditional delay arcs. If +// cond_use is min, the minimum of all conditional delays is used. If +// cond_use is max, the maximum of all conditional delays is used. If +// cond_use is min_max and min_max operating conditions are in use, the +// minimum of the conditional delay values is used for minimum operating +// conditions and the maximum of the conditional delay values is used for +// maximum operating conditions. + +// Read sdf_index value from sdf triples. +bool +readSdfSingle(const char *filename, + const char *path, + Corner *corner, + int sdf_index, + AnalysisType analysis_type, + bool unescaped_dividers, + bool incremental_only, + MinMaxAll *cond_use, + StaState *sta); + +// Read sdf_min_index and sdf_max_index values from sdf triples. +bool +readSdfMinMax(const char *filename, + const char *path, + Corner *corner, + int sdf_min_index, + int sdf_max_index, + AnalysisType analysis_type, + bool unescaped_dividers, + bool incremental_only, + MinMaxAll *cond_use, + StaState *sta); + +} // namespace +#endif diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc new file mode 100644 index 0000000..5618d72 --- /dev/null +++ b/sdf/SdfWriter.cc @@ -0,0 +1,792 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include "Machine.hh" +#include "Zlib.hh" +#include "StaConfig.hh" // STA_VERSION +#include "Fuzzy.hh" +#include "StringUtil.hh" +#include "MinMaxValues.hh" +#include "Units.hh" +#include "TimingRole.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "Sdc.hh" +#include "Network.hh" +#include "Graph.hh" +#include "StaState.hh" +#include "Corner.hh" +#include "DcalcAnalysisPt.hh" +#include "GraphDelayCalc1.hh" +#include "PathAnalysisPt.hh" +#include "SdfWriter.hh" + +namespace sta { + +class SdfWriter : public StaState +{ +public: + SdfWriter(StaState *sta); + ~SdfWriter(); + void write(const char *filename, + Corner *corner, + char sdf_divider, + int digits, + bool gzip, + bool no_timestamp, + bool no_version); + +protected: + void writeHeader(LibertyLibrary *default_lib, + bool no_timestamp, + bool no_version); + void writeTrailer(); + void writeInterconnects(); + void writeInstInterconnects(Instance *inst); + void writeInterconnectFromPin(Pin *drvr_pin); + + void writeInstances(); + void writeInstHeader(const Instance *inst); + void writeInstTrailer(); + void writeIopaths(const Instance *inst, + bool &inst_header); + void writeIopathHeader(); + void writeIopathTrailer(); + void writeTimingChecks(const Instance *inst, + bool &inst_header); + void ensureTimingCheckheaders(bool &check_header, + const Instance *inst, + bool &inst_header); + void writeCheck(Edge *edge, + const char *sdf_check); + void writeCheck(Edge *edge, + TimingArc *arc, + const char *sdf_check, + bool use_data_edge, + bool use_clk_edge); + void writeEdgeCheck(Edge *edge, + const char *sdf_check, + int clk_tr_index, + TimingArc *arcs[TransRiseFall::index_count][TransRiseFall::index_count]); + void writeTimingCheckHeader(); + void writeTimingCheckTrailer(); + void writeWidthCheck(const Pin *pin, + const TransRiseFall *hi_low, + float min_width, + float max_width); + void writePeriodCheck(const Pin *pin, + float min_period); + const char *sdfEdge(const Transition *tr); + void writeArcDelays(Edge *edge); + void writeSdfTuple(RiseFallMinMax &delays, + TransRiseFall *tr); + void writeSdfTuple(float min_delay, + float max_delay); + void writeSdfDelay(double delay); + char *sdfPortName(const Pin *pin); + char *sdfPathName(const Pin *pin); + char *sdfPathName(const Instance *inst); + char *sdfName(const Instance *inst); + +private: + DISALLOW_COPY_AND_ASSIGN(SdfWriter); + + char sdf_divider_; + float timescale_; + + char sdf_escape_; + char network_escape_; + char *delay_format_; + + gzFile stream_; + const Corner *corner_; + int arc_delay_min_index_; + int arc_delay_max_index_; +}; + +void +writeSdf(const char *filename, + Corner *corner, + char sdf_divider, + int digits, + bool gzip, + bool no_timestamp, + bool no_version, + StaState *sta) +{ + SdfWriter writer(sta); + writer.write(filename, corner, sdf_divider, digits, gzip, + no_timestamp, no_version); +} + +SdfWriter::SdfWriter(StaState *sta) : + StaState(sta), + sdf_escape_('\\'), + network_escape_(network_->pathEscape()), + delay_format_(nullptr) +{ +} + +SdfWriter::~SdfWriter() +{ + stringDelete(delay_format_); +} + +void +SdfWriter::write(const char *filename, + Corner *corner, + char sdf_divider, + int digits, + bool gzip, + bool no_timestamp, + bool no_version) +{ + sdf_divider_ = sdf_divider; + if (delay_format_ == nullptr) + delay_format_ = new char[10]; + sprintf(delay_format_, "%%.%df", digits); + + LibertyLibrary *default_lib = network_->defaultLibertyLibrary(); + timescale_ = default_lib->units()->timeUnit()->scale(); + + corner_ = corner; + 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(); + + stream_ = gzopen(filename, gzip ? "wb" : "wT"); + + writeHeader(default_lib, no_timestamp, no_version); + writeInterconnects(); + writeInstances(); + writeTrailer(); + + gzclose(stream_); + stream_ = nullptr; +} + +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())); + + 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); + } + + gzprintf(stream_, " (VENDOR \"Parallax\")\n"); + gzprintf(stream_, " (PROGRAM \"STA\")\n"); + if (!no_version) + gzprintf(stream_, " (VERSION \"%s\")\n", STA_VERSION); + gzprintf(stream_, " (DIVIDER %c)\n", sdf_divider_); + + OperatingConditions *cond_min = + sdc_->operatingConditions(MinMax::min()); + if (cond_min == nullptr) + cond_min = default_lib->defaultOperatingConditions(); + OperatingConditions *cond_max = + sdc_->operatingConditions(MinMax::max()); + if (cond_max == nullptr) + cond_max = default_lib->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()); + } + + const char *sdf_timescale = nullptr; + if (fuzzyEqual(timescale_, 1e-6)) + sdf_timescale = "1us"; + else if (fuzzyEqual(timescale_, 10e-6)) + sdf_timescale = "10us"; + else if (fuzzyEqual(timescale_, 100e-6)) + sdf_timescale = "100us"; + else if (fuzzyEqual(timescale_, 1e-9)) + sdf_timescale = "1ns"; + else if (fuzzyEqual(timescale_, 10e-9)) + sdf_timescale = "10ns"; + else if (fuzzyEqual(timescale_, 100e-9)) + sdf_timescale = "100ns"; + else if (fuzzyEqual(timescale_, 1e-12)) + sdf_timescale = "1ps"; + else if (fuzzyEqual(timescale_, 10e-12)) + sdf_timescale = "10ps"; + else if (fuzzyEqual(timescale_, 100e-12)) + sdf_timescale = "100ps"; + if (sdf_timescale) + gzprintf(stream_, " (TIMESCALE %s)\n", sdf_timescale); +} + +void +SdfWriter::writeTrailer() +{ + gzprintf(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"); + + writeInstInterconnects(network_->topInstance()); + + LeafInstanceIterator *inst_iter = network_->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + writeInstInterconnects(inst); + } + delete inst_iter; + + gzprintf(stream_, " )\n"); + gzprintf(stream_, " )\n"); + gzprintf(stream_, " )\n"); +} + +void +SdfWriter::writeInstInterconnects(Instance *inst) +{ + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (network_->isDriver(pin)) + writeInterconnectFromPin(pin); + } + delete pin_iter; +} + +void +SdfWriter::writeInterconnectFromPin(Pin *drvr_pin) +{ + Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin); + VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->isWire()) { + Pin *load_pin = edge->to(graph_)->pin(); + gzprintf(stream_, " (INTERCONNECT %s %s ", + sdfPathName(drvr_pin), + sdfPathName(load_pin)); + writeArcDelays(edge); + gzprintf(stream_, ")\n"); + } + } +} + +void +SdfWriter::writeInstances() +{ + LeafInstanceIterator *leaf_iter = network_->leafInstanceIterator(); + while (leaf_iter->hasNext()) { + const Instance *inst = leaf_iter->next(); + bool inst_header = false; + writeIopaths(inst, inst_header); + writeTimingChecks(inst, inst_header); + if (inst_header) + writeInstTrailer(); + } + delete leaf_iter; +} + +void +SdfWriter::writeInstHeader(const Instance *inst) +{ + gzprintf(stream_, " (CELL\n"); + gzprintf(stream_, " (CELLTYPE \"%s\")\n", network_->cellName(inst)); + gzprintf(stream_, " (INSTANCE %s)\n", sdfPathName(inst)); +} + +void +SdfWriter::writeInstTrailer() +{ + gzprintf(stream_, " )\n"); +} + +void +SdfWriter::writeIopaths(const Instance *inst, + bool &inst_header) +{ + bool iopath_header = false; + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *from_pin = pin_iter->next(); + if (network_->isLoad(from_pin)) { + Vertex *from_vertex = graph_->pinLoadVertex(from_pin); + VertexOutEdgeIterator edge_iter(from_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + 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_, " "); + } + gzprintf(stream_, " (IOPATH %s %s ", + sdfPortName(from_pin), + sdfPortName(to_pin)); + writeArcDelays(edge); + if (sdf_cond) + gzprintf(stream_, ")"); + gzprintf(stream_, ")\n"); + } + } + } + } + delete pin_iter; + + if (iopath_header) + writeIopathTrailer(); +} + +void +SdfWriter::writeIopathHeader() +{ + gzprintf(stream_, " (DELAY\n"); + gzprintf(stream_, " (ABSOLUTE\n"); +} + +void +SdfWriter::writeIopathTrailer() +{ + gzprintf(stream_, " )\n"); + gzprintf(stream_, " )\n"); +} + +void +SdfWriter::writeArcDelays(Edge *edge) +{ + RiseFallMinMax delays; + TimingArcSet *arc_set = edge->timingArcSet(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + TransRiseFall *tr = arc->toTrans()->asRiseFall(); + ArcDelay min_delay = graph_->arcDelay(edge, arc, arc_delay_min_index_); + delays.setValue(tr, MinMax::min(), delayAsFloat(min_delay)); + + ArcDelay max_delay = graph_->arcDelay(edge, arc, arc_delay_max_index_); + delays.setValue(tr, MinMax::max(), delayAsFloat(max_delay)); + } + + if (delays.hasValue(TransRiseFall::rise(), MinMax::min()) + && delays.hasValue(TransRiseFall::fall(), MinMax::min())) { + // Rise and fall. + writeSdfTuple(delays, TransRiseFall::rise()); + // Merge rise/fall values if they are the same. + if (!(fuzzyEqual(delays.value(TransRiseFall::rise(), MinMax::min()), + delays.value(TransRiseFall::fall(), MinMax::min())) + && fuzzyEqual(delays.value(TransRiseFall::rise(), MinMax::max()), + delays.value(TransRiseFall::fall(),MinMax::max())))) { + gzprintf(stream_, " "); + writeSdfTuple(delays, TransRiseFall::fall()); + } + } + else if (delays.hasValue(TransRiseFall::rise(), MinMax::min())) + // Rise only. + writeSdfTuple(delays, TransRiseFall::rise()); + else if (delays.hasValue(TransRiseFall::fall(), MinMax::min())) { + // Fall only. + gzprintf(stream_, "() "); + writeSdfTuple(delays, TransRiseFall::fall()); + } +} + +void +SdfWriter::writeSdfTuple(RiseFallMinMax &delays, + TransRiseFall *tr) +{ + gzprintf(stream_, "("); + writeSdfDelay(delays.value(tr, MinMax::min())); + gzprintf(stream_, "::"); + writeSdfDelay(delays.value(tr, MinMax::max())); + gzprintf(stream_, ")"); +} + +void +SdfWriter::writeSdfTuple(float min_delay, + float max_delay) +{ + gzprintf(stream_, "("); + writeSdfDelay(min_delay); + gzprintf(stream_, "::"); + writeSdfDelay(max_delay); + gzprintf(stream_, ")"); +} + +void +SdfWriter::writeSdfDelay(double delay) +{ + gzprintf(stream_, delay_format_, delay / timescale_); +} + +void +SdfWriter::writeTimingChecks(const Instance *inst, + bool &inst_header) +{ + bool check_header = false; + + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + Vertex *vertex = graph_->pinLoadVertex(pin); + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + 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 : TransRiseFall::range()) { + float min_width, max_width; + bool exists; + graph_delay_calc_->minPulseWidth(pin, hi_low, arc_delay_min_index_, + MinMax::min(), + min_width, exists); + graph_delay_calc_->minPulseWidth(pin, hi_low, arc_delay_max_index_, + MinMax::max(), + max_width, exists); + if (exists) { + ensureTimingCheckheaders(check_header, inst, inst_header); + writeWidthCheck(pin, hi_low, min_width, max_width); + } + } + float min_period; + bool exists; + graph_delay_calc_->minPeriod(pin, min_period, exists); + if (exists) { + ensureTimingCheckheaders(check_header, inst, inst_header); + writePeriodCheck(pin, min_period); + } + } + delete pin_iter; + + if (check_header) + writeTimingCheckTrailer(); +} + +void +SdfWriter::ensureTimingCheckheaders(bool &check_header, + const Instance *inst, + bool &inst_header) +{ + if (!inst_header) { + writeInstHeader(inst); + inst_header = true; + } + if (!check_header) { + writeTimingCheckHeader(); + check_header = true; + } +} + +void +SdfWriter::writeTimingCheckHeader() +{ + gzprintf(stream_, " (TIMINGCHECK\n"); +} + +void +SdfWriter::writeTimingCheckTrailer() +{ + gzprintf(stream_, " )\n"); +} + +void +SdfWriter::writeCheck(Edge *edge, + const char *sdf_check) +{ + TimingArcSet *arc_set = edge->timingArcSet(); + // Examine the arcs to see if the check requires clk or data edge specifiers. + TimingArc *arcs[TransRiseFall::index_count][TransRiseFall::index_count] = + {{nullptr, nullptr}, {nullptr, nullptr}}; + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + TransRiseFall *clk_tr = arc->fromTrans()->asRiseFall(); + TransRiseFall *data_tr = arc->toTrans()->asRiseFall();; + arcs[clk_tr->index()][data_tr->index()] = arc; + } + + if (arcs[TransRiseFall::fallIndex()][TransRiseFall::riseIndex()] == nullptr + && arcs[TransRiseFall::fallIndex()][TransRiseFall::fallIndex()] == nullptr) + writeEdgeCheck(edge, sdf_check, TransRiseFall::riseIndex(), arcs); + else if (arcs[TransRiseFall::riseIndex()][TransRiseFall::riseIndex()] == nullptr + && arcs[TransRiseFall::riseIndex()][TransRiseFall::fallIndex()] == nullptr) + writeEdgeCheck(edge, sdf_check, TransRiseFall::fallIndex(), arcs); + else { + // No special case; write all the checks with data and clock edge specifiers. + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + writeCheck(edge, arc, sdf_check, true, true); + } + } +} + +void +SdfWriter::writeEdgeCheck(Edge *edge, + const char *sdf_check, + int clk_tr_index, + TimingArc *arcs[TransRiseFall::index_count][TransRiseFall::index_count]) +{ + // SDF requires edge specifiers on the data port to define separate + // rise/fall check values. + // Check the rise/fall margins to see if they are the same to avoid adding + // data port edge specifiers if they aren't necessary. + if (arcs[clk_tr_index][TransRiseFall::riseIndex()] + && arcs[clk_tr_index][TransRiseFall::fallIndex()] + && arcs[clk_tr_index][TransRiseFall::riseIndex()] + && arcs[clk_tr_index][TransRiseFall::fallIndex()] + && fuzzyEqual(graph_->arcDelay(edge, + arcs[clk_tr_index][TransRiseFall::riseIndex()], + arc_delay_min_index_), + graph_->arcDelay(edge, + arcs[clk_tr_index][TransRiseFall::fallIndex()], + arc_delay_min_index_)) + && fuzzyEqual(graph_->arcDelay(edge, + arcs[clk_tr_index][TransRiseFall::riseIndex()], + arc_delay_max_index_), + graph_->arcDelay(edge, + arcs[clk_tr_index][TransRiseFall::fallIndex()], + arc_delay_max_index_))) + // Rise/fall margins are the same, so no data edge specifier is required. + writeCheck(edge, arcs[clk_tr_index][TransRiseFall::riseIndex()], + sdf_check, false, true); + else { + if (arcs[clk_tr_index][TransRiseFall::riseIndex()]) + writeCheck(edge, arcs[clk_tr_index][TransRiseFall::riseIndex()], + sdf_check, true, true); + if (arcs[clk_tr_index][TransRiseFall::fallIndex()]) + writeCheck(edge, arcs[clk_tr_index][TransRiseFall::fallIndex()], + sdf_check, true, true); + } +} + +void +SdfWriter::writeCheck(Edge *edge, + TimingArc *arc, + const char *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(); + + gzprintf(stream_, " (%s ", sdf_check); + + if (sdf_cond_start) + gzprintf(stream_, "(COND %s ", sdf_cond_start); + + if (use_data_edge) + gzprintf(stream_, "(%s %s)", + sdfEdge(arc->toTrans()), + sdfPortName(to_pin)); + else + gzprintf(stream_, "%s", sdfPortName(to_pin)); + + if (sdf_cond_start) + gzprintf(stream_, ")"); + + gzprintf(stream_, " "); + + if (sdf_cond_end) + gzprintf(stream_, "(COND %s ", sdf_cond_end); + + if (use_clk_edge) + gzprintf(stream_, "(%s %s)", + sdfEdge(arc->fromTrans()), + sdfPortName(from_pin)); + else + gzprintf(stream_, "%s", sdfPortName(from_pin)); + + if (sdf_cond_end) + gzprintf(stream_, ")"); + + gzprintf(stream_, " "); + + ArcDelay min_delay = graph_->arcDelay(edge, arc, arc_delay_min_index_); + ArcDelay max_delay = graph_->arcDelay(edge, arc, arc_delay_max_index_); + writeSdfTuple(delayAsFloat(min_delay), delayAsFloat(max_delay)); + + gzprintf(stream_, ")\n"); +} + +void +SdfWriter::writeWidthCheck(const Pin *pin, + const TransRiseFall *hi_low, + float min_width, + float max_width) +{ + gzprintf(stream_, " (WIDTH (%s %s) ", + sdfEdge(hi_low->asTransition()), + sdfPortName(pin)); + writeSdfTuple(min_width, max_width); + gzprintf(stream_, ")\n"); +} + +void +SdfWriter::writePeriodCheck(const Pin *pin, + float min_period) +{ + gzprintf(stream_, " (PERIOD %s ", + sdfPortName(pin)); + writeSdfTuple(min_period, min_period); + gzprintf(stream_, ")\n"); +} + +const char * +SdfWriter::sdfEdge(const Transition *tr) +{ + if (tr == Transition::rise()) + return "posedge"; + else if (tr == Transition::fall()) + return "negedge"; + return nullptr; +} + +//////////////////////////////////////////////////////////////// + +char * +SdfWriter::sdfPortName(const Pin *pin) +{ + return const_cast(network_->portName(pin)); +} + +char * +SdfWriter::sdfPathName(const Pin *pin) +{ + Instance *inst = network_->instance(pin); + if (network_->isTopInstance(inst)) + return sdfPortName(pin); + else { + char *inst_path = sdfPathName(inst); + const char *port_name = sdfPortName(pin); + char *sdf_name = makeTmpString(strlen(inst_path)+1+strlen(port_name)+1); + sprintf(sdf_name, "%s%c%s", inst_path, sdf_divider_, port_name); + return sdf_name; + } +} + +// Based on Network::pathName. +char * +SdfWriter::sdfPathName(const Instance *instance) +{ + ConstInstanceSeq inst_path; + network_->path(instance, inst_path); + size_t name_length = 0; + ConstInstanceSeq::Iterator path_iter1(inst_path); + while (path_iter1.hasNext()) { + const Instance *inst = path_iter1.next(); + name_length += strlen(sdfName(inst)) + 1; + } + char *path_name = makeTmpString(name_length); + char *path_ptr = path_name; + // Top instance has null string name, so terminate the string here. + *path_name = '\0'; + while (inst_path.size()) { + const Instance *inst = inst_path.back(); + const char *inst_name = sdfName(inst); + strcpy(path_ptr, inst_name); + path_ptr += strlen(inst_name); + inst_path.pop_back(); + if (inst_path.size()) + *path_ptr++ = sdf_divider_; + *path_ptr = '\0'; + } + return path_name; +} + +// Escape for non-alpha numeric characters. +char * +SdfWriter::sdfName(const Instance *inst) +{ + const char *name = network_->name(inst); + char *sdf_name = makeTmpString(strlen(name) * 2 + 1); + const char *p = name; + char *s = sdf_name; + while (*p) { + char ch = *p; + // Ignore sta escapes. + if (ch != network_escape_) { + if (!(isalnum(ch) || ch == '_')) + // Insert escape. + *s++ = sdf_escape_; + *s++ = ch; + } + p++; + } + *s = '\0'; + return sdf_name; +} + +} // namespace diff --git a/sdf/SdfWriter.hh b/sdf/SdfWriter.hh new file mode 100644 index 0000000..e512e8c --- /dev/null +++ b/sdf/SdfWriter.hh @@ -0,0 +1,35 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SDF_WRITER_H +#define STA_SDF_WRITER_H + +namespace sta { + +class StaState; + +void +writeSdf(const char *filename, + Corner *corner, + char sdf_divider, + int digits, + bool gzip, + bool no_timestamp, + bool no_version, + StaState *sta); + +} // namespace +#endif diff --git a/search/Bfs.cc b/search/Bfs.cc new file mode 100644 index 0000000..0ffdb33 --- /dev/null +++ b/search/Bfs.cc @@ -0,0 +1,461 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "Report.hh" +#include "Debug.hh" +#include "Mutex.hh" +#include "ThreadForEach.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Levelize.hh" +#include "Sdc.hh" +#include "SearchPred.hh" +#include "Bfs.hh" + +namespace sta { + +BfsIterator::BfsIterator(BfsIndex bfs_index, + Level level_min, + Level level_max, + SearchPred *search_pred, + StaState *sta) : + StaState(sta), + bfs_index_(bfs_index), + level_min_(level_min), + level_max_(level_max), + search_pred_(search_pred) +{ + init(); +} + +void +BfsIterator::init() +{ + first_level_ = level_max_; + last_level_ = level_min_; + ensureSize(); +} + +void +BfsIterator::ensureSize() +{ + if (levelize_->levelized()) { + unsigned max_level_1 = levelize_->maxLevel() + 1; + if (queue_.size() < max_level_1) + queue_.resize(max_level_1); + } +} + +BfsIterator::~BfsIterator() +{ +} + +void +BfsIterator::clear() +{ + Level level = first_level_; + while (levelLessOrEqual(level, last_level_)) { + VertexSeq &level_vertices = queue_[level]; + for (auto vertex : level_vertices) { + if (vertex) + vertex->setBfsInQueue(bfs_index_, false); + } + level_vertices.clear(); + incrLevel(level); + } + init(); +} + +void +BfsIterator::reportEntries(const Network *network) +{ + Level level = first_level_; + while (levelLessOrEqual(level, last_level_)) { + VertexSeq &level_vertices = queue_[level]; + if (!level_vertices.empty()) { + printf("Level %d\n", level); + for (auto vertex : level_vertices) { + if (vertex) + printf(" %s\n", vertex->name(network)); + } + } + incrLevel(level); + } +} + +void +BfsIterator::deleteEntries(Level level) +{ + VertexSeq &level_vertices = queue_[level]; + for (auto vertex : level_vertices) { + if (vertex) + vertex->setBfsInQueue(bfs_index_, false); + } + level_vertices.clear(); +} + +bool +BfsIterator::empty() const +{ + return levelLess(last_level_, first_level_); +} + +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_); +} + +void +BfsIterator::enqueueAdjacentVertices(Vertex *vertex, + Level to_level) +{ + enqueueAdjacentVertices(vertex, search_pred_, to_level); +} + +int +BfsIterator::visit(Level to_level, + VertexVisitor *visitor) +{ + int visit_count = 0; + while (levelLessOrEqual(first_level_, last_level_) + && levelLessOrEqual(first_level_, to_level)) { + VertexSeq &level_vertices = queue_[first_level_]; + incrLevel(first_level_); + if (!level_vertices.empty()) { + for (auto vertex : level_vertices) { + if (vertex) { + vertex->setBfsInQueue(bfs_index_, false); + visitor->visit(vertex); + visit_count++; + } + } + level_vertices.clear(); + visitor->levelFinished(); + } + } + return visit_count; +} + +// VertexSeq::Iterator that filters null objects, +// and pops objects so the vector does not need to be cleared. +class QueueIterator : Iterator +{ +public: + QueueIterator(VertexSeq &vertices, + BfsIndex bfs_index); + virtual bool hasNext(); + virtual Vertex *next(); + unsigned count() { return count_; } + +private: + VertexSeq &vertices_; + BfsIndex bfs_index_; + unsigned count_; +}; + +QueueIterator::QueueIterator(VertexSeq &vertices, + BfsIndex bfs_index) : + vertices_(vertices), + bfs_index_(bfs_index), + count_(0) +{ +} + +bool +QueueIterator::hasNext() +{ + Vertex *next = nullptr; + while (!vertices_.empty() + && (next = vertices_.back()) == nullptr) + vertices_.pop_back(); + return next != nullptr; +} + +Vertex * +QueueIterator::next() +{ + Vertex *next = vertices_.back(); + next->setBfsInQueue(bfs_index_, false); + vertices_.pop_back(); + count_++; + return next; +} + +int +BfsIterator::visitParallel(Level to_level, + VertexVisitor *visitor) +{ + int visit_count = 0; + if (!empty()) { + if (thread_count_ <= 1) + visit_count = visit(to_level, visitor); + else { + std::mutex lock; + Level level = first_level_; + while (levelLessOrEqual(level, last_level_) + && levelLessOrEqual(level, to_level)) { + VertexSeq &level_vertices = queue_[level]; + if (!level_vertices.empty()) { + incrLevel(first_level_); + QueueIterator iter(level_vertices, bfs_index_); + std::vector threads; + + for (int i = 0; i < thread_count_; i++) { + ForEachArg arg(&iter, lock, + visitor->copy()); + // Missing check for null vertex. + threads.push_back(std::thread(forEachBegin, arg)); + } + + // Wait for all threads working on this level before moving on. + for (auto &thread : threads) + thread.join(); + + visit_count += iter.count(); + visitor->levelFinished(); + level = first_level_; + } + else { + incrLevel(first_level_); + level = first_level_; + } + } + } + } + return visit_count; +} + +bool +BfsIterator::hasNext() +{ + return hasNext(last_level_); +} + +bool +BfsIterator::hasNext(Level to_level) +{ + findNext(to_level); + return levelLessOrEqual(first_level_, last_level_) + && !queue_[first_level_].empty(); +} + +Vertex * +BfsIterator::next() +{ + VertexSeq &level_vertices = queue_[first_level_]; + Vertex *vertex = level_vertices.back(); + level_vertices.pop_back(); + vertex->setBfsInQueue(bfs_index_, false); + return vertex; +} + +void +BfsIterator::findNext(Level to_level) +{ + while (levelLessOrEqual(first_level_, last_level_) + && levelLessOrEqual(first_level_, to_level) + && queue_[first_level_].empty()) + incrLevel(first_level_); +} + +void +BfsIterator::enqueue(Vertex *vertex) +{ + debugPrint1(debug_, "bfs", 2, "enqueue %s\n", vertex->name(sdc_network_)); + if (!vertex->bfsInQueue(bfs_index_)) { + Level level = vertex->level(); + UniqueLock lock(queue_lock_); + if (!vertex->bfsInQueue(bfs_index_)) { + vertex->setBfsInQueue(bfs_index_, true); + queue_[level].push_back(vertex); + + if (levelLess(last_level_, level)) + last_level_ = level; + if (levelLess(level, first_level_)) + first_level_ = level; + } + } +} + +bool +BfsIterator::inQueue(Vertex *vertex) +{ + // checkInQueue(vertex); + return vertex->bfsInQueue(bfs_index_); +} + +void +BfsIterator::checkInQueue(Vertex *vertex) +{ + Level level = vertex->level(); + if (static_cast(queue_.size()) > level) { + for (auto v : queue_[level]) { + if (v == vertex) { + if (vertex->bfsInQueue(bfs_index_)) + return; + else + printf("extra %s\n", vertex->name(sdc_network_)); + } + } + } + if (vertex->bfsInQueue(bfs_index_)) + printf("missing %s\n", vertex->name(sdc_network_)); +} + +void +BfsIterator::deleteVertexBefore(Vertex *vertex) +{ + remove(vertex); +} + +// Remove by inserting null vertex pointer. +void +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) { + for (auto &v : queue_[level]) { + if (v == vertex) { + v = nullptr; + vertex->setBfsInQueue(bfs_index_, false); + break; + } + } + } +} + +//////////////////////////////////////////////////////////////// + +BfsFwdIterator::BfsFwdIterator(BfsIndex bfs_index, + SearchPred *search_pred, + StaState *sta) : + BfsIterator(bfs_index, 0, INT_MAX, search_pred, sta) +{ +} + +// clear() without saving lists to list_free_. +BfsFwdIterator::~BfsFwdIterator() +{ + for (Level level = first_level_; level <= last_level_; level++) + deleteEntries(level); +} + +void +BfsFwdIterator::incrLevel(Level &level) +{ + level++; +} + +bool +BfsFwdIterator::levelLessOrEqual(Level level1, + Level level2) const +{ + return level1 <= level2; +} + +bool +BfsFwdIterator::levelLess(Level level1, + Level level2) const +{ + return level1 < level2; +} + +void +BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred, + Level to_level) +{ + 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); + } + } +} + +//////////////////////////////////////////////////////////////// + +BfsBkwdIterator::BfsBkwdIterator(BfsIndex bfs_index, + SearchPred *search_pred, + StaState *sta) : + BfsIterator(bfs_index, INT_MAX, 0, search_pred, sta) +{ +} + +// clear() without saving lists to list_free_. +BfsBkwdIterator::~BfsBkwdIterator() +{ + for (Level level = first_level_; level >= last_level_; level--) + deleteEntries(level); +} + +void +BfsBkwdIterator::incrLevel(Level &level) +{ + level--; +} + +bool +BfsBkwdIterator::levelLessOrEqual(Level level1, + Level level2) const +{ + return level1 >= level2; +} + +bool +BfsBkwdIterator::levelLess(Level level1, + Level level2) const +{ + return level1 > level2; +} + +void +BfsBkwdIterator::enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred, + Level to_level) +{ + 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); + } + } +} + +} // namespace diff --git a/search/Bfs.hh b/search/Bfs.hh new file mode 100644 index 0000000..3556cac --- /dev/null +++ b/search/Bfs.hh @@ -0,0 +1,168 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_BFS_H +#define STA_BFS_H + +#include +#include "DisallowCopyAssign.hh" +#include "Iterator.hh" +#include "Set.hh" +#include "StaState.hh" +#include "GraphClass.hh" +#include "VertexVisitor.hh" + +namespace sta { + +class SearchPred; +class BfsFwdIterator; +class BfsBkwdIterator; + +// LevelQueue is a vector of vertex vectors indexed by logic level. +typedef Vector LevelQueue; + +// Abstract base class for forward and backward breadth first search iterators. +// Visit all of the vertices at a level before moving to the next. +// Use enqueue to seed the search. +// Use enqueueAdjacentVertices as a vertex is visited to queue the +// fanout vertices filtered by the search predicate. +// +// 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 +{ +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; + // 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, + SearchPred *search_pred, + Level to_level) = 0; + 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 Network *network); + + virtual bool hasNext(); + bool hasNext(Level to_level); + virtual Vertex *next(); + + // Apply visitor to all vertices in the queue in level order. + // Returns the number of vertices that are visited. + virtual int visit(Level to_level, + VertexVisitor *visitor); + // Apply visitor to all vertices in the queue in level order, + // using threads to parallelize the visits. visitor must be thread safe. + // Returns the number of vertices that are visited. + int visitParallel(Level to_level, + VertexVisitor *visitor); + +protected: + BfsIterator(BfsIndex bfs_index, + Level level_min, + Level level_max, + SearchPred *search_pred, + StaState *sta); + void init(); + void deleteEntries(Level level); + virtual bool levelLess(Level level1, + Level level2) const = 0; + virtual bool levelLessOrEqual(Level level1, + Level level2) const = 0; + virtual void incrLevel(Level &level) = 0; + void findNext(Level to_level); + void deleteEntries(); + + BfsIndex bfs_index_; + Level level_min_; + Level level_max_; + SearchPred *search_pred_; + LevelQueue queue_; + std::mutex queue_lock_; + // Min (max) level of queued vertices. + Level first_level_; + // Max (min) level of queued vertices. + Level last_level_; + + friend class BfsFwdIterator; + friend class BfsBkwdIterator; + +private: + DISALLOW_COPY_AND_ASSIGN(BfsIterator); +}; + +class BfsFwdIterator : public BfsIterator +{ +public: + BfsFwdIterator(BfsIndex bfs_index, + SearchPred *search_pred, + StaState *sta); + virtual ~BfsFwdIterator(); + virtual void enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred, + Level to_level); + 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); + +private: + DISALLOW_COPY_AND_ASSIGN(BfsFwdIterator); +}; + +class BfsBkwdIterator : public BfsIterator +{ +public: + BfsBkwdIterator(BfsIndex bfs_index, + SearchPred *search_pred, + StaState *sta); + virtual ~BfsBkwdIterator(); + virtual void enqueueAdjacentVertices(Vertex *vertex, + SearchPred *search_pred, + Level to_level); + 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); + +private: + DISALLOW_COPY_AND_ASSIGN(BfsBkwdIterator); +}; + +} // namespace +#endif diff --git a/search/CheckMaxSkews.cc b/search/CheckMaxSkews.cc new file mode 100644 index 0000000..dcd4749 --- /dev/null +++ b/search/CheckMaxSkews.cc @@ -0,0 +1,296 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "TimingRole.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Clock.hh" +#include "PathVertex.hh" +#include "PathAnalysisPt.hh" +#include "Search.hh" +#include "CheckMaxSkews.hh" + +namespace sta { + +// Abstract base class. +class MaxSkewCheckVisitor +{ +public: + MaxSkewCheckVisitor() {} + virtual ~MaxSkewCheckVisitor() {} + virtual void visit(MaxSkewCheck &check, + const StaState *sta) = 0; + +private: + DISALLOW_COPY_AND_ASSIGN(MaxSkewCheckVisitor); +}; + +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: + DISALLOW_COPY_AND_ASSIGN(MaxSkewChecksVisitor); + + MaxSkewCheckSeq &checks_; +}; + +MaxSkewChecksVisitor::MaxSkewChecksVisitor(MaxSkewCheckSeq &checks) : + MaxSkewCheckVisitor(), + checks_(checks) +{ +} + +void +MaxSkewChecksVisitor::visit(MaxSkewCheck &check, + const StaState *) +{ + checks_.push_back(check.copy()); +} + +class MaxSkewViolatorsVisititor : public MaxSkewCheckVisitor +{ +public: + explicit MaxSkewViolatorsVisititor(MaxSkewCheckSeq &checks); + virtual void visit(MaxSkewCheck &check, + const StaState *sta); + +private: + DISALLOW_COPY_AND_ASSIGN(MaxSkewViolatorsVisititor); + MaxSkewCheckSeq &checks_; +}; + +MaxSkewViolatorsVisititor:: +MaxSkewViolatorsVisititor(MaxSkewCheckSeq &checks) : + MaxSkewCheckVisitor(), + checks_(checks) +{ +} + +void +MaxSkewViolatorsVisititor::visit(MaxSkewCheck &check, + const StaState *sta) +{ + if (fuzzyLess(check.slack(sta), 0.0)) + checks_.push_back(check.copy()); +} + +MaxSkewCheckSeq & +CheckMaxSkews::violations() +{ + 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: + DISALLOW_COPY_AND_ASSIGN(MaxSkewSlackVisitor); + + 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) + min_slack_check_ = check.copy(); + else if (slack_less(&check, min_slack_check_)) { + delete min_slack_check_; + min_slack_check_ = check.copy(); + } +} + +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; +} + +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); + } +} + +void +CheckMaxSkews:: visitMaxSkewChecks(Vertex *vertex, + MaxSkewCheckVisitor *visitor) +{ + Graph *graph = sta_->graph(); + Search *search = sta_->search(); + const MinMax *clk_min_max = MinMax::max(); + VertexInEdgeIterator edge_iter(vertex, graph); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role() == TimingRole::skew()) { + Vertex *ref_vertex = edge->from(graph); + TimingArcSet *arc_set = edge->timingArcSet(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + TransRiseFall *clk_tr = arc->fromTrans()->asRiseFall(); + TransRiseFall *ref_tr = arc->toTrans()->asRiseFall(); + VertexPathIterator clk_path_iter(vertex, clk_tr, clk_min_max, search); + while (clk_path_iter.hasNext()) { + PathVertex *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_tr, ref_ap, sta_); + while (ref_path_iter.hasNext()) { + PathVertex *ref_path = ref_path_iter.next(); + if (ref_path->isClock(search)) { + MaxSkewCheck check(clk_path, ref_path, arc, edge); + visitor->visit(check, sta_); + } + } + } + } + } + } + } +} + +//////////////////////////////////////////////////////////////// + +MaxSkewCheck::MaxSkewCheck(PathVertex *clk_path, + PathVertex *ref_path, + TimingArc *check_arc, + Edge *check_edge) : + clk_path_(clk_path), + ref_path_(ref_path), + check_arc_(check_arc), + check_edge_(check_edge) +{ +} + +MaxSkewCheck * +MaxSkewCheck::copy() +{ + return new MaxSkewCheck(&clk_path_, &ref_path_, check_arc_, check_edge_); +} + +Pin * +MaxSkewCheck::clkPin(const StaState *sta) const +{ + return clk_path_.pin(sta); +} + +Pin * +MaxSkewCheck::refPin(const StaState *sta) const +{ + return ref_path_.pin(sta); +} + +ArcDelay +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)); +} + +Delay +MaxSkewCheck::skew(const StaState *sta) const +{ + return Delay(clk_path_.arrival(sta) - ref_path_.arrival(sta)); +} + +Slack +MaxSkewCheck::slack(const StaState *sta) const +{ + return maxSkew(sta) - skew(sta); +} + +//////////////////////////////////////////////////////////////// + +MaxSkewSlackLess::MaxSkewSlackLess(const StaState *sta) : + sta_(sta) +{ +} + +bool +MaxSkewSlackLess::operator()(const MaxSkewCheck *check1, + const MaxSkewCheck *check2) const +{ + Slack slack1 = check1->slack(sta_); + Slack slack2 = check2->slack(sta_); + return slack1 < slack2 + || (fuzzyEqual(slack1, slack2) + // Break ties based on constrained pin names. + && sta_->network()->pinLess(check1->clkPin(sta_),check2->clkPin(sta_))); +} + +} // namespace diff --git a/search/CheckMaxSkews.hh b/search/CheckMaxSkews.hh new file mode 100644 index 0000000..bacc84f --- /dev/null +++ b/search/CheckMaxSkews.hh @@ -0,0 +1,92 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_MAX_SKEW_H +#define STA_MAX_SKEW_H + +#include "DisallowCopyAssign.hh" +#include "StaState.hh" +#include "GraphClass.hh" +#include "SearchClass.hh" +#include "Delay.hh" +#include "PathRef.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_; + +private: + DISALLOW_COPY_AND_ASSIGN(CheckMaxSkews); +}; + +class MaxSkewCheck +{ +public: + MaxSkewCheck(PathVertex *clk_path, + PathVertex *ref_path, + TimingArc *check_arc, + Edge *check_edge); + MaxSkewCheck *copy(); + PathVertex *clkPath() { return &clk_path_; } + Pin *clkPin(const StaState *sta) const; + PathVertex *refPath() { return &ref_path_; } + Pin *refPin(const StaState *sta) 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_; } + +private: + DISALLOW_COPY_AND_ASSIGN(MaxSkewCheck); + + PathVertex clk_path_; + PathVertex ref_path_; + TimingArc *check_arc_; + Edge *check_edge_; +}; + +class MaxSkewSlackLess +{ +public: + explicit MaxSkewSlackLess(const StaState *sta); + bool operator()(const MaxSkewCheck *check1, + const MaxSkewCheck *check2) const; + +protected: + const StaState *sta_; +}; + +} // namespace +#endif diff --git a/search/CheckMinPeriods.cc b/search/CheckMinPeriods.cc new file mode 100644 index 0000000..5533771 --- /dev/null +++ b/search/CheckMinPeriods.cc @@ -0,0 +1,243 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Clock.hh" +#include "Sdc.hh" +#include "Graph.hh" +#include "DcalcAnalysisPt.hh" +#include "GraphDelayCalc.hh" +#include "Search.hh" +#include "CheckMinPeriods.hh" + +namespace sta { + +// Abstract base class. +class MinPeriodCheckVisitor +{ +public: + MinPeriodCheckVisitor() {} + virtual ~MinPeriodCheckVisitor() {} + virtual void visit(MinPeriodCheck &check, + StaState *sta) = 0; + +private: + DISALLOW_COPY_AND_ASSIGN(MinPeriodCheckVisitor); +}; + +CheckMinPeriods::CheckMinPeriods(StaState *sta) : + sta_(sta) +{ +} + +CheckMinPeriods::~CheckMinPeriods() +{ + checks_.deleteContents(); +} + +void +CheckMinPeriods::clear() +{ + checks_.deleteContentsClear(); +} + +class MinPeriodViolatorsVisitor : public MinPeriodCheckVisitor +{ +public: + MinPeriodViolatorsVisitor(MinPeriodCheckSeq &checks); + virtual void visit(MinPeriodCheck &check, + StaState *sta); + +private: + DISALLOW_COPY_AND_ASSIGN(MinPeriodViolatorsVisitor); + + MinPeriodCheckSeq &checks_; +}; + +MinPeriodViolatorsVisitor::MinPeriodViolatorsVisitor(MinPeriodCheckSeq &checks): + checks_(checks) +{ +} + +void +MinPeriodViolatorsVisitor::visit(MinPeriodCheck &check, + StaState *sta) +{ + if (fuzzyLess(check.slack(sta), 0.0)) + checks_.push_back(check.copy()); +} + +MinPeriodCheckSeq & +CheckMinPeriods::violations() +{ + clear(); + MinPeriodViolatorsVisitor visitor(checks_); + visitMinPeriodChecks(&visitor); + sort(checks_, MinPeriodSlackLess(sta_)); + return checks_; +} + +void +CheckMinPeriods::visitMinPeriodChecks(MinPeriodCheckVisitor *visitor) +{ + Graph *graph = sta_->graph(); + VertexIterator vertex_iter(graph); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + if (isClkEnd(vertex, graph)) + visitMinPeriodChecks(vertex, visitor); + } +} + +void +CheckMinPeriods::visitMinPeriodChecks(Vertex *vertex, + MinPeriodCheckVisitor *visitor) +{ + Search *search = sta_->search(); + GraphDelayCalc *graph_dcalc = sta_->graphDelayCalc(); + Pin *pin = vertex->pin(); + float min_period; + bool exists; + graph_dcalc->minPeriod(pin, min_period, exists); + if (exists) { + ClockSet clks; + search->clocks(vertex, clks); + ClockSet::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + MinPeriodCheck check(pin, clk); + visitor->visit(check, sta_); + } + } +} + +//////////////////////////////////////////////////////////////// + +class MinPeriodSlackVisitor : public MinPeriodCheckVisitor +{ +public: + MinPeriodSlackVisitor(); + virtual void visit(MinPeriodCheck &check, + StaState *sta); + MinPeriodCheck *minSlackCheck(); + +private: + DISALLOW_COPY_AND_ASSIGN(MinPeriodSlackVisitor); + + MinPeriodCheck *min_slack_check_; +}; + +MinPeriodSlackVisitor::MinPeriodSlackVisitor() : + min_slack_check_(nullptr) +{ +} + +void +MinPeriodSlackVisitor::visit(MinPeriodCheck &check, + StaState *sta) +{ + 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(); + } +} + +MinPeriodCheck * +MinPeriodSlackVisitor::minSlackCheck() +{ + return min_slack_check_; +} + +MinPeriodCheck * +CheckMinPeriods::minSlackCheck() +{ + clear(); + MinPeriodSlackVisitor visitor; + visitMinPeriodChecks(&visitor); + MinPeriodCheck *check = visitor.minSlackCheck(); + // Save check for cleanup. + checks_.push_back(check); + return check; +} + +//////////////////////////////////////////////////////////////// + +MinPeriodCheck::MinPeriodCheck(Pin *pin, + Clock *clk) : + pin_(pin), + clk_(clk) +{ +} + +MinPeriodCheck * +MinPeriodCheck::copy() +{ + return new MinPeriodCheck(pin_, clk_); +} + +float +MinPeriodCheck::period() const +{ + return clk_->period(); +} + +float +MinPeriodCheck::minPeriod(const StaState *sta) const +{ + GraphDelayCalc *graph_dcalc = sta->graphDelayCalc(); + float min_period; + bool exists; + graph_dcalc->minPeriod(pin_, min_period, exists); + return min_period; +} + +Slack +MinPeriodCheck::slack(const StaState *sta) const +{ + return clk_->period() - minPeriod(sta); +} + +//////////////////////////////////////////////////////////////// + +MinPeriodSlackLess::MinPeriodSlackLess(StaState *sta) : + sta_(sta) +{ +} + +bool +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(); + return fuzzyLess(slack1, slack2) + // Break ties based on pin and clock names. + || (fuzzyEqual(slack1, slack2) + && (sta_->network()->pinLess(pin1, pin2) + || (pin1 == pin2 + && ClockNameLess()(check1->clk(), + check2->clk())))); +} + +} // namespace diff --git a/search/CheckMinPeriods.hh b/search/CheckMinPeriods.hh new file mode 100644 index 0000000..a1e296d --- /dev/null +++ b/search/CheckMinPeriods.hh @@ -0,0 +1,84 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_MIN_PERIOD_H +#define STA_MIN_PERIOD_H + +#include "DisallowCopyAssign.hh" +#include "StaState.hh" +#include "NetworkClass.hh" +#include "GraphClass.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" +#include "Delay.hh" + +namespace sta { + +class MinPeriodCheckVisitor; + +class CheckMinPeriods +{ +public: + explicit CheckMinPeriods(StaState *sta); + ~CheckMinPeriods(); + void clear(); + MinPeriodCheckSeq &violations(); + // Min period check with the least slack. + MinPeriodCheck *minSlackCheck(); + +protected: + void visitMinPeriodChecks(MinPeriodCheckVisitor *visitor); + void visitMinPeriodChecks(Vertex *vertex, + MinPeriodCheckVisitor *visitor); + + MinPeriodCheckSeq checks_; + StaState *sta_; + +private: + DISALLOW_COPY_AND_ASSIGN(CheckMinPeriods); +}; + +class MinPeriodCheck +{ +public: + MinPeriodCheck(Pin *pin, Clock *clk); + MinPeriodCheck *copy(); + Pin *pin() const { return pin_; } + Clock *clk() const { return clk_; } + float period() const; + float minPeriod(const StaState *sta) const; + Slack slack(const StaState *sta) const; + +private: + DISALLOW_COPY_AND_ASSIGN(MinPeriodCheck); + + Pin *pin_; + Clock *clk_; +}; + +class MinPeriodSlackLess +{ +public: + explicit MinPeriodSlackLess(StaState *sta); + bool operator()(const MinPeriodCheck *check1, + const MinPeriodCheck *check2) const; + +private: + const StaState *sta_; +}; + +} // namespace +#endif diff --git a/search/CheckMinPulseWidths.cc b/search/CheckMinPulseWidths.cc new file mode 100644 index 0000000..df00ef4 --- /dev/null +++ b/search/CheckMinPulseWidths.cc @@ -0,0 +1,510 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Debug.hh" +#include "DisallowCopyAssign.hh" +#include "TimingRole.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Clock.hh" +#include "Sdc.hh" +#include "ClkInfo.hh" +#include "Tag.hh" +#include "PathVertex.hh" +#include "PathRef.hh" +#include "Corner.hh" +#include "DcalcAnalysisPt.hh" +#include "GraphDelayCalc.hh" +#include "PathAnalysisPt.hh" +#include "SearchPred.hh" +#include "PathEnd.hh" +#include "Search.hh" +#include "CheckMinPulseWidths.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; + +private: + DISALLOW_COPY_AND_ASSIGN(MinPulseWidthCheckVisitor); +}; + +CheckMinPulseWidths::CheckMinPulseWidths(StaState *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: + DISALLOW_COPY_AND_ASSIGN(MinPulseWidthChecksVisitor); + + 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); + } +} + +MinPulseWidthCheckSeq & +CheckMinPulseWidths::check(const Corner *corner) +{ + 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()) { + Pin *pin = pin_iter.next(); + Vertex *vertex = graph->pinLoadVertex(pin); + visitMinPulseWidthChecks(vertex, &visitor); + } + sort(checks_, MinPulseWidthSlackLess(sta_)); + return checks_; +} + +//////////////////////////////////////////////////////////////// + +class MinPulseWidthViolatorsVisitor : public MinPulseWidthCheckVisitor +{ +public: + explicit MinPulseWidthViolatorsVisitor(const Corner *corner, + MinPulseWidthCheckSeq &checks); + virtual void visit(MinPulseWidthCheck &check, + const StaState *sta); + +private: + DISALLOW_COPY_AND_ASSIGN(MinPulseWidthViolatorsVisitor); + + 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 (fuzzyLess(check.slack(sta), 0.0) + && (corner_ == nullptr + || check.corner(sta) == corner_)) { + MinPulseWidthCheck *copy = new MinPulseWidthCheck(check.openPath()); + checks_.push_back(copy); + } +} + +MinPulseWidthCheckSeq & +CheckMinPulseWidths::violations(const Corner *corner) +{ + clear(); + MinPulseWidthViolatorsVisitor visitor(corner, checks_); + visitMinPulseWidthChecks(&visitor); + sort(checks_, MinPulseWidthSlackLess(sta_)); + return checks_; +} + +//////////////////////////////////////////////////////////////// + +class MinPulseWidthSlackVisitor : public MinPulseWidthCheckVisitor +{ +public: + MinPulseWidthSlackVisitor(const Corner *corner); + virtual void visit(MinPulseWidthCheck &check, + const StaState *sta); + MinPulseWidthCheck *minSlackCheck(); + +private: + DISALLOW_COPY_AND_ASSIGN(MinPulseWidthSlackVisitor); + + 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) +{ + 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(); + } + } +} + +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; +} + +void +CheckMinPulseWidths:: +visitMinPulseWidthChecks(MinPulseWidthCheckVisitor *visitor) +{ + Graph *graph = sta_->graph(); + Debug *debug = sta_->debug(); + Network *sdc_network = sta_->network(); + VertexIterator vertex_iter(graph); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + if (isClkEnd(vertex, graph)) { + debugPrint1(debug, "mpw", 1, "check mpw %s\n", vertex->name(sdc_network)); + visitMinPulseWidthChecks(vertex, visitor); + } + } +} + +void +CheckMinPulseWidths:: +visitMinPulseWidthChecks(Vertex *vertex, + MinPulseWidthCheckVisitor *visitor) +{ + Search *search = sta_->search(); + const MinMax *min_max = MinMax::max(); + 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); + PathVertex close_path; + check.closePath(sta_, close_path); + // Don't bother visiting if nobody is home. + if (!close_path.isNull()) + visitor->visit(check, sta_); + } + } + } + } +} + +//////////////////////////////////////////////////////////////// + +MinPulseWidthCheck::MinPulseWidthCheck() : + open_path_() +{ +} + +MinPulseWidthCheck::MinPulseWidthCheck(Path *open_path) : + open_path_(open_path) +{ +} + +MinPulseWidthCheck * +MinPulseWidthCheck::copy() +{ + return new MinPulseWidthCheck(&open_path_); +} + +Pin * +MinPulseWidthCheck::pin(const StaState *sta) const +{ + return open_path_.pin(sta); +} + +const TransRiseFall * +MinPulseWidthCheck::openTransition(const StaState *sta) const +{ + return open_path_.transition(sta); +} + +void +MinPulseWidthCheck::closePath(const StaState *sta, + // Return value. + PathVertex &close) const +{ + PathAnalysisPt *open_ap = open_path_.pathAnalysisPt(sta); + PathAnalysisPt *close_ap = open_ap->tgtClkAnalysisPt(); + const TransRiseFall *open_tr = open_path_.transition(sta); + const TransRiseFall *close_tr = open_tr->opposite(); + Tag *open_tag = open_path_.tag(sta); + ClkInfo *open_clk_info = open_tag->clkInfo(); + 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); + Tag close_tag(0, + close_tr->index(), + close_ap->index(), + &close_clk_info, + open_tag->isClock(), + open_tag->inputDelay(), + open_tag->isSegmentStart(), + open_tag->states(), + false, sta); + debugPrint1(sta->debug(), "mpw", 3, " open %s\n", + open_tag->asString(sta)); + debugPrint1(sta->debug(), "mpw", 3, " close %s\n", + close_tag.asString(sta)); + VertexPathIterator close_iter(open_path_.vertex(sta), close_tr, + close_ap, sta); + while (close_iter.hasNext()) { + PathVertex *close_path = close_iter.next(); + if (tagMatchNoPathAp(close_path->tag(sta), &close_tag)) { + debugPrint1(sta->debug(), "mpw", 3, " match %s\n", + close_path->tag(sta)->asString(sta)); + close.copy(close_path); + break; + } + } +} + +Arrival +MinPulseWidthCheck::openArrival(const StaState *sta) const +{ + return open_path_.arrival(sta); +} + +Arrival +MinPulseWidthCheck::closeArrival(const StaState *sta) const +{ + PathVertex close; + closePath(sta, close); + return close.arrival(sta); +} + +Arrival +MinPulseWidthCheck::openDelay(const StaState *sta) const +{ + return openArrival(sta) - openClkEdge(sta)->time(); +} + +Arrival +MinPulseWidthCheck::closeDelay(const StaState *sta) const +{ + return closeArrival(sta) - closeClkEdge(sta)->time(); +} + +ClockEdge * +MinPulseWidthCheck::openClkEdge(const StaState *sta) const +{ + return open_path_.clkEdge(sta->search()); +} + +ClockEdge * +MinPulseWidthCheck::closeClkEdge(const StaState *sta) const +{ + Tag *open_tag = open_path_.tag(sta); + ClkInfo *open_clk_info = open_tag->clkInfo(); + return open_clk_info->clkEdge()->opposite(); +} + +float +MinPulseWidthCheck::closeOffset(const StaState *sta) const +{ + ClockEdge *open_clk_edge = openClkEdge(sta); + ClockEdge *close_clk_edge = closeClkEdge(sta); + if (open_clk_edge->time() > close_clk_edge->time()) + return open_clk_edge->clock()->period(); + else + return 0.0; +} + +Arrival +MinPulseWidthCheck::width(const StaState *sta) const +{ + return closeArrival(sta) + closeOffset(sta) + - open_path_.arrival(sta) + + commonClkPessimism(sta); +} + +float +MinPulseWidthCheck::minWidth(const StaState *sta) const +{ + float min_width; + bool exists; + minPulseWidth(&open_path_, sta, min_width, exists); + return min_width; +} + +// Precedence: +// set_min_pulse_width constraint +// SDF annotation +// Liberty library +static void +minPulseWidth(const Path *path, + const StaState *sta, + // Return values. + float &min_width, + bool &exists) +{ + Pin *pin = path->pin(sta); + Clock *clk = path->clock(sta); + const TransRiseFall *tr = path->transition(sta); + Sdc *sdc = sta->sdc(); + // set_min_pulse_width command. + sdc->minPulseWidth(pin, clk, tr, min_width, exists); + if (!exists) { + GraphDelayCalc *graph_dcalc = sta->graphDelayCalc(); + const MinMax *min_max = path->minMax(sta); + const PathAnalysisPt *path_ap = path->pathAnalysisPt(sta); + const DcalcAnalysisPt *dcalc_ap = path_ap->dcalcAnalysisPt(); + graph_dcalc->minPulseWidth(pin, tr, dcalc_ap->index(), min_max, + min_width, exists); + } +} + +Crpr +MinPulseWidthCheck::commonClkPessimism(const StaState *sta) const +{ + CheckCrpr *check_crpr = sta->search()->checkCrpr(); + PathVertex close; + closePath(sta, close); + if (!close.isNull()) + return check_crpr->checkCrpr(openPath(), &close); + else + return 0.0; +} + +Slack +MinPulseWidthCheck::slack(const StaState *sta) const +{ + return width(sta) - minWidth(sta); +} + +Corner * +MinPulseWidthCheck::corner(const StaState *sta) const +{ + return open_path_.pathAnalysisPt(sta)->corner(); +} + +//////////////////////////////////////////////////////////////// + +MinPulseWidthSlackLess::MinPulseWidthSlackLess(const StaState *sta) : + sta_(sta) +{ +} + +bool +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_); + return slack1 < slack2 + || (fuzzyEqual(slack1, slack2) + // Break ties for the sake of regression stability. + && (sta_->network()->pinLess(pin1, pin2) + || (pin1 == pin2 + && check1->openPath()->trIndex(sta_) + < check2->openPath()->trIndex(sta_)))); +} + +} // namespace diff --git a/search/CheckMinPulseWidths.hh b/search/CheckMinPulseWidths.hh new file mode 100644 index 0000000..dcae4af --- /dev/null +++ b/search/CheckMinPulseWidths.hh @@ -0,0 +1,110 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_MIN_PULSE_WIDTH_H +#define STA_MIN_PULSE_WIDTH_H + +#include "DisallowCopyAssign.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" +#include "StaState.hh" +#include "PathRef.hh" + +namespace sta { + +class TransRiseFall; +class MinPulseWidthCheck; +class MinPulseWidthCheckVisitor; + +class CheckMinPulseWidths +{ +public: + explicit 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); + + MinPulseWidthCheckSeq checks_; + StaState *sta_; + +private: + DISALLOW_COPY_AND_ASSIGN(CheckMinPulseWidths); +}; + +class MinPulseWidthCheck +{ +public: + explicit MinPulseWidthCheck(); + MinPulseWidthCheck(Path *open_path); + MinPulseWidthCheck *copy(); + Pin *pin(const StaState *sta) const; + const TransRiseFall *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; + const Path *openPath() const { return &open_path_; } + Arrival openArrival(const StaState *sta) const; + void closePath(const StaState *sta, + // Return value. + PathVertex &close) const; + Arrival closeArrival(const StaState *sta) const; + Arrival openDelay(const StaState *sta) const; + Arrival closeDelay(const StaState *sta) const; + float closeOffset(const StaState *sta) const; + ClockEdge *openClkEdge(const StaState *sta) const; + ClockEdge *closeClkEdge(const StaState *sta) const; + Crpr commonClkPessimism(const StaState *sta) const; + +protected: + // Open path of the pulse. + PathRef open_path_; + +private: + DISALLOW_COPY_AND_ASSIGN(MinPulseWidthCheck); +}; + +class MinPulseWidthSlackLess +{ +public: + explicit MinPulseWidthSlackLess(const StaState *sta); + bool operator()(const MinPulseWidthCheck *check1, + const MinPulseWidthCheck *check2) const; + +private: + const StaState *sta_; +}; + +} // namespace +#endif diff --git a/search/CheckSlewLimits.cc b/search/CheckSlewLimits.cc new file mode 100644 index 0000000..34d4ce1 --- /dev/null +++ b/search/CheckSlewLimits.cc @@ -0,0 +1,380 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Fuzzy.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "Graph.hh" +#include "StaState.hh" +#include "DcalcAnalysisPt.hh" +#include "Corner.hh" +#include "PathVertex.hh" +#include "Search.hh" +#include "CheckSlewLimits.hh" + +namespace sta { + +class PinSlewLimitSlackLess +{ +public: + PinSlewLimitSlackLess(const Corner *corner, + const MinMax *min_max, + CheckSlewLimits *check_slew_limit, + const StaState *sta); + bool operator()(Pin *pin1, + 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()(Pin *pin1, + Pin *pin2) const +{ + const Corner *corner1, *corner2; + const TransRiseFall *tr1, *tr2; + Slew slew1, slew2; + float limit1, limit2, slack1, slack2; + check_slew_limit_->checkSlews(pin1, corner_, min_max_, + corner1, tr1, slew1, limit1, slack1); + check_slew_limit_->checkSlews(pin2, corner_, min_max_, + corner2, tr2, slew2, limit2, slack2); + return 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::init(const MinMax *min_max) +{ + const Network *network = sta_->network(); + Cell *top_cell = network->cell(network->topInstance()); + float top_limit; + bool top_limit_exists; + sta_->sdc()->slewLimit(top_cell, min_max, + top_limit, top_limit_exists); + top_limit_= top_limit; + top_limit_exists_ = top_limit_exists; +} + +void +CheckSlewLimits::checkSlews(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + // Return values. + const Corner *&corner1, + const TransRiseFall *&tr, + Slew &slew, + float &limit, + float &slack) const +{ + corner1 = nullptr; + tr = nullptr; + slew = 0.0; + limit = 0.0; + slack = MinMax::min()->initValue(); + if (corner) + checkSlews1(pin, corner, min_max, + corner1, tr, slew, limit, slack); + else { + for (auto corner : *sta_->corners()) { + checkSlews1(pin, corner, min_max, + corner1, tr, slew, limit, slack); + } + } +} + +void +CheckSlewLimits::checkSlews1(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + // Return values. + const Corner *&corner1, + const TransRiseFall *&tr, + Slew &slew, + float &limit, + float &slack) const +{ + Vertex *vertex, *bidirect_drvr_vertex; + sta_->graph()->pinVertices(pin, vertex, bidirect_drvr_vertex); + checkSlews1(vertex, corner, min_max, + corner1, tr, slew, limit, slack); + if (bidirect_drvr_vertex) + checkSlews1(bidirect_drvr_vertex, corner, min_max, + corner1, tr, slew, limit, slack); +} + +void +CheckSlewLimits::checkSlews1(Vertex *vertex, + const Corner *corner1, + const MinMax *min_max, + // Return values. + const Corner *&corner, + const TransRiseFall *&tr, + Slew &slew, + float &limit, + float &slack) const +{ + for (auto tr1 : TransRiseFall::range()) { + float limit1; + bool limit1_exists; + findLimit(vertex->pin(), vertex, tr1, min_max, limit1, limit1_exists); + if (limit1_exists) { + checkSlew(vertex, corner1, min_max, tr1, limit1, + corner, tr, slew, slack, limit); + } + } +} + +void +CheckSlewLimits::findLimit(const Pin *pin, + const Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max, + // Return values. + float &limit, + bool &exists) const + +{ + exists = false; + const Network *network = sta_->network(); + Sdc *sdc = sta_->sdc(); + bool is_clk = sta_->search()->isClock(vertex); + // Look for clock slew limits. + ClockSet clks; + clockDomains(vertex, clks); + ClockSet::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + PathClkOrData clk_data = is_clk ? PathClkOrData::clk : PathClkOrData::data; + float clk_limit; + bool clk_limit_exists; + sdc->slewLimit(clk, tr, clk_data, min_max, + clk_limit, clk_limit_exists); + if (clk_limit_exists + && (!exists + || min_max->compare(limit, clk_limit))) { + // Use the tightest clock limit. + limit = clk_limit; + exists = true; + } + } + if (!exists) { + // Default to top ("design") limit. + exists = top_limit_exists_; + limit = top_limit_; + if (network->isTopLevelPort(pin)) { + Port *port = network->port(pin); + float port_limit; + bool port_limit_exists; + sdc->slewLimit(port, min_max, port_limit, port_limit_exists); + // Use the tightest limit. + if (port_limit_exists + && (!exists + || min_max->compare(limit, port_limit))) { + limit = port_limit; + exists = true; + } + } + else { + float pin_limit; + bool pin_limit_exists; + sdc->slewLimit(pin, min_max, + pin_limit, pin_limit_exists); + // Use the tightest limit. + if (pin_limit_exists + && (!exists + || min_max->compare(limit, pin_limit))) { + limit = pin_limit; + exists = true; + } + + float port_limit; + bool port_limit_exists; + LibertyPort *port = network->libertyPort(pin); + if (port) { + port->slewLimit(min_max, port_limit, port_limit_exists); + // Use the tightest limit. + if (port_limit_exists + && (!exists + || min_max->compare(limit, port_limit))) { + limit = port_limit; + exists = true; + } + } + } + } +} + +void +CheckSlewLimits::clockDomains(const Vertex *vertex, + // Return value. + ClockSet &clks) const +{ + VertexPathIterator path_iter(const_cast(vertex), sta_); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + Clock *clk = path->clock(sta_); + if (clk) + clks.insert(clk); + } +} + +void +CheckSlewLimits::checkSlew(Vertex *vertex, + const Corner *corner1, + const MinMax *min_max, + const TransRiseFall *tr1, + float limit1, + // Return values. + const Corner *&corner, + const TransRiseFall *&tr, + Slew &slew, + float &slack, + float &limit) const +{ + const DcalcAnalysisPt *dcalc_ap = corner1->findDcalcAnalysisPt(min_max); + Slew slew1 = sta_->graph()->slew(vertex, tr1, dcalc_ap->index()); + float slew2 = delayAsFloat(slew1); + float slack1 = (min_max == MinMax::max()) + ? limit1 - slew2 : slew2 - limit1; + if (corner == nullptr + || (slack1 < slack + // Break ties for the sake of regression stability. + || (fuzzyEqual(slack1, slack) + && tr1->index() < tr->index()))) { + corner = corner1; + tr = tr1; + slew = slew1; + slack = slack1; + limit = limit1; + } +} + +PinSeq * +CheckSlewLimits::pinSlewLimitViolations(const Corner *corner, + const MinMax *min_max) +{ + init(min_max); + const Network *network = sta_->network(); + PinSeq *violators = new PinSeq; + LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + pinSlewLimitViolations(inst, corner, min_max, violators); + } + delete inst_iter; + // Check top level ports. + pinSlewLimitViolations(network->topInstance(), corner, min_max, violators); + sort(violators, PinSlewLimitSlackLess(corner, min_max, this, sta_)); + return violators; +} + +void +CheckSlewLimits::pinSlewLimitViolations(Instance *inst, + const Corner *corner, + const MinMax *min_max, + PinSeq *violators) +{ + const Network *network = sta_->network(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + const Corner *corner1; + const TransRiseFall *tr; + Slew slew; + float limit, slack; + checkSlews(pin, corner, min_max, corner1, tr, slew, limit, slack ); + if (tr && slack < 0.0) + violators->push_back(pin); + } + delete pin_iter; +} + +Pin * +CheckSlewLimits::pinMinSlewLimitSlack(const Corner *corner, + const MinMax *min_max) +{ + init(min_max); + const Network *network = sta_->network(); + Pin *min_slack_pin = 0; + float min_slack = MinMax::min()->initValue(); + LeafInstanceIterator *inst_iter = network->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + pinMinSlewLimitSlack(inst, corner, min_max, min_slack_pin, min_slack); + } + delete inst_iter; + // Check top level ports. + pinMinSlewLimitSlack(network->topInstance(), corner, min_max, + min_slack_pin, min_slack); + return min_slack_pin; +} + +void +CheckSlewLimits::pinMinSlewLimitSlack(Instance *inst, + const Corner *corner, + const MinMax *min_max, + // Return values. + Pin *&min_slack_pin, + float &min_slack) +{ + const Network *network = sta_->network(); + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + const Corner *corner1; + const TransRiseFall *tr; + Slew slew; + float limit, slack; + checkSlews(pin, corner, min_max, corner1, tr, slew, limit, slack); + if (tr + && (min_slack_pin == 0 + || slack < min_slack)) { + min_slack_pin = pin; + min_slack = slack; + } + } + delete pin_iter; +} + +} // namespace diff --git a/search/CheckSlewLimits.hh b/search/CheckSlewLimits.hh new file mode 100644 index 0000000..3bb4b2a --- /dev/null +++ b/search/CheckSlewLimits.hh @@ -0,0 +1,112 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CHECK_SLEW_LIMIT_H +#define STA_CHECK_SLEW_LIMIT_H + +#include "MinMax.hh" +#include "Transition.hh" +#include "NetworkClass.hh" +#include "GraphClass.hh" +#include "Delay.hh" + +namespace sta { + +class StaState; +class DcalcAnalysisPt; + +class CheckSlewLimits +{ +public: + CheckSlewLimits(const StaState *sta); + void init(const MinMax *min_max); + // Requires init(). + // corner=nullptr checks all corners. + void checkSlews(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + // Return values. + // Corner is nullptr for no slew limit. + const Corner *&corner1, + const TransRiseFall *&tr, + Slew &slew, + float &limit, + float &slack) const; + // corner=nullptr checks all corners. + PinSeq *pinSlewLimitViolations(const Corner *corner, + const MinMax *min_max); + // corner=nullptr checks all corners. + Pin *pinMinSlewLimitSlack(const Corner *corner, + const MinMax *min_max); + +protected: + void checkSlews1(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + // Return values. + const Corner *&corner1, + const TransRiseFall *&tr, + Slew &slew, + float &limit, + float &slack) const; + void checkSlews1(Vertex *vertex, + const Corner *corner1, + const MinMax *min_max, + // Return values. + const Corner *&corner, + const TransRiseFall *&tr, + Slew &slew, + float &limit, + float &slack) const; + void checkSlew(Vertex *vertex, + const Corner *corner1, + const MinMax *min_max, + const TransRiseFall *tr1, + float limit1, + // Return values. + const Corner *&corner, + const TransRiseFall *&tr, + Slew &slew, + float &slack, + float &limit) const; + void findLimit(const Pin *pin, + const Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max, + // Return values. + float &limit1, + bool &limit1_exists) const; + void pinSlewLimitViolations(Instance *inst, + const Corner *corner, + const MinMax *min_max, + PinSeq *violators); + void pinMinSlewLimitSlack(Instance *inst, + const Corner *corner, + const MinMax *min_max, + // Return values. + Pin *&min_slack_pin, + float &min_slack); + void clockDomains(const Vertex *vertex, + // Return value. + ClockSet &clks) const; + + float top_limit_; + bool top_limit_exists_; + const StaState *sta_; +}; + +} // namespace +#endif diff --git a/search/CheckTiming.cc b/search/CheckTiming.cc new file mode 100644 index 0000000..8bf8a8b --- /dev/null +++ b/search/CheckTiming.cc @@ -0,0 +1,438 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Error.hh" +#include "PortDirection.hh" +#include "TimingRole.hh" +#include "Network.hh" +#include "NetworkCmp.hh" +#include "Graph.hh" +#include "PortDelay.hh" +#include "ExceptionPath.hh" +#include "Sdc.hh" +#include "SearchPred.hh" +#include "Levelize.hh" +#include "Bfs.hh" +#include "Search.hh" +#include "Genclks.hh" +#include "PathVertex.hh" +#include "CheckTiming.hh" + +namespace sta { + +using std::string; + +CheckTiming::CheckTiming(StaState *sta) : + StaState(sta) +{ +} + +CheckTiming::~CheckTiming() +{ + deleteErrors(); +} + +void +CheckTiming::deleteErrors() +{ + CheckErrorSeq::Iterator error_iter(errors_); + while (error_iter.hasNext()) { + CheckError *error = error_iter.next(); + deleteContents(error); + delete error; + } +} + +void +CheckTiming::clear() +{ + deleteErrors(); + errors_.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) +{ + clear(); + if (no_input_delay) + checkNoInputDelay(); + if (no_output_delay) + checkNoOutputDelay(); + if (reg_multiple_clks || reg_no_clks) + checkRegClks(reg_multiple_clks, reg_no_clks); + if (unconstrained_endpoints) + checkUnconstrainedEndpoints(); + if (loops) + checkLoops(); + if (generated_clks) + checkGeneratedClocks(); + return errors_; +} + +// Make sure there is a set_input_delay for each input/bidirect. +void +CheckTiming::checkNoInputDelay() +{ + PinSet no_arrival; + Instance *top_inst = network_->topInstance(); + InstancePinIterator *pin_iter = network_->pinIterator(top_inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (!sdc_->isClock(pin)) { + PortDirection *dir = network_->direction(pin); + if (dir->isAnyInput() + && !sdc_->hasInputDelay(pin)) + no_arrival.insert(pin); + } + } + delete pin_iter; + pushPinErrors("Warning: There %is %d input port%s missing set_input_delay.", + no_arrival); +} + +void +CheckTiming::checkNoOutputDelay() +{ + PinSet no_departure; + checkNoOutputDelay(no_departure); + pushPinErrors("Warning: There %is %d output port%s missing set_output_delay.", + no_departure); +} + +void +CheckTiming::checkNoOutputDelay(PinSet &no_departure) +{ + Instance *top_inst = network_->topInstance(); + InstancePinIterator *pin_iter = network_->pinIterator(top_inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + PortDirection *dir = network_->direction(pin); + if (dir->isAnyOutput() + && !sdc_->hasOutputDelay(pin)) + no_departure.insert(pin); + } + delete pin_iter; +} + +bool +CheckTiming::hasClkedCheck(Vertex *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_))) + 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) +{ + PinSet no_clk_pins, multiple_clk_pins; + VertexSet::ConstIterator reg_clk_iter(graph_->regClkVertices()); + while (reg_clk_iter.hasNext()) { + Vertex *vertex = reg_clk_iter.next(); + Pin *pin = vertex->pin(); + ClockSet clks; + search_->clocks(vertex, clks); + if (reg_no_clks && clks.empty()) + no_clk_pins.insert(pin); + if (reg_multiple_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); +} + +void +CheckTiming::checkLoops() +{ + // These may not need to be sorted because the graph roots are + // sorted during levelization so the discovery should be consistent. + GraphLoopSeq *loops = levelize_->loops(); + // Count the combinational loops. + int loop_count = 0; + GraphLoopSeq::Iterator loop_iter1(loops); + while (loop_iter1.hasNext()) { + GraphLoop *loop = loop_iter1.next(); + if (loop->isCombinational()) + 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())); + + GraphLoopSeq::Iterator loop_iter2(loops); + while (loop_iter2.hasNext()) { + GraphLoop *loop = loop_iter2.next(); + 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; + } + error->push_back(stringCopy("| 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); + + // Separator between loops. + error->push_back(stringCopy("--------------------------------")); + } + } + errors_.push_back(error); + } +} + +void +CheckTiming::checkUnconstrainedEndpoints() +{ + PinSet unconstrained_ends; + checkUnconstraintedOutputs(unconstrained_ends); + checkUnconstrainedSetups(unconstrained_ends); + pushPinErrors("Warning: There %is %d unconstrained endpoint%s.", + unconstrained_ends); +} + +void +CheckTiming::checkUnconstraintedOutputs(PinSet &unconstrained_ends) +{ + Instance *top_inst = network_->topInstance(); + InstancePinIterator *pin_iter = network_->pinIterator(top_inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + PortDirection *dir = network_->direction(pin); + if (dir->isAnyOutput() + && !((hasClkedDepature(pin) + && hasClkedArrival(graph_->pinLoadVertex(pin))) + || hasMaxDelay(pin))) + unconstrained_ends.insert(pin); + } + delete pin_iter; +} + +bool +CheckTiming::hasClkedDepature(Pin *pin) +{ + PinOutputDelayIterator delay_iter(pin, sdc_); + while (delay_iter.hasNext()) { + OutputDelay *output_delay = delay_iter.next(); + if (output_delay->clkEdge() != nullptr) + return true; + } + return false; +} + +// Check for max delay exception that ends at pin. +bool +CheckTiming::hasMaxDelay(Pin *pin) +{ + ExceptionPathSet *exceptions = sdc_->exceptions(); + ExceptionPathSet::Iterator exception_iter(exceptions); + while (exception_iter.hasNext()) { + ExceptionPath *exception = exception_iter.next(); + ExceptionTo *to = exception->to(); + if (exception->isPathDelay() + && exception->minMax() == MinMaxAll::max() + && to + && to->hasPins() + && to->pins()->hasKey(pin)) + return true; + } + return false; +} + +void +CheckTiming::checkUnconstrainedSetups(PinSet &unconstrained_ends) +{ + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role() == TimingRole::setup() + && (!search_->isClock(edge->from(graph_)) + || !hasClkedArrival(edge->to(graph_)))) { + unconstrained_ends.insert(vertex->pin()); + break; + } + } + } +} + +bool +CheckTiming::hasClkedArrival(Vertex *vertex) +{ + VertexPathIterator path_iter(vertex, this); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + if (path->clock(this)) + return true; + } + return false; +} + +void +CheckTiming::checkGeneratedClocks() +{ + ClockSet gen_clk_errors; + for (auto clk : sdc_->clks()) { + if (clk->isGenerated()) { + search_->genclks()->checkMaster(clk); + bool found_clk = false; + VertexSet src_vertices; + 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; + } + } + if (!found_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); +} + +// 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) +{ + 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())); + // Sort the error pins so the output is independent of the order + // the the errors are discovered. + PinSeq pin_seq; + sortPinSet(&pins, network_, pin_seq); + PinSeq::Iterator pin_iter(pin_seq); + while (pin_iter.hasNext()) { + const Pin *pin = pin_iter.next(); + const char *pin_name = stringCopy(sdc_network_->pathName(pin)); + error->push_back(pin_name); + } + errors_.push_back(error); + } +} + +void +CheckTiming::pushClkErrors(const char *msg, + 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())); + // Sort the error clks so the output is independent of the order + // the the errors are discovered. + ClockSeq clk_seq; + sortClockSet(&clks, clk_seq); + ClockSeq::Iterator clk_iter(clk_seq); + while (clk_iter.hasNext()) { + const Clock *clk = clk_iter.next(); + const char *clk_name = stringCopy(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') { + const char *obj_str = integerString(obj_count); + error_msg += obj_str; + stringDelete(obj_str); + s++; + } + else + internalError("unknown print flag"); + } + else + error_msg += ch; + } +} + +} // namespace diff --git a/search/CheckTiming.hh b/search/CheckTiming.hh new file mode 100644 index 0000000..6012465 --- /dev/null +++ b/search/CheckTiming.hh @@ -0,0 +1,76 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CHECK_TIMING_H +#define STA_CHECK_TIMING_H + +#include "DisallowCopyAssign.hh" +#include "Vector.hh" +#include "StringSeq.hh" +#include "NetworkClass.hh" +#include "StaState.hh" + +namespace sta { + +typedef StringSeq CheckError; +typedef Vector CheckErrorSeq; + +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); + +protected: + void clear(); + void deleteErrors(); + void checkNoInputDelay(); + void checkNoOutputDelay(); + void checkRegClks(bool reg_multiple_clks, + bool reg_no_clks); + void checkUnconstrainedEndpoints(); + bool hasClkedArrival(Vertex *vertex); + void checkNoOutputDelay(PinSet &ends); + void checkUnconstraintedOutputs(PinSet &unconstrained_ends); + void checkUnconstrainedSetups(PinSet &unconstrained_ends); + void checkLoops(); + bool hasClkedDepature(Pin *pin); + bool hasClkedCheck(Vertex *vertex); + bool hasMaxDelay(Pin *pin); + void checkGeneratedClocks(); + void pushPinErrors(const char *msg, + PinSet &pins); + void pushClkErrors(const char *msg, + ClockSet &clks); + void errorMsgSubst(const char *msg, + int count, + string &error_msg); + + CheckErrorSeq errors_; + +private: + DISALLOW_COPY_AND_ASSIGN(CheckTiming); +}; + +} // namespace +#endif diff --git a/search/ClkInfo.cc b/search/ClkInfo.cc new file mode 100644 index 0000000..0d1f7bb --- /dev/null +++ b/search/ClkInfo.cc @@ -0,0 +1,340 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Sdc.hh" +#include "Corner.hh" +#include "Search.hh" +#include "Tag.hh" +#include "PathAnalysisPt.hh" +#include "ClkInfo.hh" + +namespace sta { + +static bool +clkInfoEqual(const ClkInfo *clk_info1, + const ClkInfo *clk_info2, + const StaState *sta); +static int +clkInfoCmp(const ClkInfo *clk_info1, + const ClkInfo *clk_info2, + const StaState *sta); + +ClkInfo::ClkInfo(ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, + const Pin *gen_clk_src, + bool is_gen_clk_src_path, + const TransRiseFall *pulse_clk_sense, + Arrival insertion, + float latency, + ClockUncertainties *uncertainties, + PathAPIndex path_ap_index, + PathVertexRep &crpr_clk_path, + const StaState *sta) : + clk_edge_(clk_edge), + clk_src_(clk_src), + gen_clk_src_(gen_clk_src), + crpr_clk_path_(crpr_clk_path), + uncertainties_(uncertainties), + insertion_(insertion), + latency_(latency), + is_propagated_(is_propagated), + is_gen_clk_src_path_(is_gen_clk_src_path), + is_pulse_clk_(pulse_clk_sense != nullptr), + pulse_clk_sense_(pulse_clk_sense ? pulse_clk_sense->index() : 0), + path_ap_index_(path_ap_index) +{ + findHash(sta); +} + +ClkInfo::~ClkInfo() +{ +} + +void +ClkInfo::findHash(const StaState *sta) +{ + hash_ = hash_init_value; + if (clk_edge_) + hashIncr(hash_, clk_edge_->index()); + + const Network *network = sta->network(); + if (clk_src_) + hashIncr(hash_, network->vertexIndex(clk_src_)); + if (gen_clk_src_) + hashIncr(hash_, network->vertexIndex(gen_clk_src_)); + hashIncr(hash_, crprClkVertexIndex()); + if (uncertainties_) { + float uncertainty; + bool exists; + uncertainties_->value(MinMax::min(), uncertainty, exists); + if (exists) + hashIncr(hash_, uncertainty * 1E+12F); + uncertainties_->value(MinMax::max(), uncertainty, exists); + if (exists) + hashIncr(hash_, uncertainty * 1E+12F); + } + hashIncr(hash_, latency_ * 1E+12F); + hashIncr(hash_, delayAsFloat(insertion_) * 1E+12F); + 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_); +} + +VertexIndex +ClkInfo::crprClkVertexIndex() const +{ + if (crpr_clk_path_.isNull()) + return 0; + else + return crpr_clk_path_.vertexIndex(); +} + +const char * +ClkInfo::asString(const StaState *sta) const +{ + Network *network = sta->network(); + Corners *corners = sta->corners(); + string str; + + PathAnalysisPt *path_ap = corners->findPathAnalysisPt(path_ap_index_); + str += stringPrintTmp("%s/%d ", + path_ap->pathMinMax()->asString(), + path_ap_index_); + if (clk_edge_) + str += clk_edge_->name(); + else + str += "unclocked"; + + if (clk_src_) { + str += " clk_src "; + str += network->pathName(clk_src_); + } + + const Pin *crpr_clk_pin = crprClkPin(sta); + if (crpr_clk_pin) { + str += " crpr_pin "; + str += network->pathName(crpr_clk_pin); + } + + if (is_gen_clk_src_path_) + str += " genclk"; + + char *result = makeTmpString(str.size() + 1); + strcpy(result, str.c_str()); + return result; +} + +Clock * +ClkInfo::clock() const +{ + if (clk_edge_) + return clk_edge_->clock(); + else + return nullptr; +} + +TransRiseFall * +ClkInfo::pulseClkSense() const +{ + if (is_pulse_clk_) + return TransRiseFall::find(pulse_clk_sense_); + else + return nullptr; +} + +const Pin * +ClkInfo::crprClkPin(const StaState *sta) const +{ + if (!crpr_clk_path_.isNull()) + return crpr_clk_path_.vertex(sta)->pin(); + else + return nullptr; +} + +bool +ClkInfo::refsFilter(const StaState *sta) const +{ + return !crpr_clk_path_.isNull() + && crpr_clk_path_.tag(sta)->isFilter(); +} + +//////////////////////////////////////////////////////////////// + +size_t +ClkInfoHash::operator()(const ClkInfo *clk_info) +{ + return clk_info->hash(); +} + +//////////////////////////////////////////////////////////////// + +ClkInfoEqual::ClkInfoEqual(const StaState *sta) : + sta_(sta) +{ +} + +bool +ClkInfoEqual::operator()(const ClkInfo *clk_info1, + const ClkInfo *clk_info2) +{ + return clkInfoEqual(clk_info1, clk_info2, sta_); +} + +static bool +clkInfoEqual(const ClkInfo *clk_info1, + const ClkInfo *clk_info2, + const StaState *sta) +{ + bool crpr_on = sta->sdc()->crprActive(); + ClockUncertainties *uncertainties1 = clk_info1->uncertainties(); + ClockUncertainties *uncertainties2 = clk_info2->uncertainties(); + return clk_info1->clkEdge() == clk_info2->clkEdge() + && clk_info1->pathAPIndex() == clk_info2->pathAPIndex() + && clk_info1->clkSrc() == clk_info2->clkSrc() + && clk_info1->genClkSrc() == clk_info2->genClkSrc() + && (!crpr_on + || (PathVertexRep::equal(clk_info1->crprClkPath(), + clk_info2->crprClkPath()))) + && ((uncertainties1 == nullptr + && uncertainties2 == nullptr) + || (uncertainties1 && uncertainties2 + && MinMaxValues::equal(uncertainties1, + uncertainties2))) + && clk_info1->insertion() == clk_info2->insertion() + && clk_info1->latency() == clk_info2->latency() + && clk_info1->isPropagated() == clk_info2->isPropagated() + && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() + && clk_info1->isPulseClk() == clk_info2->isPulseClk() + && clk_info1->pulseClkSenseTrIndex() == clk_info2->pulseClkSenseTrIndex(); +} + +//////////////////////////////////////////////////////////////// + +ClkInfoLess::ClkInfoLess(const StaState *sta) : + sta_(sta) +{ +} + +bool +ClkInfoLess::operator()(const ClkInfo *clk_info1, + const ClkInfo *clk_info2) const +{ + return clkInfoCmp(clk_info1, clk_info2, sta_) < 0; +} + +static int +clkInfoCmp(const ClkInfo *clk_info1, + const ClkInfo *clk_info2, + const StaState *sta) +{ + ClockEdge *clk_edge1 = clk_info1->clkEdge(); + ClockEdge *clk_edge2 = clk_info2->clkEdge(); + int edge_index1 = clk_edge1 ? clk_edge1->index() : -1; + int edge_index2 = clk_edge2 ? clk_edge2->index() : -1; + if (edge_index1 < edge_index2) + return -1; + 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) + return -1; + if (path_ap_index1 > path_ap_index2) + return 1; + + const Pin *clk_src1 = clk_info1->clkSrc(); + const Pin *clk_src2 = clk_info2->clkSrc(); + if (clk_src1 < clk_src2) + return -1; + if (clk_src1 > clk_src2) + return 1; + + const Pin *gen_clk_src1 = clk_info1->genClkSrc(); + const Pin *gen_clk_src2 = clk_info2->genClkSrc(); + if (gen_clk_src1 < gen_clk_src2) + return -1; + if (gen_clk_src1 > gen_clk_src2) + return 1; + + bool crpr_on = sta->sdc()->crprActive(); + if (crpr_on) { + const PathVertexRep &crpr_path1 = clk_info1->crprClkPath(); + const PathVertexRep &crpr_path2 = clk_info2->crprClkPath(); + int path_cmp = PathVertexRep::cmp(crpr_path1, crpr_path2); + if (path_cmp != 0) + return path_cmp; + } + + const ClockUncertainties *uncertainties1 = clk_info1->uncertainties(); + const ClockUncertainties *uncertainties2 = clk_info2->uncertainties(); + if (uncertainties1 < uncertainties2) + return -1; + if (uncertainties1 > uncertainties2) + return 1; + + const Arrival &insert1 = clk_info1->insertion(); + const Arrival &insert2 = clk_info2->insertion(); + if (insert1 < insert2) + return -1; + if (insert1 > insert2) + return 1; + + float latency1 = clk_info1->latency(); + float latency2 = clk_info2->latency(); + if (latency1 < latency2) + return -1; + if (latency1 > latency2) + return 1; + + bool is_propagated1 = clk_info1->isPropagated(); + bool is_propagated2 = clk_info2->isPropagated(); + if (!is_propagated1 && is_propagated2) + return -1; + if (is_propagated1 && !is_propagated2) + return 1; + + bool is_gen_clk_src_path1 = clk_info1->isGenClkSrcPath(); + bool is_gen_clk_src_path2 = clk_info2->isGenClkSrcPath(); + if (!is_gen_clk_src_path1 && is_gen_clk_src_path2) + return -1; + if (is_gen_clk_src_path1 && !is_gen_clk_src_path2) + return 1; + + bool is_pulse_clk1 = clk_info1->isPulseClk(); + bool is_pulse_clk2 = clk_info2->isPulseClk(); + if (!is_pulse_clk1 && is_pulse_clk2) + return -1; + if (is_pulse_clk1 && !is_pulse_clk2) + return 1; + + int pulse_clk_sense_index1 = clk_info1->pulseClkSenseTrIndex(); + int pulse_clk_sense_index2 = clk_info2->pulseClkSenseTrIndex(); + if (pulse_clk_sense_index1 < pulse_clk_sense_index2) + return -1; + if (pulse_clk_sense_index1 > pulse_clk_sense_index2) + return 1; + else + return 0; +} + +} // namespace diff --git a/search/ClkInfo.hh b/search/ClkInfo.hh new file mode 100644 index 0000000..988870e --- /dev/null +++ b/search/ClkInfo.hh @@ -0,0 +1,124 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CLK_INFO_H +#define STA_CLK_INFO_H + +#include "DisallowCopyAssign.hh" +#include "Transition.hh" +#include "SearchClass.hh" +#include "PathVertexRep.hh" + +namespace sta { + +class PathVertex; + +class ClkInfo +{ +public: + ClkInfo(ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, + const Pin *gen_clk_src, + bool is_gen_clk_src_path, + const TransRiseFall *pulse_clk_sense, + Arrival insertion, + float latency, + ClockUncertainties *uncertainties, + PathAPIndex path_ap_index, + PathVertexRep &crpr_clk_path, + const StaState *sta); + ~ClkInfo(); + const char *asString(const StaState *sta) const; + ClockEdge *clkEdge() const { return clk_edge_; } + Clock *clock() const; + const Pin *clkSrc() const { return clk_src_; } + bool isPropagated() const { return is_propagated_; } + const Pin *genClkSrc() const { return gen_clk_src_; } + bool isPulseClk() const { return is_pulse_clk_; } + TransRiseFall *pulseClkSense() const; + int pulseClkSenseTrIndex() const { return pulse_clk_sense_; } + 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_; } + // Clock path for the last driver in the clock network used for + // crpr resolution. + PathVertexRep &crprClkPath() { return crpr_clk_path_; } + const PathVertexRep &crprClkPath() const { return crpr_clk_path_; } + const Pin *crprClkPin(const StaState *sta) const; + // Much faster than crprClkPin. + VertexIndex crprClkVertexIndex() const; + // Much faster than crprClkPin != nullptr + bool hasCrprClkPin() const { return !crpr_clk_path_.isNull(); } + bool refsFilter(const StaState *sta) const; + // This clk_info/tag is used for a generated clock source path. + bool isGenClkSrcPath() const { return is_gen_clk_src_path_; } + size_t hash() const { return hash_; } + +protected: + void findHash(const StaState *sta); + +private: + DISALLOW_COPY_AND_ASSIGN(ClkInfo); + + ClockEdge *clk_edge_; + const Pin *clk_src_; + const Pin *gen_clk_src_; + PathVertexRep crpr_clk_path_; + ClockUncertainties *uncertainties_; + Arrival insertion_; + float latency_; + size_t hash_; + unsigned int is_propagated_:1; + unsigned int is_gen_clk_src_path_:1; + unsigned int is_pulse_clk_:1; + unsigned int pulse_clk_sense_:TransRiseFall::index_bit_count; + unsigned int path_ap_index_:path_ap_index_bit_count; +}; + +class ClkInfoLess +{ +public: + explicit ClkInfoLess(const StaState *sta); + ~ClkInfoLess() {} + bool operator()(const ClkInfo *clk_info1, + const ClkInfo *clk_info2) const; + +protected: + const StaState *sta_; +}; + +class ClkInfoHash +{ +public: + size_t operator()(const ClkInfo *clk_info); +}; + +class ClkInfoEqual +{ +public: + ClkInfoEqual(const StaState *sta); + bool operator()(const ClkInfo *clk_info1, + const ClkInfo *clk_info2); + +protected: + const StaState *sta_; +}; + +} // namespace +#endif diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc new file mode 100644 index 0000000..d6a268f --- /dev/null +++ b/search/ClkSkew.cc @@ -0,0 +1,342 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include // abs +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "Report.hh" +#include "Debug.hh" +#include "Fuzzy.hh" +#include "Units.hh" +#include "TimingArc.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Sdc.hh" +#include "Bfs.hh" +#include "PathVertex.hh" +#include "StaState.hh" +#include "PathAnalysisPt.hh" +#include "SearchPred.hh" +#include "Search.hh" +#include "Crpr.hh" +#include "ClkSkew.hh" + +namespace sta { + +using std::abs; + +class ClkSkew +{ +public: + ClkSkew(); + ClkSkew(PathVertex *src_path, + PathVertex *tgt_path, + StaState *sta); + ClkSkew(ClkSkew &clk_skew); + void copy(ClkSkew &clk_skew); + PathVertex *srcPath() { return &src_path_; } + PathVertex *tgtPath() { return &tgt_path_; } + float srcLatency(StaState *sta); + float tgtLatency(StaState *sta); + Crpr crpr(StaState *sta); + float skew() const { return skew_; } + +private: + PathVertex src_path_; + PathVertex tgt_path_; + float skew_; + + DISALLOW_COPY_AND_ASSIGN(ClkSkew); +}; + +ClkSkew::ClkSkew() : + skew_(0.0) +{ +} + +ClkSkew::ClkSkew(PathVertex *src_path, + PathVertex *tgt_path, + StaState *sta) +{ + src_path_.copy(src_path); + tgt_path_.copy(tgt_path); + skew_ = srcLatency(sta) - tgtLatency(sta) - delayAsFloat(crpr(sta)); +} + +ClkSkew::ClkSkew(ClkSkew &clk_skew) +{ + copy(clk_skew); +} + +void +ClkSkew::copy(ClkSkew &clk_skew) +{ + src_path_.copy(clk_skew.src_path_); + tgt_path_.copy(clk_skew.tgt_path_); + skew_ = clk_skew.skew_; +} + +float +ClkSkew::srcLatency(StaState *sta) +{ + Arrival src_arrival = src_path_.arrival(sta); + return delayAsFloat(src_arrival) - src_path_.clkEdge(sta)->time(); +} + +float +ClkSkew::tgtLatency(StaState *sta) +{ + Arrival tgt_arrival = tgt_path_.arrival(sta); + return delayAsFloat(tgt_arrival) - tgt_path_.clkEdge(sta)->time(); +} + +Crpr +ClkSkew::crpr(StaState *sta) +{ + CheckCrpr *check_crpr = sta->search()->checkCrpr(); + return check_crpr->checkCrpr(&src_path_, &tgt_path_); +} + +//////////////////////////////////////////////////////////////// + +ClkSkews::ClkSkews(StaState *sta) : + StaState(sta) +{ +} + +void +ClkSkews::reportClkSkew(ClockSet *clks, + const Corner *corner, + const SetupHold *setup_hold, + int digits) +{ + ClkSkewMap skews; + findClkSkew(clks, corner, setup_hold, skews); + + // Sort the clocks to report in a stable order. + ClockSeq sorted_clks; + ClockSet::Iterator clk_iter1(clks); + while (clk_iter1.hasNext()) { + Clock *clk = clk_iter1.next(); + sorted_clks.push_back(clk); + } + sort(sorted_clks, ClkNameLess()); + + Unit *time_unit = units_->timeUnit(); + ClockSeq::Iterator clk_iter2(sorted_clks); + while (clk_iter2.hasNext()) { + Clock *clk = clk_iter2.next(); + report_->print("Clock %s\n", clk->name()); + ClkSkew *clk_skew = skews.findKey(clk); + if (clk_skew) { + report_->print("Latency CRPR Skew\n"); + PathVertex *src_path = clk_skew->srcPath(); + PathVertex *tgt_path = clk_skew->tgtPath(); + report_->print("%s %s\n", + sdc_network_->pathName(src_path->pin(this)), + src_path->transition(this)->asString()); + report_->print("%7s\n", + time_unit->asString(clk_skew->srcLatency(this), digits)); + report_->print("%s %s\n", + sdc_network_->pathName(tgt_path->pin(this)), + tgt_path->transition(this)->asString()); + report_->print("%7s %7s %7s\n", + time_unit->asString(clk_skew->tgtLatency(this), digits), + time_unit->asString(delayAsFloat(-clk_skew->crpr(this)), digits), + time_unit->asString(clk_skew->skew(), digits)); + } + else + report_->print("No launch/capture paths found.\n"); + report_->print("\n"); + } + + skews.deleteContents(); +} + +void +ClkSkews::findClkSkew(ClockSet *clks, + const Corner *corner, + const SetupHold *setup_hold, + ClkSkewMap &skews) +{ + VertexSet::ConstIterator reg_clk_iter(graph_->regClkVertices()); + while (reg_clk_iter.hasNext()) { + Vertex *src_vertex = reg_clk_iter.next(); + if (hasClkPaths(src_vertex, clks)) { + VertexOutEdgeIterator edge_iter(src_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role()->genericRole() == TimingRole::regClkToQ()) { + Vertex *q_vertex = edge->to(graph_); + TransRiseFall *tr = edge->timingArcSet()->isRisingFallingEdge(); + TransRiseFallBoth *src_tr = tr + ? tr->asRiseFallBoth() + : TransRiseFallBoth::riseFall(); + findClkSkewFrom(src_vertex, q_vertex, src_tr, clks, + corner, setup_hold, skews); + } + } + } + } +} + +bool +ClkSkews::hasClkPaths(Vertex *vertex, + ClockSet *clks) +{ + VertexPathIterator path_iter(vertex, this); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + Clock *path_clk = path->clock(this); + if (clks->hasKey(path_clk)) + return true; + } + return false; +} + +void +ClkSkews::findClkSkewFrom(Vertex *src_vertex, + Vertex *q_vertex, + TransRiseFallBoth *src_tr, + ClockSet *clks, + const Corner *corner, + const SetupHold *setup_hold, + ClkSkewMap &skews) +{ + VertexSet endpoints; + findFanout(q_vertex, endpoints); + VertexSet::Iterator end_iter(endpoints); + while (end_iter.hasNext()) { + Vertex *end = end_iter.next(); + VertexInEdgeIterator edge_iter(end, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + TimingRole *role = edge->role(); + if (role->isTimingCheck() + && ((setup_hold == SetupHold::max() + && role->genericRole() == TimingRole::setup()) + || ((setup_hold == SetupHold::min() + && role->genericRole() == TimingRole::hold())))) { + Vertex *tgt_vertex = edge->from(graph_); + TransRiseFall *tgt_tr1 = edge->timingArcSet()->isRisingFallingEdge(); + TransRiseFallBoth *tgt_tr = tgt_tr1 + ? tgt_tr1->asRiseFallBoth() + : TransRiseFallBoth::riseFall(); + findClkSkew(src_vertex, src_tr, tgt_vertex, tgt_tr, + clks, corner, setup_hold, skews); + } + } + } +} + +void +ClkSkews::findClkSkew(Vertex *src_vertex, + TransRiseFallBoth *src_tr, + Vertex *tgt_vertex, + TransRiseFallBoth *tgt_tr, + ClockSet *clks, + const Corner *corner, + const SetupHold *setup_hold, + ClkSkewMap &skews) +{ + Unit *time_unit = units_->timeUnit(); + const SetupHold *tgt_min_max = setup_hold->opposite(); + VertexPathIterator src_iter(src_vertex, this); + while (src_iter.hasNext()) { + PathVertex *src_path = src_iter.next(); + Clock *src_clk = src_path->clock(this); + if (src_tr->matches(src_path->transition(this)) + && src_path->minMax(this) == setup_hold + && clks->hasKey(src_clk)) { + Corner *src_corner = src_path->pathAnalysisPt(this)->corner(); + if (corner == nullptr + || src_corner == corner) { + VertexPathIterator tgt_iter(tgt_vertex, this); + while (tgt_iter.hasNext()) { + PathVertex *tgt_path = tgt_iter.next(); + Clock *tgt_clk = tgt_path->clock(this); + if (tgt_clk == src_clk + && tgt_tr->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, this); + ClkSkew *clk_skew = skews.findKey(src_clk); + debugPrint8(debug_, "clk_skew", 2, "%s %s %s -> %s %s %s crpr = %s skew = %s\n", + network_->pathName(src_path->pin(this)), + src_path->transition(this)->asString(), + time_unit->asString(probe.srcLatency(this)), + network_->pathName(tgt_path->pin(this)), + tgt_path->transition(this)->asString(), + time_unit->asString(probe.tgtLatency(this)), + delayAsString(probe.crpr(this), this), + time_unit->asString(probe.skew())); + if (clk_skew == nullptr) { + clk_skew = new ClkSkew(probe); + skews[src_clk] = clk_skew; + } + else if (fuzzyGreater(probe.skew(), clk_skew->skew())) + clk_skew->copy(probe); + } + } + } + } + } +} + +class FanOutSrchPred : public SearchPred0 +{ +public: + FanOutSrchPred(const StaState *sta); + virtual bool searchThru(Edge *edge); +}; + +FanOutSrchPred::FanOutSrchPred(const StaState *sta) : + SearchPred0(sta) +{ +} + +bool +FanOutSrchPred::searchThru(Edge *edge) +{ + TimingRole *role = edge->role(); + return role == TimingRole::wire() + || role == TimingRole::combinational() + || role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable(); +} + +void +ClkSkews::findFanout(Vertex *from, + // Return value. + VertexSet &endpoints) +{ + debugPrint1(debug_, "fanout", 1, "%s\n", + from->name(sdc_network_)); + FanOutSrchPred pred(this); + BfsFwdIterator fanout_iter(BfsIndex::other, &pred, this); + fanout_iter.enqueue(from); + while (fanout_iter.hasNext()) { + Vertex *fanout = fanout_iter.next(); + if (fanout->hasChecks()) { + debugPrint1(debug_, "fanout", 1, " endpoint %s\n", + fanout->name(sdc_network_)); + endpoints.insert(fanout); + } + fanout_iter.enqueueAdjacentVertices(fanout); + } +} + +} // namespace diff --git a/search/ClkSkew.hh b/search/ClkSkew.hh new file mode 100644 index 0000000..ef6eff8 --- /dev/null +++ b/search/ClkSkew.hh @@ -0,0 +1,69 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CLK_SKEW_H +#define STA_CLK_SKEW_H + +#include "Map.hh" +#include "SdcClass.hh" + +namespace sta { + +class ClkSkew; + +typedef Map ClkSkewMap; + +// Find and report min clock skews. +class ClkSkews : public StaState +{ +public: + ClkSkews(StaState *sta); + // Report clk skews for clks. + void reportClkSkew(ClockSet *clks, + const Corner *corner, + const SetupHold *setup_hold, + int digits); + +protected: + void findClkSkew(ClockSet *clks, + const Corner *corner, + const SetupHold *setup_hold, + ClkSkewMap &skews); + bool hasClkPaths(Vertex *vertex, + ClockSet *clks); + void findClkSkewFrom(Vertex *src_vertex, + Vertex *q_vertex, + TransRiseFallBoth *src_tr, + ClockSet *clks, + const Corner *corner, + const SetupHold *setup_hold, + ClkSkewMap &skews); + void findClkSkew(Vertex *src_vertex, + TransRiseFallBoth *src_tr, + Vertex *tgt_vertex, + TransRiseFallBoth *tgt_tr, + ClockSet *clks, + const Corner *corner, + const SetupHold *setup_hold, + ClkSkewMap &skews); + void findFanout(Vertex *from, + // Return value. + VertexSet &endpoints); +}; + +} // namespace +#endif + diff --git a/search/Corner.cc b/search/Corner.cc new file mode 100644 index 0000000..c5cbe74 --- /dev/null +++ b/search/Corner.cc @@ -0,0 +1,498 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Sdc.hh" +#include "Parasitics.hh" +#include "DcalcAnalysisPt.hh" +#include "PathAnalysisPt.hh" +#include "Corner.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() +{ + DcalcAnalysisPtSeq::Iterator ap_iter(dcalc_analysis_pts_); + while (ap_iter.hasNext()) { + DcalcAnalysisPt *dcalc_ap = ap_iter.next(); + 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; + StringSet::Iterator name_iter(corner_names); + while (name_iter.hasNext()) { + const char *name = name_iter.next(); + Corner *corner = new Corner(name, index); + corners_.push_back(corner); + // Use the copied name in the map. + corner_map_[corner->name()] = corner; + index++; + } + updateCornerParasiticAnalysisPts(); + makeAnalysisPts(); +} + +void +Corners::makeParasiticAnalysisPtsSingle() +{ + if (parasitic_analysis_pts_.size() != 1) { + parasitics_->deleteParasitics(); + parasitic_analysis_pts_.deleteContentsClear(); + ParasiticAnalysisPt *ap = new ParasiticAnalysisPt("min_max", 0, + MinMax::max()); + parasitic_analysis_pts_.push_back(ap); + updateCornerParasiticAnalysisPts(); + } +} + +void +Corners::makeParasiticAnalysisPtsMinMax() +{ + if (parasitic_analysis_pts_.size() != 2) { + parasitics_->deleteParasitics(); + parasitic_analysis_pts_.deleteContentsClear(); + parasitic_analysis_pts_.resize(MinMax::index_count); + for (auto min_max : MinMax::range()) { + int mm_index = min_max->index(); + ParasiticAnalysisPt *ap = new ParasiticAnalysisPt(min_max->asString(), + mm_index, + min_max); + parasitic_analysis_pts_[mm_index] = ap; + } + updateCornerParasiticAnalysisPts(); + } +} + +void +Corners::updateCornerParasiticAnalysisPts() +{ + CornerSeq::Iterator corner_iter(corners_); + while (corner_iter.hasNext()) { + Corner *corner = corner_iter.next(); + corner->setParasiticAnalysisPtcount(parasitic_analysis_pts_.size()); + ParasiticAnalysisPtSeq::Iterator ap_iter(parasitic_analysis_pts_); + while (ap_iter.hasNext()) { + ParasiticAnalysisPt *ap = ap_iter.next(); + corner->addParasiticAP(ap); + } + } +} + +void +Corners::makeAnalysisPts() +{ + dcalc_analysis_pts_.deleteContentsClear(); + path_analysis_pts_.deleteContentsClear(); + + CornerSeq::Iterator corner_iter(corners_); + while (corner_iter.hasNext()) { + Corner *corner = corner_iter.next(); + makeDcalcAnalysisPts(corner); + makePathAnalysisPts(corner); + } +} + +void +Corners::makeDcalcAnalysisPts(Corner *corner) +{ + DcalcAnalysisPt *min_ap, *max_ap; + switch (sdc_->analysisType()) { + case AnalysisType::single: + corner->setDcalcAnalysisPtcount(1); + makeDcalcAnalysisPt(corner, MinMax::max(), MinMax::min()); + 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_(stringCopy(name)), + index_(index), + path_analysis_pts_(MinMax::index_count) +{ +} + +Corner::~Corner() +{ + stringDelete(name_); +} + +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 { + internalError("unknown parasitic analysis point count"); + return nullptr; + } +} + +void +Corner::setParasiticAnalysisPtcount(int ap_count) +{ + parasitic_analysis_pts_.resize(ap_count); +} + +void +Corner::addParasiticAP(ParasiticAnalysisPt *ap) +{ + if (parasitic_analysis_pts_.size() == 1) + parasitic_analysis_pts_[0] = ap; + else + parasitic_analysis_pts_[ap->minMax()->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 { + internalError("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); +} + +LibertySeq * +Corner::libertyLibraries(const MinMax *min_max) +{ + return &liberty_[min_max->index()]; +} + +int +Corner::libertyIndex(const MinMax *min_max) const +{ + return index_ * MinMax::index_count + min_max->index(); +} + +//////////////////////////////////////////////////////////////// + +CornerIterator::CornerIterator(const StaState *sta) : + iter_(sta->corners()->corners()) +{ +} + +bool +CornerIterator::hasNext() +{ + return iter_.hasNext(); +} + +Corner * +CornerIterator::next() +{ + return iter_.next(); +} + +//////////////////////////////////////////////////////////////// + +DcalcAnalysisPtIterator::DcalcAnalysisPtIterator(const StaState *sta) : + ap_iter_(sta->corners()->dcalcAnalysisPts()) +{ +} + +bool +DcalcAnalysisPtIterator::hasNext() +{ + return ap_iter_.hasNext(); +} + +DcalcAnalysisPt * +DcalcAnalysisPtIterator::next() +{ + return ap_iter_.next(); +} + +//////////////////////////////////////////////////////////////// + +PathAnalysisPtIterator::PathAnalysisPtIterator(const StaState *sta) : + ap_iter_(sta->corners()->pathAnalysisPts()) +{ +} + +bool +PathAnalysisPtIterator::hasNext() +{ + return ap_iter_.hasNext(); +} + +PathAnalysisPt * +PathAnalysisPtIterator::next() +{ + return ap_iter_.next(); +} + +} // namespace diff --git a/search/Corner.hh b/search/Corner.hh new file mode 100644 index 0000000..e98c89e --- /dev/null +++ b/search/Corner.hh @@ -0,0 +1,188 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CORNER_H +#define STA_CORNER_H + +#include "DisallowCopyAssign.hh" +#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); + virtual ~Corners(); + void clear(); + int count() const; + bool multiCorner() const; + Corner *findCorner(const char *corner); + Corner *findCorner(int corner_index); + void makeCorners(StringSet *corner_names); + void analysisTypeChanged(); + void operatingConditionsChanged(); + + void makeParasiticAnalysisPtsSingle(); + void makeParasiticAnalysisPtsMinMax(); + 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 updateCornerParasiticAnalysisPts(); + 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_; + + DISALLOW_COPY_AND_ASSIGN(Corners); +}; + +class Corner +{ +public: + Corner(const char *name, + int index); + ~Corner(); + const char *name() const { return name_; } + 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); + LibertySeq *libertyLibraries(const MinMax *min_max); + int libertyIndex(const MinMax *min_max) const; + +protected: + void setParasiticAnalysisPtcount(int ap_count); + void addParasiticAP(ParasiticAnalysisPt *path_ap); + void setDcalcAnalysisPtcount(DcalcAPIndex ap_count); + void addDcalcAP(DcalcAnalysisPt *dcalc_ap); + void addPathAP(PathAnalysisPt *path_ap); + +private: + const char *name_; + int index_; + ParasiticAnalysisPtSeq parasitic_analysis_pts_; + DcalcAnalysisPtSeq dcalc_analysis_pts_; + PathAnalysisPtSeq path_analysis_pts_; + LibertySeq liberty_[MinMax::index_count]; + + friend class Corners; + DISALLOW_COPY_AND_ASSIGN(Corner); +}; + +// Obsolete. Use range iterator. +// for (auto corner : *sta->corners()) {} +class CornerIterator : public Iterator +{ +public: + explicit CornerIterator(const StaState *sta); + virtual ~CornerIterator() {} + virtual bool hasNext(); + virtual Corner *next(); + +protected: + CornerSeq::ConstIterator iter_; + +private: + DISALLOW_COPY_AND_ASSIGN(CornerIterator); +}; + +// Obsolete. Use range iterator. +// for (auto dcalc_ap : sta->corners()->dcalcAnalysisPts()) {} +class DcalcAnalysisPtIterator : public Iterator +{ +public: + explicit DcalcAnalysisPtIterator(const StaState *sta); + virtual ~DcalcAnalysisPtIterator() {} + virtual bool hasNext(); + virtual DcalcAnalysisPt *next(); + +protected: + DcalcAnalysisPtSeq::ConstIterator ap_iter_; + +private: + DISALLOW_COPY_AND_ASSIGN(DcalcAnalysisPtIterator); +}; + +// Obsolete. Use range iterator. +// for (auto path_ap : sta->corners()->pathAnalysisPts()) {} +class PathAnalysisPtIterator : public Iterator +{ +public: + explicit PathAnalysisPtIterator(const StaState *sta); + virtual ~PathAnalysisPtIterator() {} + virtual bool hasNext(); + virtual PathAnalysisPt *next(); + +protected: + PathAnalysisPtSeq::ConstIterator ap_iter_; + +private: + DISALLOW_COPY_AND_ASSIGN(PathAnalysisPtIterator); +}; + +} // namespace +#endif diff --git a/search/Crpr.cc b/search/Crpr.cc new file mode 100644 index 0000000..4acc4f5 --- /dev/null +++ b/search/Crpr.cc @@ -0,0 +1,398 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include // abs +#include +#include "Machine.hh" +#include "Debug.hh" +#include "Vector.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Sdc.hh" +#include "PathVertex.hh" +#include "PathVertexRep.hh" +#include "Path.hh" +#include "PathAnalysisPt.hh" +#include "ClkInfo.hh" +#include "Tag.hh" +#include "TagGroup.hh" +#include "VisitPathEnds.hh" +#include "PathEnd.hh" +#include "Search.hh" +#include "Genclks.hh" +#include "Crpr.hh" + +namespace sta { + +using std::min; +using std::abs; + +CheckCrpr::CheckCrpr(StaState *sta) : + StaState(sta) +{ +} + +PathVertex * +CheckCrpr::clkPathPrev(const PathVertex *path, + PathVertex &tmp) + +{ + Vertex *vertex = path->vertex(this); + int arrival_index; + bool exists; + path->arrivalIndex(arrival_index, exists); + return clkPathPrev(vertex, arrival_index, tmp); +} + +PathVertex * +CheckCrpr::clkPathPrev(Vertex *vertex, + int arrival_index, + PathVertex &tmp) +{ + PathVertexRep *prevs = vertex->prevPaths(); + if (prevs) { + PathVertexRep *prev = &prevs[arrival_index]; + if (prev->isNull()) + return nullptr; + else { + tmp.init(graph_->vertex(prev->vertexIndex()), + search_->tag(prev->tagIndex()), this); + return &tmp; + } + } + else + internalError("missing prev paths"); +} + +//////////////////////////////////////////////////////////////// + +// Find the maximum possible crpr (clock min/max delta delay) for a +// path from it's ClkInfo. +Arrival +CheckCrpr::maxCrpr(ClkInfo *clk_info) +{ + const PathVertexRep &crpr_clk_path = clk_info->crprClkPath(); + if (!crpr_clk_path.isNull()) { + PathVertex crpr_clk_vpath(crpr_clk_path, this); + if (!crpr_clk_vpath.isNull()) { + Arrival other_arrival = otherMinMaxArrival(&crpr_clk_vpath); + float crpr_diff = abs(delayAsFloat(crpr_clk_vpath.arrival(this), + EarlyLate::late(), + this) + - delayAsFloat(other_arrival, EarlyLate::early(), + this)); + return crpr_diff; + } + } + return 0.0F; +} + +Arrival +CheckCrpr::otherMinMaxArrival(const PathVertex *path) +{ + PathAnalysisPt *other_ap = path->pathAnalysisPt(this)->tgtClkAnalysisPt(); + Tag *tag = path->tag(this); + VertexPathIterator other_iter(path->vertex(this), + path->transition(this), + other_ap, this); + while (other_iter.hasNext()) { + PathVertex *other = other_iter.next(); + if (tagMatchCrpr(other->tag(this), tag)) + return other->arrival(this); + } + // No corresponding path found. + // Match the arrival so the difference is zero. + return path->arrival(this); +} + +Crpr +CheckCrpr::checkCrpr(const Path *src_path, + const PathVertex *tgt_clk_path) +{ + Crpr crpr; + Pin *crpr_pin; + checkCrpr(src_path, tgt_clk_path, crpr, crpr_pin); + return crpr; +} + +void +CheckCrpr::checkCrpr(const Path *src_path, + const PathVertex *tgt_clk_path, + // Return values. + Crpr &crpr, + Pin *&crpr_pin) +{ + crpr = 0.0; + crpr_pin = nullptr; + if (sdc_->crprActive() + && src_path && tgt_clk_path) { + bool same_pin = (sdc_->crprMode() == CrprMode::same_pin); + checkCrpr1(src_path, tgt_clk_path, same_pin, crpr, crpr_pin); + } +} + +void +CheckCrpr::checkCrpr1(const Path *src_path, + const PathVertex *tgt_clk_path, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&crpr_pin) +{ + crpr = 0.0; + crpr_pin = nullptr; + ClkInfo *src_clk_info = src_path->tag(this)->clkInfo(); + ClkInfo *tgt_clk_info = tgt_clk_path->tag(this)->clkInfo(); + Clock *src_clk = src_clk_info->clock(); + Clock *tgt_clk = tgt_clk_info->clock(); + const PathVertex src_clk_path1(src_clk_info->crprClkPath(), this); + const PathVertex *src_clk_path = + src_clk_path1.isNull() ? nullptr : &src_clk_path1; + const MinMax *src_clk_min_max = + src_clk_path ? src_clk_path->minMax(this) : src_path->minMax(this); + if (crprPossible(src_clk, tgt_clk) + // Note that crpr clk min/max is NOT the same as the path min max. + // For path from latches that are borrowing the enable path + // is from the opposite min/max of the data. + && src_clk_min_max != tgt_clk_path->minMax(this) + && (src_clk_path != nullptr + || src_clk->isGenerated())) { + // Src path from input port clk path can only be from generated clk path. + PathVertex port_clk_path; + if (src_clk_path == nullptr) { + portClkPath(src_clk_info->clkEdge(), + src_clk_info->clkSrc(), + src_path->pathAnalysisPt(this), + port_clk_path); + src_clk_path = &port_clk_path; + } + findCrpr(src_clk_path, tgt_clk_path, same_pin, crpr, crpr_pin); + } +} + +// Find the clk path for an input/output port. +void +CheckCrpr::portClkPath(const ClockEdge *clk_edge, + const Pin *clk_src_pin, + const PathAnalysisPt *path_ap, + // Return value. + PathVertex &genclk_path) +{ + Vertex *clk_vertex = graph_->pinDrvrVertex(clk_src_pin); + VertexPathIterator path_iter(clk_vertex, clk_edge->transition(), + path_ap, this); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + if (path->clkEdge(this) == clk_edge + && path->isClock(this)) { + genclk_path.copy(path); + break; + } + } +} + +void +CheckCrpr::findCrpr(const PathVertex *src_clk_path, + const PathVertex *tgt_clk_path, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&crpr_pin) +{ + crpr = 0.0; + crpr_pin = nullptr; + const PathVertex *src_clk_path1 = src_clk_path; + const PathVertex *tgt_clk_path1 = tgt_clk_path; + PathVertexSeq src_gclk_paths, tgt_gclk_paths; + if (src_clk_path1->clkInfo(this)->clkSrc() + != tgt_clk_path1->clkInfo(this)->clkSrc()) { + // Push src/tgt genclk src paths into a vector, + // The last genclk src path is at index 0. + genClkSrcPaths(src_clk_path1, src_gclk_paths); + genClkSrcPaths(tgt_clk_path1, tgt_gclk_paths); + // Search from the first gen clk toward the end + // of the path to find a common root pin. + int i = src_gclk_paths.size() - 1; + int j = tgt_gclk_paths.size() - 1; + for (; i >= 0 && j >= 0; i--, j--) { + PathVertex &src_path = src_gclk_paths[i]; + PathVertex &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]; + } + else + break; + } + } + const PathVertex *src_clk_path2 = src_clk_path1; + const PathVertex *tgt_clk_path2 = tgt_clk_path1; + PathVertex tmp1, tmp2; + // src_clk_path and tgt_clk_path are now in the same (gen)clk src path. + // Use the vertex levels to back up the deeper path to see if they + // overlap. + while (src_clk_path2 && tgt_clk_path2 + && src_clk_path2->pin(this) != tgt_clk_path2->pin(this)) { + Level src_level = src_clk_path2->vertex(this)->level(); + Level tgt_level = tgt_clk_path2->vertex(this)->level(); + if (src_level >= tgt_level) + src_clk_path2 = clkPathPrev(src_clk_path2, tmp1); + if (tgt_level >= src_level) + tgt_clk_path2 = clkPathPrev(tgt_clk_path2, tmp2); + } + if (src_clk_path2 && tgt_clk_path2 + && (src_clk_path2->transition(this) == tgt_clk_path2->transition(this) + || same_pin)) { + debugPrint1(debug_, "crpr", 2, "crpr pin %s\n", + network_->pathName(src_clk_path2->pin(this))); + crpr = findCrpr1(src_clk_path2, tgt_clk_path2); + crpr_pin = src_clk_path2->pin(this); + } +} + +void +CheckCrpr::genClkSrcPaths(const PathVertex *path, + PathVertexSeq &gclk_paths) +{ + ClkInfo *clk_info = path->clkInfo(this); + ClockEdge *clk_edge = clk_info->clkEdge(); + const Pin *clk_src = clk_info->clkSrc(); + PathAnalysisPt *path_ap = path->pathAnalysisPt(this); + gclk_paths.push_back(path); + while (clk_edge->clock()->isGenerated()) { + PathVertex genclk_path; + search_->genclks()->srcPath(clk_edge, clk_src, path_ap, genclk_path); + if (genclk_path.isNull()) + break; + clk_info = genclk_path.clkInfo(this); + clk_src = clk_info->clkSrc(); + clk_edge = clk_info->clkEdge(); + gclk_paths.push_back(genclk_path); + } +} + +Crpr +CheckCrpr::findCrpr1(const PathVertex *src_clk_path, + const PathVertex *tgt_clk_path) +{ + if (pocv_enabled_) { + // 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); + float crpr_sigma2 = delaySigma2(src_clk_path->arrival(this), src_el) + + delaySigma2(src_clk_path->arrival(this), tgt_el); + return makeDelay2(0.0, -crpr_sigma2, -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); + debugPrint1(debug_, "crpr", 2, " src delta %s\n", + delayAsString(src_delta, this)); + debugPrint1(debug_, "crpr", 2, " tgt delta %s\n", + delayAsString(tgt_delta, this)); + float common_delay = min(src_delta, tgt_delta); + debugPrint2(debug_, "crpr", 2, " %s delta %s\n", + network_->pathName(src_clk_path->pin(this)), + delayAsString(common_delay, this)); + return common_delay; + } +} + +Crpr +CheckCrpr::outputDelayCrpr(const Path *src_clk_path, + const ClockEdge *tgt_clk_edge) +{ + Crpr crpr; + Pin *crpr_pin; + outputDelayCrpr(src_clk_path, tgt_clk_edge, crpr, crpr_pin); + return crpr; +} + +void +CheckCrpr::outputDelayCrpr(const Path *src_path, + const ClockEdge *tgt_clk_edge, + // Return values. + Crpr &crpr, + Pin *&crpr_pin) +{ + crpr = 0.0; + crpr_pin = nullptr; + if (sdc_->crprActive()) { + const PathAnalysisPt *path_ap = src_path->pathAnalysisPt(this); + const PathAnalysisPt *tgt_path_ap = path_ap->tgtClkAnalysisPt(); + bool same_pin = (sdc_->crprMode() == CrprMode::same_pin); + outputDelayCrpr1(src_path,tgt_clk_edge,tgt_path_ap, 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) +{ + crpr = 0.0; + crpr_pin = nullptr; + Clock *tgt_clk = tgt_clk_edge->clock(); + Clock *src_clk = src_path->clock(this); + if (tgt_clk->isGenerated() + && crprPossible(src_clk, tgt_clk)) { + PathVertex tgt_genclk_path; + portClkPath(tgt_clk_edge, tgt_clk_edge->clock()->defaultPin(), tgt_path_ap, + tgt_genclk_path); + PathVertex src_clk_path(src_path->clkInfo(this)->crprClkPath(), this); + if (!src_clk_path.isNull()) { + findCrpr(&src_clk_path, &tgt_genclk_path, same_pin, crpr, crpr_pin); + } + } +} + +bool +CheckCrpr::crprPossible(Clock *clk1, + Clock *clk2) +{ + return clk1 && clk2 + && !clk1->isVirtual() + && !clk2->isVirtual() + // Generated clock 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())); +} + +float +CheckCrpr::crprArrivalDiff(const PathVertex *path) +{ + Arrival other_arrival = otherMinMaxArrival(path); + float crpr_diff = abs(delayAsFloat(path->arrival(this), + EarlyLate::late(), this) + - delayAsFloat(other_arrival, + EarlyLate::early(), this)); + return crpr_diff; +} + +} // namespace diff --git a/search/Crpr.hh b/search/Crpr.hh new file mode 100644 index 0000000..ac626ba --- /dev/null +++ b/search/Crpr.hh @@ -0,0 +1,97 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CRPR_H +#define STA_CRPR_H + +#include "DisallowCopyAssign.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" + +namespace sta { + +class CrprPaths; + +// Clock Reconvergence Pessimism Removal. +class CheckCrpr : public StaState +{ +public: + explicit CheckCrpr(StaState *sta); + + // Find the maximum possible crpr (clock min/max delta delay) for path. + Arrival maxCrpr(ClkInfo *clk_info); + // Timing check CRPR. + Crpr checkCrpr(const Path *src_clk_path, + const PathVertex *tgt_clk_path); + void checkCrpr(const Path *src_path, + const PathVertex *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); + + // Previous clk path when crpr is enabled. + PathVertex *clkPathPrev(const PathVertex *path, + PathVertex &tmp); + // For Search::reportArrivals. + PathVertex *clkPathPrev(Vertex *vertex, + int arrival_index, + PathVertex &tmp); + +private: + Arrival otherMinMaxArrival(const PathVertex *path); + void checkCrpr1(const Path *src_path, + const PathVertex *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); + bool crprPossible(Clock *clk1, + Clock *clk2); + void genClkSrcPaths(const PathVertex *path, + PathVertexSeq &gclk_paths); + void findCrpr(const PathVertex *src_clk_path, + const PathVertex *tgt_clk_path, + bool same_pin, + // Return values. + Crpr &crpr, + Pin *&common_pin); + void portClkPath(const ClockEdge *clk_edge, + const Pin *clk_src_pin, + const PathAnalysisPt *path_ap, + // Return value. + PathVertex &port_clk_path); + Crpr findCrpr1(const PathVertex *src_clk_path, + const PathVertex *tgt_clk_path); + float crprArrivalDiff(const PathVertex *path); +}; + +} // namespace +#endif diff --git a/search/FindRegister.cc b/search/FindRegister.cc new file mode 100644 index 0000000..cf73b83 --- /dev/null +++ b/search/FindRegister.cc @@ -0,0 +1,806 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "TimingRole.hh" +#include "FuncExpr.hh" +#include "TimingArc.hh" +#include "Sequential.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Sdc.hh" +#include "SearchPred.hh" +#include "Search.hh" +#include "Clock.hh" +#include "FindRegister.hh" + +namespace sta { + +static TimingSense +pathSenseThru(TimingSense from_sense, + TimingSense thru_sense); +static bool +hasMinPulseWidthCheck(LibertyPort *port); + +// Predicate used for searching from clocks to find registers. +class FindRegClkPred : public SearchPred1 +{ +public: + FindRegClkPred(Clock *clk, + const StaState *sta); + virtual bool searchThru(Edge *edge); + virtual bool searchFrom(const Vertex *from_vertex); + +private: + DISALLOW_COPY_AND_ASSIGN(FindRegClkPred); + + Clock *clk_; +}; + +FindRegClkPred::FindRegClkPred(Clock *clk, + const StaState *sta) : + SearchPred1(sta), + clk_(clk) +{ +} + +bool +FindRegClkPred::searchFrom(const Vertex *from_vertex) +{ + const Sdc *sdc = sta_->sdc(); + const Pin *from_pin = from_vertex->pin(); + return !sdc->clkStopPropagation(from_pin, clk_) + && SearchPred1::searchFrom(from_vertex); +} + +bool +FindRegClkPred::searchThru(Edge *edge) +{ + 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() {} + void visitRegs(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches); + +private: + DISALLOW_COPY_AND_ASSIGN(FindRegVisitor); + void visitRegs(const Pin *clk_pin, + TimingSense clk_sense, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches); + virtual void visitReg(Instance *inst) = 0; + virtual void visitSequential(Instance *inst, + Sequential *seq) = 0; + void visitFanoutRegs(Vertex *from_vertex, + TimingSense from_sense, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches, + SearchPred &clk_pred, + VertexSet &visited_vertices); + void findSequential(const Pin *clk_pin, + Instance *inst, + LibertyCell *cell, + TimingSense clk_sense, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches, + bool &has_seqs, + bool &matches); + bool findInferedSequential(LibertyCell *cell, + TimingSense clk_sense, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches); + bool hasTimingCheck(LibertyCell *cell, + LibertyPort *clk, + LibertyPort *d); + +}; + +FindRegVisitor::FindRegVisitor(StaState *sta) : + StaState(sta) +{ +} + +void +FindRegVisitor::visitRegs(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + 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(); + FindRegClkPred clk_pred(clk, this); + VertexSet visited_vertices; + ClockVertexPinIterator pin_iter(clk); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + visitFanoutRegs(vertex, TimingSense::positive_unate, + clk_tr, 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_tr, edge_triggered, + latches, clk_pred, + visited_vertices); + } + } + } + else { + VertexSet::ConstIterator reg_clk_iter(graph_->regClkVertices()); + while (reg_clk_iter.hasNext()) { + Vertex *vertex = reg_clk_iter.next(); + visitRegs(vertex->pin(), TimingSense::positive_unate, + TransRiseFallBoth::riseFall(), + edge_triggered, latches); + } + } +} + +void +FindRegVisitor::visitFanoutRegs(Vertex *from_vertex, + TimingSense from_sense, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches, + SearchPred &clk_pred, + VertexSet &visited_vertices) +{ + if (!visited_vertices.hasKey(from_vertex) + && clk_pred.searchFrom(from_vertex)) { + visited_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_); + 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_tr, edge_triggered, 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_tr, edge_triggered, latches, + clk_pred, visited_vertices); + } + } +} + +void +FindRegVisitor::visitRegs(const Pin *clk_pin, + TimingSense clk_sense, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + Instance *inst = network_->instance(clk_pin); + LibertyCell *cell = network_->libertyCell(inst); + if (!edge_triggered || !latches + || clk_tr != TransRiseFallBoth::riseFall()) { + bool matches, has_seqs; + findSequential(clk_pin, inst, cell, clk_sense, clk_tr, + edge_triggered, latches, + has_seqs, matches); + if (!has_seqs) + matches = findInferedSequential(cell, clk_sense, clk_tr, + edge_triggered, latches); + if (matches) + visitReg(inst); + } + else + // Do not require sequentials to match if the search is + // not specific to edge triggered, latches, or clock edge. + visitReg(inst); +} + +void +FindRegVisitor::findSequential(const Pin *clk_pin, + Instance *inst, + LibertyCell *cell, + TimingSense clk_sense, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches, + bool &has_seqs, + bool &matches) +{ + has_seqs = false; + matches = false; + LibertyCellSequentialIterator seq_iter(cell); + while (seq_iter.hasNext()) { + has_seqs = true; + Sequential *seq = seq_iter.next(); + if ((seq->isRegister() && edge_triggered) + || (seq->isLatch() && latches)) { + if (clk_tr == TransRiseFallBoth::riseFall()) { + 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_tr == TransRiseFallBoth::rise()) + || (path_sense == TimingSense::negative_unate + && clk_tr == TransRiseFallBoth::fall())) { + visitSequential(inst, seq); + matches = true; + break; + } + } + } + } +} + +bool +FindRegVisitor::findInferedSequential(LibertyCell *cell, + TimingSense clk_sense, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + bool matches = false; + const TransRiseFall *clk_tr1 = clk_tr->asRiseFall(); + LibertyCellTimingArcSetIterator set_iter(cell); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); + TimingArcSetArcIterator arc_iter(set); + TimingArc *arc = arc_iter.next(); + TransRiseFall *arc_clk_tr = arc->fromTrans()->asRiseFall(); + bool tr_matches = (clk_tr == TransRiseFallBoth::riseFall() + || (arc_clk_tr == clk_tr1 + && clk_sense == TimingSense::positive_unate) + || (arc_clk_tr == clk_tr1->opposite() + && clk_sense == TimingSense::negative_unate)); + TimingRole *role = set->role(); + if (tr_matches + && ((role == TimingRole::regClkToQ() + && edge_triggered) + || (role == TimingRole::latchEnToQ() + && latches))) { + matches = true; + break; + } + } + return matches; +} + +bool +FindRegVisitor::hasTimingCheck(LibertyCell *cell, + LibertyPort *clk, + LibertyPort *d) +{ + LibertyCellTimingArcSetIterator set_iter(cell, clk, d); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); + TimingRole *role = set->role(); + if (role->isTimingCheck()) + return true; + } + return false; +} + +class FindRegInstances : public FindRegVisitor +{ +public: + explicit FindRegInstances(StaState *sta); + InstanceSet *findRegs(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches); + +private: + DISALLOW_COPY_AND_ASSIGN(FindRegInstances); + virtual void visitReg(Instance *inst); + virtual void visitSequential(Instance *inst, + Sequential *seq); + + InstanceSet *regs_; +}; + +FindRegInstances::FindRegInstances(StaState *sta) : + FindRegVisitor(sta), + regs_(nullptr) +{ +} + +InstanceSet * +FindRegInstances::findRegs(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + regs_ = new InstanceSet; + visitRegs(clks, clk_tr, edge_triggered, latches); + return regs_; +} + +void +FindRegInstances::visitSequential(Instance *, + Sequential *) +{ +} + +void +FindRegInstances::visitReg(Instance *inst) +{ + regs_->insert(inst); +} + +InstanceSet * +findRegInstances(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches, + StaState *sta) +{ + FindRegInstances find_regs(sta); + return find_regs.findRegs(clks, clk_tr, edge_triggered, latches); +} + +//////////////////////////////////////////////////////////////// + +class FindRegPins : public FindRegVisitor +{ +public: + explicit FindRegPins(StaState *sta); + PinSet *findPins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches); + +protected: + DISALLOW_COPY_AND_ASSIGN(FindRegPins); + 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); + // Sequential expressions to find instance pins. + virtual FuncExpr *seqExpr1(Sequential *seq) = 0; + virtual FuncExpr *seqExpr2(Sequential *seq) = 0; + + PinSet *pins_; +}; + +FindRegPins::FindRegPins(StaState *sta) : + FindRegVisitor(sta), + pins_(nullptr) +{ +} + +PinSet * +FindRegPins::findPins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + pins_ = new PinSet; + visitRegs(clks, clk_tr, edge_triggered, latches); + return pins_; +} + +void +FindRegPins::visitSequential(Instance *inst, + Sequential *seq) +{ + visitExpr(seqExpr1(seq), inst, seq); + visitExpr(seqExpr2(seq), inst, seq); +} + +void +FindRegPins::visitExpr(FuncExpr *expr, + Instance *inst, + Sequential *) +{ + if (expr) { + FuncExprPortIterator port_iter(expr); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + Pin *pin = network_->findPin(inst, port); + if (pin) + pins_->insert(pin); + } + } +} + +void +FindRegPins::visitReg(Instance *inst) +{ + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (matchPin(pin)) + pins_->insert(pin); + } + delete pin_iter; +} + +bool +FindRegPins::matchPin(Pin *) +{ + return true; +} + +class FindRegDataPins : public FindRegPins +{ +public: + explicit FindRegDataPins(StaState *sta); + +private: + DISALLOW_COPY_AND_ASSIGN(FindRegDataPins); + virtual bool matchPin(Pin *pin); + virtual FuncExpr *seqExpr1(Sequential *seq); + virtual FuncExpr *seqExpr2(Sequential *seq); +}; + +FindRegDataPins::FindRegDataPins(StaState *sta) : + FindRegPins(sta) +{ +} + +FuncExpr * +FindRegDataPins::seqExpr1(Sequential *seq) +{ + return seq->data(); +} + +FuncExpr * +FindRegDataPins::seqExpr2(Sequential *) +{ + return nullptr; +} + +bool +FindRegDataPins::matchPin(Pin *pin) +{ + LibertyPort *port = network_->libertyPort(pin); + Vertex *vertex = graph_->pinLoadVertex(pin); + float ignore; + bool has_min_period; + port->minPeriod(ignore, has_min_period); + return vertex && vertex->hasChecks() + && !has_min_period + && !hasMinPulseWidthCheck(port); +} + +static bool +hasMinPulseWidthCheck(LibertyPort *port) +{ + float ignore; + bool exists; + port->minPulseWidth(TransRiseFall::rise(), ignore, exists); + if (exists) + return true; + port->minPulseWidth(TransRiseFall::fall(), ignore, exists); + return exists; +} + +PinSet * +findRegDataPins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches, + StaState *sta) +{ + FindRegDataPins find_regs(sta); + return find_regs.findPins(clks, clk_tr, edge_triggered, latches); +} + +//////////////////////////////////////////////////////////////// + +class FindRegClkPins : public FindRegPins +{ +public: + explicit FindRegClkPins(StaState *sta); + +private: + DISALLOW_COPY_AND_ASSIGN(FindRegClkPins); + virtual bool matchPin(Pin *pin); + virtual FuncExpr *seqExpr1(Sequential *seq); + virtual FuncExpr *seqExpr2(Sequential *seq); +}; + +FindRegClkPins::FindRegClkPins(StaState *sta) : + FindRegPins(sta) +{ +} + +bool +FindRegClkPins::matchPin(Pin *pin) +{ + LibertyPort *port = network_->libertyPort(pin); + LibertyCell *cell = port->libertyCell(); + LibertyCellTimingArcSetIterator set_iter(cell, port, nullptr); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); + TimingRole *role = set->role(); + if (role->isTimingCheck()) + return true; + } + return false; +} + + +FuncExpr * +FindRegClkPins::seqExpr1(Sequential *seq) +{ + return seq->clock(); +} + +FuncExpr * +FindRegClkPins::seqExpr2(Sequential *) +{ + return nullptr; +} + +PinSet * +findRegClkPins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches, + StaState *sta) +{ + FindRegClkPins find_regs(sta); + return find_regs.findPins(clks, clk_tr, edge_triggered, latches); +} + +//////////////////////////////////////////////////////////////// + +class FindRegAsyncPins : public FindRegPins +{ +public: + explicit FindRegAsyncPins(StaState *sta); + +private: + DISALLOW_COPY_AND_ASSIGN(FindRegAsyncPins); + virtual bool matchPin(Pin *pin); + virtual FuncExpr *seqExpr1(Sequential *seq) { return seq->clear(); } + virtual FuncExpr *seqExpr2(Sequential *seq) { return seq->preset(); } +}; + +FindRegAsyncPins::FindRegAsyncPins(StaState *sta) : + FindRegPins(sta) +{ +} + +bool +FindRegAsyncPins::matchPin(Pin *pin) +{ + LibertyPort *port = network_->libertyPort(pin); + LibertyCell *cell = port->libertyCell(); + LibertyCellTimingArcSetIterator set_iter(cell, port, nullptr); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); + TimingRole *role = set->role(); + if (role == TimingRole::regSetClr()) + return true; + } + return false; +} + +PinSet * +findRegAsyncPins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches, + StaState *sta) +{ + FindRegAsyncPins find_regs(sta); + return find_regs.findPins(clks, clk_tr, edge_triggered, latches); +} + +//////////////////////////////////////////////////////////////// + +class FindRegOutputPins : public FindRegPins +{ +public: + explicit FindRegOutputPins(StaState *sta); + +private: + DISALLOW_COPY_AND_ASSIGN(FindRegOutputPins); + virtual bool matchPin(Pin *pin); + virtual void visitSequential(Instance *inst, + Sequential *seq); + void visitOutput(LibertyPort *port, + Instance *inst); + // Unused. + virtual FuncExpr *seqExpr1(Sequential *seq); + virtual FuncExpr *seqExpr2(Sequential *seq); +}; + +FindRegOutputPins::FindRegOutputPins(StaState *sta) : + FindRegPins(sta) +{ +} + +bool +FindRegOutputPins::matchPin(Pin *pin) +{ + LibertyPort *port = network_->libertyPort(pin); + LibertyCell *cell = port->libertyCell(); + LibertyCellTimingArcSetIterator set_iter(cell, nullptr, port); + while (set_iter.hasNext()) { + TimingArcSet *set = set_iter.next(); + TimingRole *role = set->role(); + if (role == TimingRole::regClkToQ() + || role == TimingRole::latchEnToQ() + || role == TimingRole::latchDtoQ()) + return true; + } + return false; +} + +void +FindRegOutputPins::visitSequential(Instance *inst, + Sequential *seq) +{ + visitOutput(seq->output(), inst); + visitOutput(seq->outputInv(), inst); +} + +void +FindRegOutputPins::visitOutput(LibertyPort *port, + Instance *inst) +{ + if (port) { + // Sequential outputs are internal ports. + // Find the output port that is connected to the internal port + // by uses it as its function. + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + LibertyPort *pin_port = network_->libertyPort(pin); + FuncExpr *func = pin_port->function(); + if (func + && func->port() + && func->port() == port) + pins_->insert(pin); + } + delete pin_iter; + } +} + +FuncExpr * +FindRegOutputPins::seqExpr1(Sequential *) +{ + return nullptr; +} + +FuncExpr * +FindRegOutputPins::seqExpr2(Sequential *) +{ + return nullptr; +} + +PinSet * +findRegOutputPins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches, + StaState *sta) +{ + FindRegOutputPins find_regs(sta); + return find_regs.findPins(clks, clk_tr, edge_triggered, latches); +} + +//////////////////////////////////////////////////////////////// + +static TimingSense path_sense_thru[timing_sense_count][timing_sense_count]; + +static void +initPathSenseThru1(TimingSense from, + TimingSense thru, + TimingSense to) +{ + path_sense_thru[int(from)][int(thru)] = to; +} + +void +initPathSenseThru() +{ + initPathSenseThru1(TimingSense::positive_unate, TimingSense::positive_unate, + TimingSense::positive_unate); + initPathSenseThru1(TimingSense::positive_unate, TimingSense::negative_unate, + TimingSense::negative_unate); + initPathSenseThru1(TimingSense::positive_unate, TimingSense::non_unate, + TimingSense::non_unate); + initPathSenseThru1(TimingSense::positive_unate, TimingSense::none, + TimingSense::none); + initPathSenseThru1(TimingSense::positive_unate, TimingSense::unknown, + TimingSense::unknown); + initPathSenseThru1(TimingSense::negative_unate, TimingSense::positive_unate, + TimingSense::negative_unate); + initPathSenseThru1(TimingSense::negative_unate, TimingSense::negative_unate, + TimingSense::positive_unate); + initPathSenseThru1(TimingSense::negative_unate, TimingSense::non_unate, + TimingSense::non_unate); + initPathSenseThru1(TimingSense::negative_unate, TimingSense::none, + TimingSense::none); + initPathSenseThru1(TimingSense::negative_unate, TimingSense::unknown, + TimingSense::unknown); + + initPathSenseThru1(TimingSense::non_unate, TimingSense::positive_unate, + TimingSense::non_unate); + initPathSenseThru1(TimingSense::non_unate, TimingSense::negative_unate, + TimingSense::non_unate); + initPathSenseThru1(TimingSense::non_unate, TimingSense::non_unate, + TimingSense::non_unate); + initPathSenseThru1(TimingSense::non_unate, TimingSense::none, + TimingSense::none); + initPathSenseThru1(TimingSense::non_unate, TimingSense::unknown, + TimingSense::unknown); + + initPathSenseThru1(TimingSense::none, TimingSense::positive_unate, + TimingSense::none); + initPathSenseThru1(TimingSense::none, TimingSense::negative_unate, + TimingSense::none); + initPathSenseThru1(TimingSense::none, TimingSense::non_unate, + TimingSense::none); + initPathSenseThru1(TimingSense::none, TimingSense::none, + TimingSense::none); + initPathSenseThru1(TimingSense::none, TimingSense::unknown, + TimingSense::unknown); + + initPathSenseThru1(TimingSense::unknown, TimingSense::positive_unate, + TimingSense::unknown); + initPathSenseThru1(TimingSense::unknown, TimingSense::negative_unate, + TimingSense::unknown); + initPathSenseThru1(TimingSense::unknown, TimingSense::non_unate, + TimingSense::unknown); + initPathSenseThru1(TimingSense::unknown, TimingSense::none, + TimingSense::unknown); + initPathSenseThru1(TimingSense::unknown, TimingSense::unknown, + TimingSense::unknown); +} + +static TimingSense +pathSenseThru(TimingSense from_sense, + TimingSense thru_sense) +{ + return path_sense_thru[int(from_sense)][int(thru_sense)]; +} + +} // namespace diff --git a/search/FindRegister.hh b/search/FindRegister.hh new file mode 100644 index 0000000..24631e0 --- /dev/null +++ b/search/FindRegister.hh @@ -0,0 +1,47 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_FIND_REGISTER_H +#define STA_FIND_REGISTER_H + +#include "StaState.hh" +#include "LibertyClass.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" + +namespace sta { + +InstanceSet * +findRegInstances(ClockSet *clks, const TransRiseFallBoth *clk_tr, + bool edge_triggered, bool latches, StaState *sta); +PinSet * +findRegDataPins(ClockSet *clks, const TransRiseFallBoth *clk_tr, + bool edge_triggered, bool latches, StaState *sta); +PinSet * +findRegClkPins(ClockSet *clks, const TransRiseFallBoth *clk_tr, + bool edge_triggered, bool latches, StaState *sta); +PinSet * +findRegAsyncPins(ClockSet *clks, const TransRiseFallBoth *clk_tr, + bool edge_triggered, bool latches, StaState *sta); +PinSet * +findRegOutputPins(ClockSet *clks, const TransRiseFallBoth *clk_tr, + bool edge_triggered, bool latches, StaState *sta); + +void +initPathSenseThru(); + +} // namespace +#endif diff --git a/search/GatedClk.cc b/search/GatedClk.cc new file mode 100644 index 0000000..3fe2d91 --- /dev/null +++ b/search/GatedClk.cc @@ -0,0 +1,257 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "FuncExpr.hh" +#include "PortDirection.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Sdc.hh" +#include "Search.hh" +#include "GatedClk.hh" + +namespace sta { + +GatedClk::GatedClk(const StaState *sta) : + StaState(sta) +{ +} + +bool +GatedClk::isGatedClkEnable(Vertex *vertex) 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); + 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 +{ + 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(); + if (enable_port + && enable_port->direction()->isInput() + && !sdc_->isDisableClockGatingCheck(enable_pin) + && !sdc_->isDisableClockGatingCheck(inst) + && eval_pred->searchFrom(enable_vertex)) { + 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_); + 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; + } + } + } + } + } + } +} + +void +GatedClk::gatedClkEnables(Vertex *clk_vertex, + // Return value. + PinSet &enable_pins) +{ + 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)) { + FuncExpr *func = nullptr; + Vertex *gclk_vertex = nullptr; + VertexOutEdgeIterator edge_iter(clk_vertex, graph_); + while (edge_iter.hasNext()) { + 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; + } + } + } + } + } +} + +void +GatedClk::isClkGatingFunc(FuncExpr *func, + 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) + func = func->left(); + if (func->op() == FuncExpr::op_and) + logic_value = LogicValue::one; + else if (func->op() == FuncExpr::op_or) + logic_value = LogicValue::zero; + else { + is_clk_gate = false; + return; + } + + FuncExprSet funcs; + functionClkOperands(func, func->left(), funcs); + 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; + } + } + else { + 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; + } + } + } + } + is_clk_gate = false; +} + +void +GatedClk::functionClkOperands(FuncExpr *root_expr, + FuncExpr *expr, + FuncExprSet &funcs) const +{ + if (expr->op() != root_expr->op()) + funcs.insert(expr); + else { + functionClkOperands(root_expr, expr->left(), funcs); + functionClkOperands(root_expr, expr->right(), funcs); + } +} + +TransRiseFall * +GatedClk::gatedClkActiveTrans(LogicValue active_value, + const MinMax *min_max) const +{ + TransRiseFall *leading_tr; + switch (active_value) { + case LogicValue::one: + case LogicValue::unknown: + leading_tr = TransRiseFall::rise(); + break; + case LogicValue::zero: + leading_tr = TransRiseFall::fall(); + break; + default: + internalError("illegal gated clock active value"); + leading_tr = TransRiseFall::rise(); + break; + } + if (min_max == MinMax::max()) + return leading_tr; + else + return leading_tr->opposite(); +} + +} // namespace diff --git a/search/GatedClk.hh b/search/GatedClk.hh new file mode 100644 index 0000000..aba7fc2 --- /dev/null +++ b/search/GatedClk.hh @@ -0,0 +1,56 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_GATED_CLK_H +#define STA_GATED_CLK_H + +#include "SdcClass.hh" +#include "GraphClass.hh" +#include "SearchClass.hh" + +namespace sta { + +typedef Set FuncExprSet; + +class GatedClk : public StaState +{ +public: + GatedClk(const StaState *sta); + + bool isGatedClkEnable(Vertex *vertex) 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); + TransRiseFall *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; + void functionClkOperands(FuncExpr *root_expr, + FuncExpr *curr_expr, + FuncExprSet &funcs) const; +}; + +} // namespace +#endif diff --git a/search/Genclks.cc b/search/Genclks.cc new file mode 100644 index 0000000..8161d70 --- /dev/null +++ b/search/Genclks.cc @@ -0,0 +1,1310 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Stats.hh" +#include "Debug.hh" +#include "Report.hh" +#include "Network.hh" +#include "PortDirection.hh" +#include "Graph.hh" +#include "Sdc.hh" +#include "ExceptionPath.hh" +#include "Clock.hh" +#include "StaState.hh" +#include "SearchPred.hh" +#include "Bfs.hh" +#include "TagGroup.hh" +#include "Corner.hh" +#include "PathAnalysisPt.hh" +#include "Levelize.hh" +#include "PathVertexRep.hh" +#include "Search.hh" +#include "Genclks.hh" + +namespace sta { + +class GenclkInfo +{ +public: + GenclkInfo(Clock *gclk, + Level gclk_level, + VertexSet *fanins, + FilterPath *src_filter); + ~GenclkInfo(); + EdgeSet *fdbkEdges() const { return fdbk_edges_; } + VertexSet *fanins() const { return fanins_; } + Level gclkLevel() const { return gclk_level_; } + FilterPath *srcFilter() const { return src_filter_; } + FilterPath *pllFilter() const { return pll_filter_; } + void setPllFilter(FilterPath *pll_filter); + void setLatchFdbkEdges(EdgeSet *fdbk_edges); + bool foundLatchFdbkEdges() const { return found_latch_fdbk_edges_; } + void setFoundLatchFdbkEdges(bool found); + +protected: + DISALLOW_COPY_AND_ASSIGN(GenclkInfo); + + Clock *gclk_; + Level gclk_level_; + VertexSet *fanins_; + EdgeSet *fdbk_edges_; + bool found_latch_fdbk_edges_; + FilterPath *src_filter_; + FilterPath *pll_filter_; +}; + +GenclkInfo::GenclkInfo(Clock *gclk, + Level gclk_level, + VertexSet *fanins, + FilterPath *src_filter) : + gclk_(gclk), + gclk_level_(gclk_level), + fanins_(fanins), + fdbk_edges_(nullptr), + found_latch_fdbk_edges_(false), + src_filter_(src_filter), + pll_filter_(nullptr) +{ +} + +GenclkInfo::~GenclkInfo() +{ + delete fanins_; + delete fdbk_edges_; + delete src_filter_; + delete pll_filter_; +} + +void +GenclkInfo::setPllFilter(FilterPath *pll_filter) +{ + pll_filter_ = pll_filter; +} + +void +GenclkInfo::setLatchFdbkEdges(EdgeSet *fdbk_edges) +{ + fdbk_edges_ = fdbk_edges; +} + +void +GenclkInfo::setFoundLatchFdbkEdges(bool found) +{ + found_latch_fdbk_edges_ = found; +} + +//////////////////////////////////////////////////////////////// + +Genclks::Genclks(StaState *sta) : + StaState(sta), + found_insertion_delays_(false) +{ +} + +Genclks::~Genclks() +{ + genclk_info_map_.deleteContentsClear(); + clearSrcPaths(); +} + +void +Genclks::clear() +{ + found_insertion_delays_ = false; + genclk_info_map_.deleteContentsClear(); + clearSrcPaths(); +} + +VertexSet * +Genclks::fanins(const Clock *clk) +{ + GenclkInfo *genclk_info = genclkInfo(clk); + return genclk_info->fanins(); +} + +Vertex * +Genclks::srcPathVertex(const Pin *pin) const +{ + bool is_bidirect = network_->direction(pin)->isBidirect(); + // Insertion delay is to the driver vertex for clks defined on + // bidirect pins. + if (is_bidirect && network_->isLeaf(pin)) + return graph_->pinDrvrVertex(pin); + else + // Insertion delay is to the load vertex for clks defined on + // bidirect ports. + return graph_->pinLoadVertex(pin); +} + +Level +Genclks::clkPinMaxLevel(Clock *clk) const +{ + Level max_level = 0; + ClockVertexPinIterator pin_iter(clk); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + Vertex *vertex = srcPathVertex(pin); + max_level = max(max_level, vertex->level()); + } + return max_level; +} + +class ClockPinMaxLevelLess +{ +public: + explicit ClockPinMaxLevelLess(const Genclks *genclks); + bool operator()(Clock *clk1, + Clock *clk2) const; + +protected: + const Genclks *genclks_; +}; + +ClockPinMaxLevelLess::ClockPinMaxLevelLess(const Genclks *genclks) : + genclks_(genclks) +{ +} + +bool +ClockPinMaxLevelLess::operator()(Clock *clk1, + Clock *clk2) const +{ + return genclks_->clkPinMaxLevel(clk1) < genclks_->clkPinMaxLevel(clk2); +} + +// Generated clock source paths. +// The path between the source clock and generated clock is used +// to find the insertion delay (source latency) when the clock is +// propagated and for reporting path type "full_clock_expanded". +void +Genclks::ensureInsertionDelays() +{ + if (!found_insertion_delays_) { + Stats stats(debug_); + debugPrint0(debug_, "genclk", 1, "find generated clk insertion delays\n"); + + ClockSeq gclks; + for (auto clk : sdc_->clks()) { + if (clk->isGenerated()) { + checkMaster(clk); + 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)); + + ClockSeq::Iterator gclk_iter(gclks); + while (gclk_iter.hasNext()) { + Clock *gclk = gclk_iter.next(); + if (gclk->masterClk()) { + findInsertionDelays(gclk); + if (gclk->pllOut()) + findPllDelays(gclk); + recordSrcPaths(gclk); + } + } + + stats.report("Find generated clk insertion delays"); + found_insertion_delays_ = true; + } +} + +// Similar to ClkTreeSearchPred but ignore constants. +class GenClkMasterSearchPred : public SearchPred +{ +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_; + +private: + DISALLOW_COPY_AND_ASSIGN(GenClkMasterSearchPred); +}; + +GenClkMasterSearchPred::GenClkMasterSearchPred(const StaState *sta) : + SearchPred(), + sta_(sta) +{ +} + +bool +GenClkMasterSearchPred::searchFrom(const Vertex *from_vertex) +{ + return !from_vertex->isDisabledConstraint(); +} + +bool +GenClkMasterSearchPred::searchThru(Edge *edge) +{ + const Sdc *sdc = sta_->sdc(); + TimingRole *role = edge->role(); + // Propagate clocks through constants. + return !(edge->isDisabledLoop() + || edge->isDisabledConstraint() + // Constants disable edge cond expression. + || edge->isDisabledCond() + || sdc->isDisabledCondDefault(edge) + // Register/latch preset/clr edges are disabled by default. + || (!sdc->presetClrArcsEnabled() + && role == TimingRole::regSetClr()) + || (edge->isBidirectInstPath() + && !sdc->bidirectInstPathsEnabled()) + || (edge->isBidirectNetPath() + && !sdc->bidirectNetPathsEnabled())); +} + +bool +GenClkMasterSearchPred::searchTo(const Vertex *) +{ + return true; +} + +void +Genclks::checkMaster(Clock *gclk) +{ + ensureMaster(gclk); + if (gclk->masterClk() == nullptr) + report_->warn("no master clock found for generated clock %s.\n", + gclk->name()); +} + +void +Genclks::ensureMaster(Clock *gclk) +{ + Clock *master_clk = gclk->masterClk(); + if (master_clk == nullptr) { + int master_clk_count = 0; + bool found_master = false; + if (gclk->pllOut()) { + // Search backward from generated clock source pin to a clock pin. + GenClkMasterSearchPred pred(this); + BfsBkwdIterator iter(BfsIndex::other, &pred, this); + seedSrcPins(gclk, iter); + while (iter.hasNext()) { + Vertex *vertex = iter.next(); + Pin *pin = vertex->pin(); + if (sdc_->isVertexPinClock(pin)) { + ClockSet *master_clks = sdc_->findVertexPinClocks(pin); + if (master_clks) { + ClockSet::Iterator master_iter(master_clks); + while (master_iter.hasNext()) { + master_clk = master_iter.next(); + // Master source pin can actually be a clock source pin. + if (master_clk != gclk) { + gclk->setInferedMasterClk(master_clk); + debugPrint2(debug_, "genclk", 2, " %s master clk %s\n", + gclk->name(), + master_clk->name()); + found_master = true; + master_clk_count++; + } + } + } + } + if (found_master) + break; + iter.enqueueAdjacentVertices(vertex); + } + if (master_clk_count > 1) + report_->error("generated clock %s is in the fanout of multiple clocks.\n", + gclk->name()); + } + else { + 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(); + // Master source pin can actually be a clock source pin. + if (master_clk != gclk) { + gclk->setInferedMasterClk(master_clk); + debugPrint2(debug_, "genclk", 2, " %s master clk %s\n", + gclk->name(), + master_clk->name()); + found_master = true; + master_clk_count++; + } + } + } + if (!found_master) { + // Search backward from generated clock source pin to a clock pin. + GenClkMasterSearchPred pred(this); + BfsBkwdIterator iter(BfsIndex::other, &pred, this); + seedSrcPins(gclk, iter); + while (iter.hasNext()) { + Vertex *vertex = iter.next(); + Pin *pin = vertex->pin(); + if (sdc_->isVertexPinClock(pin)) { + ClockSet *master_clks = sdc_->findVertexPinClocks(pin); + if (master_clks) { + ClockSet::Iterator master_iter(master_clks); + if (master_iter.hasNext()) { + master_clk = master_iter.next(); + // Master source pin can actually be a clock source pin. + if (master_clk != gclk) { + gclk->setInferedMasterClk(master_clk); + debugPrint2(debug_, "genclk", 2, " %s master clk %s\n", + gclk->name(), + master_clk->name()); + master_clk_count++; + break; + } + } + } + } + iter.enqueueAdjacentVertices(vertex); + } + } + if (master_clk_count > 1) + report_->error("generated clock %s pin %s is in the fanout of multiple clocks.\n", + gclk->name(), + network_->pathName(src_pin)); + } + } +} + +void +Genclks::seedSrcPins(Clock *clk, + BfsBkwdIterator &iter) +{ + VertexSet src_vertices; + clk->srcPinVertices(src_vertices, network_, graph_); + VertexSet::Iterator vertex_iter(src_vertices); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + iter.enqueue(vertex); + } +} + +//////////////////////////////////////////////////////////////// + +// 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); + +private: + DISALLOW_COPY_AND_ASSIGN(GenClkFaninSrchPred); + + bool combinational_; +}; + +GenClkFaninSrchPred::GenClkFaninSrchPred(Clock *gclk, + const StaState *sta) : + GenClkMasterSearchPred(sta), + combinational_(gclk->combinational()) +{ +} + +bool +GenClkFaninSrchPred::searchFrom(const Vertex *from_vertex) +{ + return !from_vertex->isDisabledConstraint(); +} + +bool +GenClkFaninSrchPred::searchThru(Edge *edge) +{ + const TimingRole *role = edge->role(); + return GenClkMasterSearchPred::searchThru(edge) + && (role == TimingRole::combinational() + || role == TimingRole::wire() + || !combinational_); +} + +bool +GenClkFaninSrchPred::searchTo(const Vertex *) +{ + return true; +} + +void +Genclks::findFanin(Clock *gclk, + // Return value. + VertexSet *fanins) +{ + // Search backward from generated clock source pin to a clock pin. + GenClkFaninSrchPred srch_pred(gclk, this); + BfsBkwdIterator iter(BfsIndex::other, &srch_pred, this); + seedClkVertices(gclk, iter, fanins); + while (iter.hasNext()) { + Vertex *vertex = iter.next(); + if (!fanins->hasKey(vertex)) { + fanins->insert(vertex); + debugPrint2(debug_, "genclk", 2, "gen clk %s fanin %s\n", + gclk->name(), vertex->name(sdc_network_)); + iter.enqueueAdjacentVertices(vertex); + } + } +} + +void +Genclks::seedClkVertices(Clock *clk, + BfsBkwdIterator &iter, + VertexSet *fanins) +{ + ClockVertexPinIterator pin_iter(clk); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + fanins->insert(vertex); + iter.enqueueAdjacentVertices(vertex); + if (bidirect_drvr_vertex) { + fanins->insert(bidirect_drvr_vertex); + iter.enqueueAdjacentVertices(bidirect_drvr_vertex); + } + } +} + +//////////////////////////////////////////////////////////////// + +class GenClkInsertionSearchPred : public SearchPred0, public DynLoopSrchPred +{ +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); + +private: + DISALLOW_COPY_AND_ASSIGN(GenClkInsertionSearchPred); + bool isNonGeneratedClkPin(const Pin *pin) const; + + Clock *gclk_; + GenclkInfo *genclk_info_; +}; + +GenClkInsertionSearchPred::GenClkInsertionSearchPred(Clock *gclk, + TagGroupBldr *tag_bldr, + GenclkInfo *genclk_info, + const StaState *sta) : + SearchPred0(sta), + DynLoopSrchPred(tag_bldr), + gclk_(gclk), + genclk_info_(genclk_info) +{ +} + +bool +GenClkInsertionSearchPred::searchThru(Edge *edge) +{ + 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() + && (sdc->clkThruTristateEnabled() + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())) + && !(fdbk_edges && fdbk_edges->hasKey(edge)) + && loopEnabled(edge, sdc, graph, search); +} + +bool +GenClkInsertionSearchPred::searchTo(const Vertex *to_vertex) +{ + Pin *to_pin = to_vertex->pin(); + return SearchPred0::searchTo(to_vertex) + // Propagate through other generated clock roots but not regular + // clock roots. + && !(!gclk_->vertexPins()->hasKey(to_pin) + && isNonGeneratedClkPin(to_pin)) + && genclk_info_->fanins()->hasKey(const_cast(to_vertex)); +} + +bool +GenClkInsertionSearchPred::isNonGeneratedClkPin(const Pin *pin) const +{ + const Sdc *sdc = sta_->sdc(); + ClockSet *clks = sdc->findVertexPinClocks(pin); + if (clks) { + ClockSet::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + const Clock *clk = clk_iter.next(); + if (!clk->isGenerated()) + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////// + +void +Genclks::findInsertionDelays(Clock *gclk) +{ + debugPrint1(debug_, "genclk", 2, "find gen clk %s insertion\n", + gclk->name()); + GenclkInfo *genclk_info = makeGenclkInfo(gclk); + FilterPath *src_filter = genclk_info->srcFilter(); + GenClkInsertionSearchPred srch_pred(gclk, nullptr, 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. + findSrcArrivals(gclk, insert_iter, genclk_info); + // 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); +} + +GenclkInfo * +Genclks::makeGenclkInfo(Clock *gclk) +{ + FilterPath *src_filter = makeSrcFilter(gclk); + Level gclk_level = clkPinMaxLevel(gclk); + VertexSet *fanins = new VertexSet; + findFanin(gclk, fanins); + GenclkInfo *genclk_info = new GenclkInfo(gclk, gclk_level, fanins, + src_filter); + genclk_info_map_.insert(gclk, genclk_info); + return genclk_info; +} + +GenclkInfo * +Genclks::genclkInfo(const Clock *gclk) const +{ + return genclk_info_map_.findKey(const_cast(gclk)); +} + +FilterPath * +Genclks::srcFilter(Clock *gclk) +{ + GenclkInfo *genclk_info = genclk_info_map_.findKey(gclk); + if (genclk_info) + return genclk_info->srcFilter(); + else + return nullptr; +} + +EdgeSet * +Genclks::latchFdbkEdges(const Clock *clk) +{ + GenclkInfo *genclk_info = genclkInfo(clk); + return genclk_info->fdbkEdges(); +} + +void +Genclks::findLatchFdbkEdges(const Clock *clk) +{ + GenclkInfo *genclk_info = genclkInfo(clk); + if (!genclk_info->foundLatchFdbkEdges()) + findLatchFdbkEdges(clk, genclk_info); +} + +// Generated clock insertion delays propagate through latch D->Q. +// This exposes loops through latches that are not discovered and +// flagged by levelization. Find these loops with a depth first +// search from the master clock source pins and record them to prevent +// the clock insertion search from searching through them. +// +// Because this is relatively expensive to search and it is rare to +// find latches in the clock network it is only called when a latch +// D to Q edge is encountered in the BFS arrival search. +void +Genclks::findLatchFdbkEdges(const Clock *gclk, + GenclkInfo *genclk_info) +{ + Level gclk_level = genclk_info->gclkLevel(); + EdgeSet *fdbk_edges = nullptr; + ClockVertexPinIterator pin_iter(gclk->masterClk()); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + Vertex *vertex = graph_->pinDrvrVertex(pin); + VertexSet path_vertices; + VertexSet visited_vertices; + SearchPred1 srch_pred(this); + findLatchFdbkEdges(vertex, gclk_level, srch_pred, path_vertices, + 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) +{ + if (!visited_vertices.hasKey(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)) { + debugPrint2(debug_, "genclk", 2, " found feedback edge %s -> %s\n", + from_vertex->name(sdc_network_), + to_vertex->name(sdc_network_)); + if (fdbk_edges == nullptr) + fdbk_edges = new EdgeSet; + 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); + } + path_vertices.erase(from_vertex); + } +} + +FilterPath * +Genclks::makeSrcFilter(Clock *gclk) +{ + ClockSet *from_clks = new ClockSet; + from_clks->insert(gclk->masterClk()); + const TransRiseFallBoth *rf = TransRiseFallBoth::riseFall(); + ExceptionFrom *from = sdc_->makeExceptionFrom(nullptr,from_clks,nullptr,rf); + + PinSet *thru_pins = new PinSet; + thru_pins->insert(gclk->srcPin()); + 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); + + return sdc_->makeFilterPath(from, thrus, to); +} + +void +Genclks::seedSrcPins(Clock *gclk, + FilterPath *src_filter, + BfsFwdIterator &insert_iter) +{ + Clock *master_clk = gclk->masterClk(); + ClockVertexPinIterator master_pin_iter(master_clk); + while (master_pin_iter.hasNext()) { + Pin *master_pin = master_pin_iter.next(); + Vertex *vertex = graph_->pinDrvrVertex(master_pin); + debugPrint1(debug_, "genclk", 2, " seed src pin %s\n", + 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 (auto tr : TransRiseFall::range()) { + Tag *tag = makeTag(gclk, master_clk, master_pin, tr, src_filter, + path_ap); + Arrival insert = search_->clockInsertion(master_clk, master_pin, tr, + min_max, early_late, path_ap); + tag_bldr.setArrival(tag, insert, nullptr); + } + } + search_->setVertexArrivals(vertex, &tag_bldr); + insert_iter.enqueueAdjacentVertices(vertex); + } +} + +Tag * +Genclks::makeTag(const Clock *gclk, + const Clock *master_clk, + const Pin *master_pin, + const TransRiseFall *master_tr, + FilterPath *src_filter, + const PathAnalysisPt *path_ap) +{ + ExceptionState *state = src_filter->firstState(); + // If the src pin is one of the master pins the filter is active + // from the get go. + if (master_pin == gclk->srcPin()) + state = state->nextState(); + ExceptionStateSet *states = new ExceptionStateSet; + states->insert(state); + ClkInfo *clk_info = search_->findClkInfo(master_clk->edge(master_tr), + master_pin, true, nullptr, true, + nullptr, 0.0, 0.0, nullptr, + path_ap, nullptr); + return search_->findTag(master_tr, path_ap, clk_info, false, nullptr, false, + states, true); +} + +class GenClkArrivalSearchPred : public EvalPred +{ +public: + GenClkArrivalSearchPred(Clock *gclk, + const StaState *sta); + bool searchThru(Edge *edge); + virtual bool searchTo(const Vertex *to_vertex); + +private: + DISALLOW_COPY_AND_ASSIGN(GenClkArrivalSearchPred); + + bool combinational_; +}; + +GenClkArrivalSearchPred::GenClkArrivalSearchPred(Clock *gclk, + const StaState *sta) : + EvalPred(sta), + combinational_(gclk->combinational()) +{ +} + +bool +GenClkArrivalSearchPred::searchThru(Edge *edge) +{ + const Sdc *sdc = sta_->sdc(); + const TimingRole *role = edge->role(); + return EvalPred::searchThru(edge) + && (role == TimingRole::combinational() + || role->isWire() + || !combinational_) + && (sdc->clkThruTristateEnabled() + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())); +} + +// Override EvalPred::searchTo to search to generated clock pin. +bool +GenClkArrivalSearchPred::searchTo(const Vertex *to_vertex) +{ + return SearchPred0::searchTo(to_vertex); +} + +class GenclkSrcArrivalVisitor : public ArrivalVisitor +{ +public: + GenclkSrcArrivalVisitor(Clock *gclk, + BfsFwdIterator *insert_iter, + GenclkInfo *genclk_info, + const StaState *sta); + virtual VertexVisitor *copy(); + virtual void visit(Vertex *vertex); + +protected: + GenclkSrcArrivalVisitor(Clock *gclk, + BfsFwdIterator *insert_iter, + GenclkInfo *genclk_info, + bool always_to_endpoints, + SearchPred *pred, + const StaState *sta); + + Clock *gclk_; + BfsFwdIterator *insert_iter_; + GenclkInfo *genclk_info_; + GenClkInsertionSearchPred srch_pred_; +}; + +GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk, + BfsFwdIterator *insert_iter, + GenclkInfo *genclk_info, + const StaState *sta): + ArrivalVisitor(sta), + gclk_(gclk), + insert_iter_(insert_iter), + genclk_info_(genclk_info), + srch_pred_(gclk_, tag_bldr_, genclk_info, sta) +{ +} + +// 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), + gclk_(gclk), + insert_iter_(insert_iter), + genclk_info_(genclk_info), + srch_pred_(gclk, tag_bldr_, genclk_info, sta) +{ +} + +VertexVisitor * +GenclkSrcArrivalVisitor::copy() +{ + return new GenclkSrcArrivalVisitor(gclk_, insert_iter_, genclk_info_, + always_to_endpoints_, pred_, sta_); +} + +void +GenclkSrcArrivalVisitor::visit(Vertex *vertex) +{ + const Debug *debug = sta_->debug(); + const Network *sdc_network = sta_->sdcNetwork(); + const Graph *graph = sta_->graph(); + Search *search = sta_->search(); + Genclks *genclks = search->genclks(); + debugPrint1(debug, "genclk", 2, "find gen clk insert arrival %s\n", + vertex->name(sdc_network)); + tag_bldr_->init(vertex); + has_fanin_one_ = graph->hasFaninOne(vertex); + genclks->copyGenClkSrcPaths(vertex, tag_bldr_); + visitFaninPaths(vertex); + // Propagate beyond the clock tree to reach generated clk roots. + insert_iter_->enqueueAdjacentVertices(vertex, &srch_pred_); + search->setVertexArrivals(vertex, tag_bldr_); +} + +void +Genclks::findSrcArrivals(Clock *gclk, + 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); + // 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). + insert_iter.visit(levelize_->maxLevel(), &arrival_visitor); +} + +// Copy existing generated clock source paths from vertex to tag_bldr. +void +Genclks::copyGenClkSrcPaths(Vertex *vertex, + TagGroupBldr *tag_bldr) +{ + Arrival *arrivals = vertex->arrivals(); + if (arrivals) { + PathVertexRep *prev_paths = vertex->prevPaths(); + TagGroup *tag_group = search_->tagGroup(vertex); + ArrivalMap::Iterator arrival_iter(tag_group->arrivalMap()); + while (arrival_iter.hasNext()) { + Tag *tag; + int arrival_index; + arrival_iter.next(tag, arrival_index); + if (tag->isGenClkSrcPath()) { + Arrival arrival = arrivals[arrival_index]; + PathVertexRep *prev_path = prev_paths + ? &prev_paths[arrival_index] + : nullptr; + tag_bldr->setArrival(tag, arrival, prev_path); + } + } + } +} + +//////////////////////////////////////////////////////////////// + +void +Genclks::clearSrcPaths() +{ + genclk_src_paths_.deleteArrayContents(); + genclk_src_paths_.clear(); +} + +int +Genclks::srcPathIndex(const TransRiseFall *clk_tr, + const PathAnalysisPt *path_ap) const +{ + return path_ap->index() * TransRiseFall::index_count + clk_tr->index(); +} + +void +Genclks::recordSrcPaths(Clock *gclk) +{ + int path_count = TransRiseFall::index_count + * corners_->pathAnalysisPtCount(); + + bool divide_by_1 = gclk->isDivideByOneCombinational(); + bool invert = gclk->invert(); + bool has_edges = gclk->edges() != nullptr; + + ClockVertexPinIterator gclk_pin_iter(gclk); + while (gclk_pin_iter.hasNext()) { + Pin *gclk_pin = gclk_pin_iter.next(); + PathVertexRep *src_paths = new PathVertexRep[path_count]; + genclk_src_paths_.insert(ClockPinPair(gclk, gclk_pin), src_paths); + + Vertex *gclk_vertex = srcPathVertex(gclk_pin); + bool found_src_paths = false; + VertexPathIterator path_iter(gclk_vertex, this); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + ClockEdge *src_clk_edge = path->clkEdge(this); + if (src_clk_edge + && matchesSrcFilter(path, gclk)) { + const EarlyLate *early_late = path->minMax(this); + TransRiseFall *src_clk_tr = src_clk_edge->transition(); + const TransRiseFall *tr = path->transition(this); + bool inverting_path = (tr != src_clk_tr); + const PathAnalysisPt *path_ap = path->pathAnalysisPt(this); + int path_index = srcPathIndex(tr, path_ap); + PathVertexRep &src_path = src_paths[path_index]; + if ((!divide_by_1 + || (inverting_path == invert)) + && (!has_edges + || src_clk_tr == gclk->masterClkEdgeTr(tr)) + && (src_path.isNull() + || fuzzyGreater(path->arrival(this), + src_path.arrival(this), + early_late))) { + debugPrint4(debug_, "genclk", 2, " %s insertion %s %s %s\n", + network_->pathName(gclk_pin), + early_late->asString(), + tr->asString(), + delayAsString(path->arrival(this), this)); + src_path.init(path, this); + found_src_paths = true; + } + } + } + if (!found_src_paths + // Don't warn if the master clock is ideal. + && gclk->masterClk() + && gclk->masterClk()->isPropagated()) + report_->warn("generated clock %s source pin %s missing paths from master clock %s.\n", + gclk->name(), + network_->pathName(gclk_pin), + gclk->masterClk()->name()); + } +} + +bool +Genclks::matchesSrcFilter(Path *path, + 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(); + ExceptionPath *except = state->exception(); + if (except->isFilter() + && state->nextThru() == nullptr + && except->to() + && except->to()->matches(gclk)) + return true; + } + } + return false; +} + +void +Genclks::srcPath(Path *clk_path, + // Return value. + PathVertex &src_path) const +{ + const Pin *src_pin = clk_path->pin(this); + 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); + srcPath(clk_edge->clock(), src_pin, clk_edge->transition(), + insert_ap, src_path); +} + +void +Genclks::srcPath(const ClockEdge *clk_edge, + const Pin *src_pin, + const PathAnalysisPt *path_ap, + // Return value. + PathVertex &src_path) const +{ + srcPath(clk_edge->clock(), src_pin, clk_edge->transition(), + path_ap, src_path); +} + +void +Genclks::srcPath(const Clock *gclk, + const Pin *src_pin, + const TransRiseFall *tr, + const PathAnalysisPt *path_ap, + // Return value. + PathVertex &src_path) const +{ + PathVertexRep *src_paths = + genclk_src_paths_.findKey(ClockPinPair(gclk, src_pin)); + if (src_paths) { + int path_index = srcPathIndex(tr, path_ap); + src_path.init(src_paths[path_index], this); + } + else + src_path.init(); +} + +Arrival +Genclks::insertionDelay(const Clock *clk, + const Pin *pin, + const TransRiseFall *tr, + const EarlyLate *early_late, + const PathAnalysisPt *path_ap) const +{ + PathVertex src_path; + PathAnalysisPt *insert_ap = path_ap->insertionAnalysisPt(early_late); + srcPath(clk, pin, tr, insert_ap, src_path); + if (clk->pllFdbk()) { + const MinMax *min_max = path_ap->pathMinMax(); + PathAnalysisPt *pll_ap = path_ap->insertionAnalysisPt(min_max->opposite()); + Arrival pll_delay = pllDelay(clk, tr, pll_ap); + if (src_path.isNull()) + return -pll_delay; + else { + PathRef prev; + src_path.prevPath(this, prev); + if (prev.isNull()) + return -pll_delay; + else + // PLL delay replaces clkin->clkout delay. + return prev.arrival(this) - pll_delay; + } + } + else if (!src_path.isNull()) + return src_path.arrival(this); + else + return 0.0; +} + +//////////////////////////////////////////////////////////////// + +void +Genclks::findPllDelays(Clock *gclk) +{ + debugPrint1(debug_, "genclk", 2, "find gen clk %s pll delay\n", + gclk->name()); + FilterPath *pll_filter = makePllFilter(gclk); + GenclkInfo *genclk_info = genclkInfo(gclk); + genclk_info->setPllFilter(pll_filter); + ClkTreeSearchPred srch_pred(this); + BfsFwdIterator pll_iter(BfsIndex::other, &srch_pred, this); + seedPllPin(gclk, pll_filter, pll_iter); + // Propagate arrivals to pll feedback pin level. + findPllArrivals(gclk, pll_iter); + sdc_->unrecordException(pll_filter); +} + +FilterPath * +Genclks::makePllFilter(const Clock *gclk) +{ + PinSet *from_pins = new PinSet; + from_pins->insert(gclk->pllOut()); + TransRiseFallBoth *rf = TransRiseFallBoth::riseFall(); + ExceptionFrom *from = sdc_->makeExceptionFrom(from_pins,nullptr,nullptr,rf); + + PinSet *to_pins = new PinSet; + to_pins->insert(gclk->pllFdbk()); + ExceptionTo *to = sdc_->makeExceptionTo(to_pins, nullptr, nullptr, rf, rf); + + return sdc_->makeFilterPath(from, nullptr, to); +} + +void +Genclks::seedPllPin(const Clock *gclk, + FilterPath *pll_filter, + BfsFwdIterator &pll_iter) +{ + Pin *pll_out_pin = gclk->pllOut(); + Vertex *vertex = graph_->pinDrvrVertex(pll_out_pin); + debugPrint1(debug_, "genclk", 2, " seed pllout pin %s\n", + network_->pathName(pll_out_pin)); + TagGroupBldr tag_bldr(true, this); + tag_bldr.init(vertex); + copyGenClkSrcPaths(vertex, &tag_bldr); + for (auto path_ap : corners_->pathAnalysisPts()) { + for (auto tr : TransRiseFall::range()) { + Tag *tag = makeTag(gclk, gclk, pll_out_pin, tr, pll_filter, path_ap); + tag_bldr.setArrival(tag, 0.0, nullptr); + } + } + search_->setVertexArrivals(vertex, &tag_bldr); + pll_iter.enqueueAdjacentVertices(vertex); +} + +class PllEvalPred : public EvalPred +{ +public: + explicit PllEvalPred(const StaState *sta); + // Override EvalPred::searchTo to allow eval at generated clk root. + virtual bool searchTo(const Vertex *to_vertex); +}; + +PllEvalPred::PllEvalPred(const StaState *sta) : + EvalPred(sta) +{ +} + +bool +PllEvalPred::searchTo(const Vertex *) +{ + return true; +} + +class PllArrivalVisitor : public ArrivalVisitor +{ +public: + PllArrivalVisitor(const StaState *sta, + BfsFwdIterator &pll_iter); + virtual void visit(Vertex *vertex); + +protected: + BfsFwdIterator &pll_iter_; +}; + +PllArrivalVisitor::PllArrivalVisitor(const StaState *sta, + BfsFwdIterator &pll_iter) : + ArrivalVisitor(sta), + pll_iter_(pll_iter) +{ +} + +void +PllArrivalVisitor::visit(Vertex *vertex) +{ + const Debug *debug = sta_->debug(); + const Network *sdc_network = sta_->network(); + Graph *graph = sta_->graph(); + Search *search = sta_->search(); + Genclks *genclks = search->genclks(); + debugPrint1(debug, "genclk", 2, "find gen clk pll arrival %s\n", + vertex->name(sdc_network)); + tag_bldr_->init(vertex); + genclks->copyGenClkSrcPaths(vertex, tag_bldr_); + has_fanin_one_ = graph->hasFaninOne(vertex); + visitFaninPaths(vertex); + pll_iter_.enqueueAdjacentVertices(vertex); + search->setVertexArrivals(vertex, tag_bldr_); +} + +void +Genclks::findPllArrivals(const Clock *gclk, + BfsFwdIterator &pll_iter) +{ + Pin *fdbk_pin = gclk->pllFdbk(); + Vertex *fdbk_vertex = graph_->pinLoadVertex(fdbk_pin); + Level fdbk_level = fdbk_vertex->level(); + PllArrivalVisitor arrival_visitor(this, pll_iter); + PllEvalPred eval_pred(this); + arrival_visitor.init(true, &eval_pred); + while (pll_iter.hasNext(fdbk_level)) { + Vertex *vertex = pll_iter.next(); + arrival_visitor.visit(vertex); + } +} + +Arrival +Genclks::pllDelay(const Clock *clk, + const TransRiseFall *tr, + const PathAnalysisPt *path_ap) const +{ + Vertex *fdbk_vertex = graph_->pinLoadVertex(clk->pllFdbk()); + GenclkInfo *genclk_info = genclkInfo(clk); + if (genclk_info) { + FilterPath *pll_filter = genclk_info->pllFilter(); + VertexPathIterator fdbk_path_iter(fdbk_vertex, tr, path_ap, this); + while (fdbk_path_iter.hasNext()) { + Path *path = fdbk_path_iter.next(); + if (matchesPllFilter(path, pll_filter)) + return path->arrival(this); + } + } + return delay_zero; +} + +bool +Genclks::matchesPllFilter(Path *path, + FilterPath *pll_filter) 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(); + ExceptionPath *except = state->exception(); + if (except == pll_filter) + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////// + +bool +ClockPinPairLess::operator()(const ClockPinPair &pair1, + const ClockPinPair &pair2) const + +{ + const Clock *clk1 = pair1.first; + const Clock *clk2 = pair2.first; + int clk_index1 = clk1->index(); + 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)); +} + +class ClockPinPairHash +{ +public: + size_t operator()(const ClockPinPair &pair) const; +}; + +size_t +ClockPinPairHash::operator()(const ClockPinPair &pair) const +{ + return hashSum(pair.first->index(), hashPtr(pair.second)); +} + +class ClockPinPairEqual +{ +public: + bool operator()(const ClockPinPair &pair1, + const ClockPinPair &pair2) const; +}; + +bool +ClockPinPairEqual::operator()(const ClockPinPair &pair1, + const ClockPinPair &pair2) const + +{ + return pair1.first == pair2.first + && pair1.second == pair2.second; +} + +} // namespace diff --git a/search/Genclks.hh b/search/Genclks.hh new file mode 100644 index 0000000..8114817 --- /dev/null +++ b/search/Genclks.hh @@ -0,0 +1,149 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_GENCLKS_H +#define STA_GENCLKS_H + +#include "Map.hh" +#include "Transition.hh" +#include "NetworkClass.hh" +#include "GraphClass.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" + +namespace sta { + +class GenclkInfo; +class BfsFwdIterator; +class BfsBkwdIterator; +class SearchPred; +class TagGroupBldr; + +typedef std::pair ClockPinPair; + +class ClockPinPairLess +{ +public: + bool operator()(const ClockPinPair &pair1, + const ClockPinPair &pair2) const; +}; + +typedef Map GenclkInfoMap; +typedef Map GenclkSrcPathMap; + +class Genclks : public StaState +{ +public: + Genclks(StaState *sta); + ~Genclks(); + 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); + // Generated clock insertion delay. + Arrival insertionDelay(const Clock *clk, + const Pin *pin, + const TransRiseFall *tr, + const EarlyLate *early_late, + const PathAnalysisPt *path_ap) const; + // Generated clock source path for a clock path root. + void srcPath(Path *clk_path, + // Return value. + PathVertex &src_path) const; + // Generated clock source path. + void srcPath(const ClockEdge *clk_edge, + const Pin *src_pin, + const PathAnalysisPt *path_ap, + // Return value. + PathVertex &src_path) const; + void srcPath(const Clock *clk, + const Pin *src_pin, + const TransRiseFall *tr, + const PathAnalysisPt *path_ap, + // Return value. + PathVertex &src_path) const; + Arrival pllDelay(const Clock *clk, + const TransRiseFall *tr, + const PathAnalysisPt *path_ap) const; + Vertex *srcPathVertex(const Pin *pin) const; + Level clkPinMaxLevel(Clock *clk) const; + void copyGenClkSrcPaths(Vertex *vertex, + TagGroupBldr *tag_bldr); + +private: + void findInsertionDelays(); + GenclkInfo *genclkInfo(const Clock *gclk) const; + void clearSrcPaths(); + void recordSrcPaths(Clock *gclk); + void findInsertionDelays(Clock *gclk); + void seedClkVertices(Clock *clk, + BfsBkwdIterator &iter, + VertexSet *fanins); + int srcPathIndex(const TransRiseFall *clk_tr, + const PathAnalysisPt *path_ap) const; + bool matchesSrcFilter(Path *path, + const Clock *gclk) const; + void findPllDelays(Clock *gclk); + FilterPath *makePllFilter(const Clock *gclk); + bool matchesPllFilter(Path *path, + FilterPath *pll_filter) const; + void seedPllPin(const Clock *gclk, + FilterPath *pll_filter, + BfsFwdIterator &pll_iter); + void findPllArrivals(const Clock *gclk, + BfsFwdIterator &pll_iter); + void seedSrcPins(Clock *gclk, + FilterPath *src_filter, + BfsFwdIterator &insert_iter); + void findSrcArrivals(Clock *gclk, + BfsFwdIterator &insert_iter, + GenclkInfo *genclk_info); + virtual FilterPath *makeSrcFilter(Clock *gclk); + void deleteGenClkInfo(); + virtual Tag *makeTag(const Clock *gclk, + const Clock *master_clk, + const Pin *master_pin, + const TransRiseFall *tr, + FilterPath *src_filter, + const PathAnalysisPt *path_ap); + void seedSrcPins(Clock *clk, + BfsBkwdIterator &iter); + void findInsertionDelay(Clock *gclk); + GenclkInfo *makeGenclkInfo(Clock *gclk); + FilterPath *srcFilter(Clock *gclk); + void findFanin(Clock *gclk, + // Return value. + VertexSet *fanins); + void findLatchFdbkEdges(const Clock *clk, + GenclkInfo *genclk_info); + void findLatchFdbkEdges(Vertex *vertex, + Level gclk_level, + SearchPred &srch_pred, + VertexSet &path_vertices, + VertexSet &visited_vertices, + EdgeSet *&fdbk_edges); + + + bool found_insertion_delays_; + GenclkSrcPathMap genclk_src_paths_; + GenclkInfoMap genclk_info_map_; +}; + +} // namespace +#endif diff --git a/search/Latches.cc b/search/Latches.cc new file mode 100644 index 0000000..1671406 --- /dev/null +++ b/search/Latches.cc @@ -0,0 +1,525 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Debug.hh" +#include "TimingRole.hh" +#include "TimingArc.hh" +#include "Liberty.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 "Search.hh" +#include "Crpr.hh" +#include "Latches.hh" + +namespace sta { + +Latches::Latches(StaState *sta) : + StaState(sta) +{ +} + +void +Latches::latchRequired(const Path *data_path, + const PathVertex *enable_path, + const PathVertex *disable_path, + MultiCyclePath *mcp, + 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 Arrival data_arrival = data_path->arrival(this); + float max_delay = 0.0; + bool ignore_clk_latency = false; + if (path_delay) { + max_delay = path_delay->delay(); + ignore_clk_latency = path_delay->ignoreClkLatency(); + } + if (ignore_clk_latency) { + required = max_delay + src_clk_latency; + borrow = 0.0; + adjusted_data_arrival = data_arrival; + time_given_to_startpoint = 0.0; + } + else if (enable_path && disable_path) { + 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); + 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); + // checkTgtClkTime + float tgt_clk_time = 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, + sdc_) + + open_crpr; + debugPrint3(debug_, "latch", 1, "latch data %s %s enable %s\n", + network_->pathName(data_path->pin(this)), + delayAsString(data_arrival, this), + delayAsString(enable_arrival, this)); + if (data_arrival <= enable_arrival) { + // Data arrives before latch opens. + required = enable_arrival; + borrow = 0.0; + adjusted_data_arrival = data_arrival; + time_given_to_startpoint = 0.0; + } + else { + // Data arrives while latch is transparent. + borrow = data_arrival - enable_arrival; + if (borrow <= max_borrow) + required = data_arrival; + else { + borrow = max_borrow; + required = enable_arrival + max_borrow; + } + time_given_to_startpoint = borrow + open_uncertainty + open_crpr; + + // 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; + } + } + else if (disable_path) { + required = max_delay + search_->clkPathArrival(disable_path) - margin; + // Borrow cannot be determined without enable path. + borrow = 0.0; + adjusted_data_arrival = data_arrival; + time_given_to_startpoint = 0.0; + } + else { + required = max_delay; + borrow = 0.0; + adjusted_data_arrival = data_arrival; + time_given_to_startpoint = 0.0; + } +} + +void +Latches::latchBorrowInfo(const Path *data_path, + const PathVertex *enable_path, + const PathVertex *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 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(); + open_uncertainty = PathEnd::checkClkUncertainty(data_clk_edge, enable_clk_edge, + enable_path, + TimingRole::latchSetup(), this); + if (ignore_clk_latency) { + open_latency = 0.0; + latency_diff = 0.0; + open_crpr = 0.0; + crpr_diff = 0.0; + } + else { + 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; + 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; + } + float borrow_limit; + 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); +} + +void +Latches::latchRequired(const Path *data_path, + const PathVertex *enable_path, + const PathVertex *disable_path, + const PathAnalysisPt *path_ap, + // Return values. + Required &required, + Arrival &borrow, + Arrival &adjusted_data_arrival, + Delay &time_given_to_startpoint) +{ + Vertex *data_vertex = data_path->vertex(this); + const TransRiseFall *data_tr = data_path->transition(this); + ArcDelay setup = latchSetupMargin(data_vertex,data_tr,disable_path,path_ap); + ExceptionPath *excpt = search_->exceptionTo(ExceptionPathType::any, + data_path, data_vertex->pin(), + data_tr, + enable_path->clkEdge(this), + path_ap->pathMinMax(), false, + false); + 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); +} + +// Find the latch enable open/close path from the close/open path. +void +Latches::latchEnableOtherPath(Path *path, + const PathAnalysisPt *tgt_clk_path_ap, + // Return value. + PathVertex &other_path) +{ + Vertex *vertex = path->vertex(this); + ClockEdge *clk_edge = path->clkEdge(this); + ClockEdge *other_clk_edge = + path->clkInfo(this)->isPulseClk() ? clk_edge:clk_edge->opposite(); + TransRiseFall *other_tr = path->transition(this)->opposite(); + VertexPathIterator path_iter(vertex, other_tr, tgt_clk_path_ap, this); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + if (path->isClock(this) + && path->clkEdge(this) == other_clk_edge) { + other_path.copy(path); + break; + } + } +} + +void +Latches::latchEnablePath(Path *q_path, + Edge *d_q_edge, + // Return value. + PathVertex &enable_path) 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 Instance *latch = network_->instance(q_path->pin(this)); + Vertex *en_vertex; + TransRiseFall *en_tr; + LatchEnableState state; + latchDtoQEnable(d_q_edge, latch, en_vertex, en_tr, state); + if (state == LatchEnableState::enabled) { + VertexPathIterator path_iter(en_vertex, en_tr, tgt_clk_path_ap, this); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + const ClockEdge *clk_edge = path->clkEdge(this); + if (path->isClock(this) + && clk_edge == en_clk_edge) { + enable_path.copy(path); + break; + } + } + } +} + +// The arrival time for a latch D->Q edge is clipped to the window of +// time when the latch is transparent using the open/close arrival +// times of the enable. The to_tag for Q is adjusted to the that of +// the enable open edge. +void +Latches::latchOutArrival(Path *data_path, + TimingArc *d_q_arc, + Edge *d_q_edge, + const PathAnalysisPt *path_ap, + // Return values. + Tag *&q_tag, + ArcDelay &arc_delay, + Arrival &q_arrival) +{ + Vertex *data_vertex = d_q_edge->from(graph_); + const Instance *inst = network_->instance(data_vertex->pin()); + Vertex *enable_vertex; + TransRiseFall *enable_tr; + LatchEnableState state; + latchDtoQEnable(d_q_edge, inst, enable_vertex, enable_tr, state); + // Latch enable may be missing if library is malformed. + switch (state) { + case LatchEnableState::closed: + // Latch is disabled by constant enable. + break; + case LatchEnableState::open: { + 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(this) + arc_delay; + q_tag = data_path->tag(this); + } + } + break; + case LatchEnableState::enabled: { + const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt(); + VertexPathIterator enable_iter(enable_vertex, enable_tr, + tgt_clk_path_ap, this); + while (enable_iter.hasNext()) { + PathVertex *enable_path = enable_iter.next(); + ClkInfo *en_clk_info = enable_path->clkInfo(this); + 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()))) { + PathVertex disable_path; + latchEnableOtherPath(enable_path, tgt_clk_path_ap, disable_path); + 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 (borrow > 0.0) { + // 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. + PathVertex *crpr_clk_path = + sdc_->crprActive() ? enable_path : nullptr; + 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); + TransRiseFall *q_tr = d_q_arc->toTrans()->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_tr, + en_clk_edge->clock(), + en_clk_edge->transition(), + MinMax::max(), false, states)) + q_tag = search_->findTag(q_tr, path_ap, q_clk_info, false, + nullptr, false, states, true); + } + return; + } + } + } + // No enable path found. + } + break; + } +} + +ExceptionPath * +Latches::exceptionTo(Path *data_path, + ClockEdge *en_clk_edge) +{ + // 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); +} + +ArcDelay +Latches::latchSetupMargin(Vertex *data_vertex, + const TransRiseFall *data_tr, + const Path *disable_path, + const PathAnalysisPt *path_ap) +{ + if (disable_path) { + Vertex *enable_vertex = disable_path->vertex(this); + const TransRiseFall *disable_tr = disable_path->transition(this); + VertexInEdgeIterator edge_iter(data_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + 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(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *check_arc = arc_iter.next(); + if (check_arc->toTrans()->asRiseFall() == data_tr + && check_arc->fromTrans()->asRiseFall() == disable_tr) + return search_->deratedDelay(from_vertex, check_arc, edge, + false, path_ap); + } + } + } + } + return 0.0; +} + +void +Latches::latchTimeGivenToStartpoint(Path *d_path, + Path *q_path, + Edge *d_q_edge, + // Return values. + Arrival &time_given, + PathVertex &enable_path) +{ + latchEnablePath(q_path, d_q_edge, enable_path); + if (!enable_path.isNull() + && enable_path.isClock(this)) { + const PathAnalysisPt *path_ap = q_path->pathAnalysisPt(this); + const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt(); + PathVertex disable_path; + latchEnableOtherPath(enable_path.path(), tgt_clk_path_ap, disable_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); + } + else { + time_given = 0.0; + enable_path.init(); + } +} + +void +Latches::latchDtoQEnable(Edge *d_q_edge, + const Instance *inst, + // Return values. + Vertex *&enable_vertex, + TransRiseFall *&enable_tr, + LatchEnableState &state) const +{ + enable_vertex = nullptr; + state = LatchEnableState::open; + LibertyCell *cell = network_->libertyCell(inst); + if (cell) { + TimingArcSet *d_q_set = d_q_edge->timingArcSet(); + LibertyPort *enable_port; + FuncExpr *enable_func; + cell->latchEnable(d_q_set, enable_port, enable_func, enable_tr); + if (enable_port) { + 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; + } + } + } + } + } +} + +LatchEnableState +Latches::latchDtoQState(Edge *edge) const +{ + const Vertex *from_vertex = edge->from(graph_); + const Pin *from_pin = from_vertex->pin(); + const Instance *inst = network_->instance(from_pin); + Vertex *enable_vertex; + TransRiseFall *enable_tr; + LatchEnableState state; + latchDtoQEnable(edge, inst, enable_vertex, enable_tr, state); + return state; +} + +// Latch D->Q arc looks combinational when the enable pin is disabled +// or constant. +bool +Latches::isLatchDtoQ(Edge *edge) const +{ + return edge->role() == TimingRole::latchDtoQ() + && latchDtoQState(edge) == LatchEnableState::enabled; +} + +} // namespace diff --git a/search/Latches.hh b/search/Latches.hh new file mode 100644 index 0000000..0d20447 --- /dev/null +++ b/search/Latches.hh @@ -0,0 +1,107 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_LATCH_H +#define STA_LATCH_H + +namespace sta { + +#include "GraphClass.hh" +#include "SearchClass.hh" + +enum class LatchEnableState { enabled, open, closed }; + +// Latches class defines latch behavior. +class Latches : public StaState +{ +public: + Latches(StaState *sta); + void latchTimeGivenToStartpoint(Path *d_path, + Path *q_path, + Edge *d_q_edge, + // Return values. + Arrival &time_given, + PathVertex &enable_path); + void latchRequired(const Path *data_path, + const PathVertex *enable_path, + const PathVertex *disable_path, + MultiCyclePath *mcp, + 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); + void latchRequired(const Path *data_path, + const PathVertex *enable_path, + const PathVertex *disable_path, + const PathAnalysisPt *path_ap, + // Return values. + Required &required, + Delay &borrow, + Arrival &adjusted_data_arrival, + Delay &time_given_to_startpoint); + void latchBorrowInfo(const Path *data_path, + const PathVertex *enable_path, + const PathVertex *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); + bool isLatchDtoQ(Edge *edge) const; + // Find the latch EN->Q edge for a D->Q edge. + void latchDtoQEnable(Edge *d_q_edge, + const Instance *inst, + // Return values. + Vertex *&enable_vertex, + TransRiseFall *&enable_tr, + LatchEnableState &state) const; + LatchEnableState latchDtoQState(Edge *d_q_edge) const; + void latchEnableOtherPath(Path *path, + const PathAnalysisPt *tgt_clk_path_ap, + // Return value. + PathVertex &other_path); + void latchEnablePath(Path *q_path, Edge *d_q_edge, + // Return value. + PathVertex &enable_path) const; + void latchOutArrival(Path *data_path, + TimingArc *d_q_arc, + Edge *d_q_edge, + const PathAnalysisPt *path_ap, + Tag *&q_tag, + ArcDelay &arc_delay, + Arrival &q_arrival); + +protected: + ArcDelay latchSetupMargin(Vertex *data_vertex, + const TransRiseFall *data_tr, + const Path *disable_path, + const PathAnalysisPt *path_ap); + ExceptionPath *exceptionTo(Path *data_path, + ClockEdge *en_clk_edge); +}; + +} // namespace +#endif diff --git a/search/Levelize.cc b/search/Levelize.cc new file mode 100644 index 0000000..e043953 --- /dev/null +++ b/search/Levelize.cc @@ -0,0 +1,493 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "Report.hh" +#include "Debug.hh" +#include "Stats.hh" +#include "TimingRole.hh" +#include "PortDirection.hh" +#include "Network.hh" +#include "Graph.hh" +#include "GraphCmp.hh" +#include "SearchPred.hh" +#include "Sdc.hh" +#include "Levelize.hh" + +namespace sta { + +Levelize::Levelize(StaState *sta) : + StaState(sta), + search_pred_(new SearchPredNonLatch2(sta)), + levelized_(false), + levels_valid_(false), + max_level_(0), + level_space_(10), + loops_(nullptr), + observer_(nullptr) +{ +} + +Levelize::~Levelize() +{ + delete search_pred_; + delete observer_; + deleteLoops(); +} + +void +Levelize::setLevelSpace(Level space) +{ + level_space_ = space; +} + +void +Levelize::setObserver(LevelizeObserver *observer) +{ + delete observer_; + observer_ = observer; +} + +void +Levelize::clear() +{ + levelized_ = false; + levels_valid_ = false; + roots_.clear(); + relevelize_from_.clear(); + clearLoopEdges(); + deleteLoops(); +} + +void +Levelize::clearLoopEdges() +{ + EdgeSet::Iterator edge_iter(disabled_loop_edges_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + edge->setIsDisabledLoop(false); + } + disabled_loop_edges_.clear(); +} + +void +Levelize::deleteLoops() +{ + if (loops_) { + loops_->deleteContents(); + delete loops_; + loops_ = nullptr; + loop_edges_.clear(); + } +} + +void +Levelize::ensureLevelized() +{ + if (!levels_valid_) { + if (levelized_) + relevelize(); + else + levelize(); + } +} + +// Depth first search. +// "Introduction to Algorithms", section 23.3 pg 478. +void +Levelize::levelize() +{ + Stats stats(debug_); + debugPrint0(debug_, "levelize", 1, "levelize\n"); + max_level_ = 0; + clearLoopEdges(); + deleteLoops(); + loops_ = new GraphLoopSeq; + findRoots(); + VertexSeq roots; + // Sort the roots so that loop breaking is stable in regressions. + // In situations where port directions are broken pins may + // be treated as bidirects, leading to a plethora of degenerate + // roots that take forever to sort. + if (roots.size() < 100) + sortRoots(roots); + levelizeFrom(roots); + ensureLatchLevels(); + // Find vertices in cycles that are were not accessible from roots. + levelizeCycles(); + levelized_ = true; + levels_valid_ = true; + stats.report("Levelize"); +} + +void +Levelize::findRoots() +{ + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + setLevel(vertex, 0); + if (isRoot(vertex)) { + debugPrint1(debug_, "levelize", 2, "root %s\n", vertex->name(sdc_network_)); + roots_.insert(vertex); + if (hasFanout(vertex, search_pred_, graph_)) + // Color roots with no fanout black so that they are + // not treated as degenerate loops by levelizeCycles(). + vertex->setColor(LevelColor::black); + } + else + vertex->setColor(LevelColor::white); + } +} + +// Root vertices have at no enabled (non-disabled) edges entering them +// and are not disabled. +bool +Levelize::isRoot(Vertex *vertex) +{ + if (search_pred_->searchTo(vertex)) { + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *from_vertex = edge->from(graph_); + if (search_pred_->searchFrom(from_vertex) + && search_pred_->searchThru(edge)) + return false; + } + // Bidirect pins are not treated as roots in this case. + return !sdc_->bidirectDrvrSlewFromLoad(vertex->pin()); + } + else + return false; +} + +void +Levelize::sortRoots(VertexSeq &roots) +{ + // roots_ is a set so insert/delete are fast for incremental changes. + // Copy the set into a vector for sorting. + VertexSet::Iterator root_iter(roots_); + while (root_iter.hasNext()) { + Vertex *root = root_iter.next(); + roots.push_back(root); + } + sort(roots, VertexNameLess(network_)); +} + +void +Levelize::levelizeFrom(VertexSeq &roots) +{ + VertexSeq::Iterator root_iter(roots); + while (root_iter.hasNext()) { + Vertex *root = root_iter.next(); + EdgeSeq path; + visit(root, 0, level_space_, path); + } +} + +void +Levelize::visit(Vertex *vertex, + Level level, + Level level_space, + EdgeSeq &path) +{ + Pin *from_pin = vertex->pin(); + debugPrint2(debug_, "levelize", 3, "level %d %s\n", + level, vertex->name(sdc_network_)); + vertex->setColor(LevelColor::gray); + setLevel(vertex, level); + max_level_ = max(level, max_level_); + level += level_space; + + 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)) { + LevelColor to_color = to_vertex->color(); + if (to_color == LevelColor::gray) + // Back edges form feedback loops. + recordLoop(edge, path); + else if (to_color == LevelColor::white + || to_vertex->level() < level) { + path.push_back(edge); + visit(to_vertex, level, level_space, path); + path.pop_back(); + } + } + 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. + if (sdc_->bidirectDrvrSlewFromLoad(from_pin) + && !vertex->isBidirectDriver()) { + Vertex *to_vertex = graph_->pinDrvrVertex(from_pin); + if (search_pred_->searchTo(to_vertex) + && (to_vertex->color() == LevelColor::white + || to_vertex->level() < level)) + visit(to_vertex, level, level_space, path); + } + } + vertex->setColor(LevelColor::black); +} + +void +Levelize::recordLoop(Edge *edge, + EdgeSeq &path) +{ + debugPrint3(debug_, "levelize", 2, "Loop edge %s -> %s (%s)\n", + edge->from(graph_)->name(sdc_network_), + edge->to(graph_)->name(sdc_network_), + edge->role()->asString()); + // Do not record loops if they have been invalidated. + if (loops_) { + EdgeSeq *loop_edges = loopEdges(path, edge); + GraphLoop *loop = new GraphLoop(loop_edges); + loops_->push_back(loop); + if (sdc_->dynamicLoopBreaking()) + sdc_->makeLoopExceptions(loop); + } + // Record disabled loop edges so they can be cleared without + // traversing the entire graph to find them. + disabled_loop_edges_.insert(edge); + edge->setIsDisabledLoop(true); +} + +EdgeSeq * +Levelize::loopEdges(EdgeSeq &path, + Edge *closing_edge) +{ + debugPrint0(debug_, "loop", 2, "Loop\n"); + 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(); + Pin *from_pin = edge->from(graph_)->pin(); + if (from_pin == loop_pin) + copy = true; + if (copy) { + debugPrint2(debug_, "loop", 2, " %s -> %s\n", + edge->from(graph_)->name(sdc_network_), + edge->to(graph_)->name(sdc_network_)); + loop_edges->push_back(edge); + loop_edges_.insert(edge); + } + } + debugPrint2(debug_, "loop", 2, " %s -> %s\n", + closing_edge->from(graph_)->name(sdc_network_), + closing_edge->to(graph_)->name(sdc_network_)); + loop_edges->push_back(closing_edge); + loop_edges_.insert(closing_edge); + return loop_edges; +} + +// Make sure latch D input level is not the same as the Q level. +// This is because the Q arrival depends on the D arrival and +// to find them in parallel they have to be scheduled separately +// to avoid a race condition. +void +Levelize::ensureLatchLevels() +{ + EdgeSet::Iterator latch_edge_iter(latch_d_to_q_edges_); + while (latch_edge_iter.hasNext()) { + Edge *edge = latch_edge_iter.next(); + Vertex *from = edge->from(graph_); + Vertex *to = edge->to(graph_); + if (from->level() == to->level()) + setLevel(from, from->level() + level_space_); + } + latch_d_to_q_edges_.clear(); +} + +void +Levelize::levelizeCycles() +{ + // Find vertices that were not discovered by searching from all + // graph roots. + VertexSeq uncolored; + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + if (vertex->color() == LevelColor::white + && search_pred_->searchFrom(vertex)) + uncolored.push_back(vertex); + } + + // Sort cycle vertices so results are stable. + sort(uncolored, VertexNameLess(network_)); + + VertexSeq::Iterator uncolored_iter(uncolored); + while (uncolored_iter.hasNext()) { + Vertex *vertex = uncolored_iter.next(); + // Only search from and assign root status to vertices that + // previous searches did not visit. Otherwise "everybody is a + // root". + if (vertex->color() == LevelColor::white) { + EdgeSeq path; + roots_.insert(vertex); + visit(vertex, 0, level_space_, path); + } + } +} + +void +Levelize::invalid() +{ + debugPrint0(debug_, "levelize", 1, "levels invalid\n"); + clear(); +} + +void +Levelize::invalidFrom(Vertex *vertex) +{ + debugPrint1(debug_, "levelize", 1, "level invalid from %s\n", + vertex->name(sdc_network_)); + 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) +{ + roots_.erase(vertex); + relevelize_from_.erase(vertex); +} + +void +Levelize::relevelizeFrom(Vertex *vertex) +{ + debugPrint1(debug_, "levelize", 1, "invalid relevelize from %s\n", + vertex->name(sdc_network_)); + relevelize_from_.insert(vertex); + levels_valid_ = false; +} + +void +Levelize::deleteEdgeBefore(Edge *edge) +{ + if (loop_edges_.hasKey(edge)) { + // Relevelize if a loop edge is removed. Incremental levelization + // fails because the DFS path will be missing. + invalid(); + // Prevent refererence to deleted edge by clearLoopEdges(). + disabled_loop_edges_.erase(edge); + } +} + +// Incremental relevelization. +// Note that if vertices or edges are removed from the graph the +// downstream levels will NOT be reduced to the "correct" level (the +// search will immediately terminate without visiting downstream +// vertices because the new level is less than the existing level). +// This is acceptable because the BFS search that depends on the +// levels only requires that a vertex level be greater than that of +// its predecessors. +void +Levelize::relevelize() +{ + VertexSet::Iterator vertex_iter(relevelize_from_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + debugPrint1(debug_, "levelize", 1, "relevelize from %s\n", + vertex->name(sdc_network_)); + if (search_pred_->searchFrom(vertex)) { + if (isRoot(vertex)) { + setLevel(vertex, 0); + roots_.insert(vertex); + } + EdgeSeq path; + visit(vertex, vertex->level(), 1, path); + } + } + ensureLatchLevels(); + levels_valid_ = true; + relevelize_from_.clear(); +} + +bool +Levelize::isDisabledLoop(Edge *edge) const +{ + return disabled_loop_edges_.hasKey(edge); +} + +void +Levelize::setLevel(Vertex *vertex, + Level level) +{ + if (vertex->level() != level) { + if (observer_) + observer_->levelChangedBefore(vertex); + vertex->setLevel(level); + } +} + +//////////////////////////////////////////////////////////////// + +GraphLoop::GraphLoop(EdgeSeq *edges) : + edges_(edges) +{ +} + +GraphLoop::~GraphLoop() +{ + delete edges_; +} + +bool +GraphLoop::isCombinational() const +{ + EdgeSeq::Iterator edge_iter(edges_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + TimingRole *role = edge->role(); + if (!(role == TimingRole::wire() + || role == TimingRole::combinational() + || role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())) + return false; + } + return true; +} + +void +GraphLoop::report(Report *report, + Network *network, + Graph *graph) const +{ + bool first_edge = true; + EdgeSeq::Iterator loop_edge_iter(edges_); + while (loop_edge_iter.hasNext()) { + Edge *edge = loop_edge_iter.next(); + if (first_edge) + report->print(" %s\n", edge->from(graph)->name(network)); + report->print(" %s\n", edge->to(graph)->name(network)); + first_edge = false; + } +} + +} // namespace diff --git a/search/Levelize.hh b/search/Levelize.hh new file mode 100644 index 0000000..f8fe1fe --- /dev/null +++ b/search/Levelize.hh @@ -0,0 +1,125 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_LEVELIZE_H +#define STA_LEVELIZE_H + +#include "DisallowCopyAssign.hh" +#include "StaState.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" +#include "GraphClass.hh" + +namespace sta { + +class SearchPred; +class LevelizeObserver; + +class Levelize : public StaState +{ +public: + explicit Levelize(StaState *sta); + virtual ~Levelize(); + // Space between initially assigned levels that is filled in by + // incremental levelization. Set level space before levelization. + void setLevelSpace(Level space); + bool levelized() { return levels_valid_; } + 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); + int maxLevel() const { return max_level_; } + // Vertices with no fanin edges. + VertexSet &roots() { return roots_; } + bool isRoot(Vertex *vertex); + // Reset to virgin state. + void clear(); + // Edge is disabled to break combinational loops. + bool isDisabledLoop(Edge *edge) const; + // Only valid when levels are valid. + GraphLoopSeq *loops() { return loops_; } + // Set the observer for level changes. + void setObserver(LevelizeObserver *observer); + +protected: + void levelize(); + void findRoots(); + void sortRoots(VertexSeq &roots); + void levelizeFrom(VertexSeq &roots); + void visit(Vertex *vertex, Level level, Level level_space, EdgeSeq &path); + void levelizeCycles(); + void relevelize(); + void clearLoopEdges(); + void deleteLoops(); + void recordLoop(Edge *edge, EdgeSeq &path); + EdgeSeq *loopEdges(EdgeSeq &path, Edge *closing_edge); + void ensureLatchLevels(); + void setLevel(Vertex *vertex, + Level level); + + SearchPred *search_pred_; + bool levelized_; + bool levels_valid_; + Level max_level_; + Level level_space_; + VertexSet roots_; + VertexSet relevelize_from_; + GraphLoopSeq *loops_; + EdgeSet loop_edges_; + EdgeSet disabled_loop_edges_; + EdgeSet latch_d_to_q_edges_; + LevelizeObserver *observer_; + +private: + DISALLOW_COPY_AND_ASSIGN(Levelize); +}; + +// Loops broken by levelization may not necessarily be combinational. +// For example, a register/latch output can feed back to a gated clock +// enable on the register/latch clock. +class GraphLoop +{ +public: + explicit GraphLoop(EdgeSeq *edges); + ~GraphLoop(); + EdgeSeq *edges() { return edges_; } + bool isCombinational() const; + void report(Report *report, + Network *network, + Graph *graph) const; + +private: + DISALLOW_COPY_AND_ASSIGN(GraphLoop); + + EdgeSeq *edges_; +}; + +class LevelizeObserver +{ +public: + LevelizeObserver() {} + virtual ~LevelizeObserver() {} + virtual void levelChangedBefore(Vertex *vertex) = 0; + +private: + DISALLOW_COPY_AND_ASSIGN(LevelizeObserver); +}; + +} // namespace +#endif diff --git a/search/Makefile.am b/search/Makefile.am new file mode 100644 index 0000000..8dc3322 --- /dev/null +++ b/search/Makefile.am @@ -0,0 +1,107 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +lib_LTLIBRARIES = libsearch.la + +include_HEADERS = \ + Bfs.hh \ + CheckMaxSkews.hh \ + CheckMinPeriods.hh \ + CheckMinPulseWidths.hh \ + CheckSlewLimits.hh \ + CheckTiming.hh \ + ClkInfo.hh \ + ClkSkew.hh \ + Corner.hh \ + Crpr.hh \ + FindRegister.hh \ + GatedClk.hh \ + Genclks.hh \ + Latches.hh \ + Levelize.hh \ + Path.hh \ + PathAnalysisPt.hh \ + PathEnd.hh \ + PathEnum.hh \ + PathEnumed.hh \ + PathExpanded.hh \ + PathRef.hh \ + PathGroup.hh \ + PathVertex.hh \ + PathVertexRep.hh \ + Power.hh \ + Property.hh \ + ReportPath.hh \ + Search.hh \ + SearchClass.hh \ + SearchPred.hh \ + Sim.hh \ + Sta.hh \ + StaState.hh \ + Tag.hh \ + TagGroup.hh \ + VertexVisitor.hh \ + VisitPathEnds.hh \ + VisitPathGroupVertices.hh \ + WorstSlack.hh \ + WritePathSpice.hh + +libsearch_la_SOURCES = \ + Bfs.cc \ + CheckMaxSkews.cc \ + CheckMinPeriods.cc \ + CheckMinPulseWidths.cc \ + CheckSlewLimits.cc \ + CheckTiming.cc \ + ClkInfo.cc \ + ClkSkew.cc \ + Corner.cc \ + Crpr.cc \ + FindRegister.cc \ + GatedClk.cc \ + Genclks.cc \ + Latches.cc \ + Levelize.cc \ + Path.cc \ + PathAnalysisPt.cc \ + PathEnd.cc \ + PathEnum.cc \ + PathEnumed.cc \ + PathExpanded.cc \ + PathGroup.cc \ + PathRef.cc \ + PathVertex.cc \ + PathVertexRep.cc \ + Power.cc \ + Property.cc \ + ReportPath.cc \ + Search.cc \ + SearchPred.cc \ + Sim.cc \ + Sta.cc \ + StaState.cc \ + Tag.cc \ + TagGroup.cc \ + VertexVisitor.cc \ + VisitPathEnds.cc \ + VisitPathGroupVertices.cc \ + WorstSlack.cc \ + WritePathSpice.cc + +libs: $(lib_LTLIBRARIES) + +xtags: $(SOURCES) $(HEADERS) + etags -a -o ../TAGS $(SOURCES) $(HEADERS) diff --git a/search/Path.cc b/search/Path.cc new file mode 100644 index 0000000..b2508f7 --- /dev/null +++ b/search/Path.cc @@ -0,0 +1,367 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "TimingArc.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Clock.hh" +#include "Tag.hh" +#include "Corner.hh" +#include "DcalcAnalysisPt.hh" +#include "PathAnalysisPt.hh" +#include "PathRef.hh" +#include "Path.hh" + +namespace sta { + +const char * +Path::name(const StaState *sta) const +{ + const Network *network = sta->network(); + Vertex *vertex1 = vertex(sta); + if (vertex1) { + const char *vertex_name = vertex1->name(network); + const char *tr_str = transition(sta)->asString(); + const PathAnalysisPt *path_ap = pathAnalysisPt(sta); + int ap_index = path_ap->index(); + const char *min_max = path_ap->pathMinMax()->asString(); + TagIndex tag_index = tagIndex(sta); + return stringPrintTmp("%s %s %s/%d %d", + vertex_name, tr_str, min_max, + ap_index, tag_index); + } + else + return "NULL"; +} + +Pin * +Path::pin(const StaState *sta) const +{ + return vertex(sta)->pin(); +} + +TagIndex +Path::tagIndex(const StaState *sta) const +{ + return tag(sta)->index(); +} + +ClkInfo * +Path::clkInfo(const StaState *sta) const +{ + return tag(sta)->clkInfo(); +} + +ClockEdge * +Path::clkEdge(const StaState *sta) const +{ + return tag(sta)->clkEdge(); +} + +Clock * +Path::clock(const StaState *sta) const +{ + return tag(sta)->clock(); +} + +bool +Path::isClock(const StaState *sta) const +{ + return tag(sta)->isClock(); +} + +const MinMax * +Path::minMax(const StaState *sta) const +{ + return pathAnalysisPt(sta)->pathMinMax(); +} + +PathAPIndex +Path::pathAnalysisPtIndex(const StaState *sta) const +{ + return pathAnalysisPt(sta)->index(); +} + +DcalcAnalysisPt * +Path::dcalcAnalysisPt(const StaState *sta) const +{ + return pathAnalysisPt(sta)->dcalcAnalysisPt(); +} + +Slew +Path::slew(const StaState *sta) const +{ + return sta->graph()->slew(vertex(sta), transition(sta), + dcalcAnalysisPt(sta)->index()); +} + +int +Path::trIndex(const StaState *sta) const +{ + return transition(sta)->index(); +} + +void +Path::initArrival(const StaState *sta) +{ + setArrival(delayInitValue(minMax(sta)), sta); +} + +bool +Path::arrivalIsInitValue(const StaState *sta) const +{ + return delayIsInitValue(arrival(sta), minMax(sta)); +} + +void +Path::initRequired(const StaState *sta) +{ + setRequired(delayInitValue(minMax(sta)->opposite()), sta); +} + +bool +Path::requiredIsInitValue(const StaState *sta) const +{ + return delayIsInitValue(required(sta), minMax(sta)->opposite()); +} + +Slack +Path::slack(const StaState *sta) const +{ + if (minMax(sta) == MinMax::max()) + return required(sta) - arrival(sta); + else + return arrival(sta) - required(sta); +} + +void +Path::prevPath(const StaState *sta, + // Return values. + PathRef &prev_path) const +{ + TimingArc *prev_arc; + prevPath(sta, prev_path, prev_arc); +} + +TimingArc * +Path::prevArc(const StaState *sta) const +{ + PathRef prev_path; + TimingArc *prev_arc; + prevPath(sta, prev_path, prev_arc); + return prev_arc; +} + +Edge * +Path::prevEdge(const TimingArc *prev_arc, + const StaState *sta) const +{ + if (prev_arc) { + TimingArcSet *arc_set = prev_arc->set(); + VertexInEdgeIterator edge_iter(vertex(sta), sta->graph()); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->timingArcSet() == arc_set) + return edge; + } + } + return nullptr; +} + +//////////////////////////////////////////////////////////////// + +int +Path::cmpPinTrClk(const Path *path1, + const Path *path2, + const StaState *sta) +{ + if (path1 && path2) { + const Pin *pin1 = path1->pin(sta); + const Pin *pin2 = path2->pin(sta); + const Network *network = sta->network(); + if (pin1 == pin2) { + int tr_index1 = path1->trIndex(sta); + int tr_index2 = path2->trIndex(sta); + if (tr_index1 == tr_index2) + return cmpClk(path1, path2, sta); + else if (tr_index1 < tr_index2) + return -1; + else + return 1; + } + else if (network->pathNameLess(pin1, pin2)) + return -1; + else + return 1; + } + else if (path1 == nullptr && path2 == nullptr) + return 0; + else if (path1 == nullptr) + return -1; + else + return 1; +} + +int +Path::cmpClk(const Path *path1, + const Path *path2, + const StaState *sta) +{ + ClockEdge *clk_edge1 = path1->clkEdge(sta); + ClockEdge *clk_edge2 = path2->clkEdge(sta); + if (clk_edge1 && clk_edge2) { + int index1 = clk_edge1->index(); + int index2 = clk_edge2->index(); + if (index1 == index2) + return 0; + else if (index1 < index2) + return -1; + else + return 1; + } + else if (clk_edge1 == nullptr + && clk_edge2 == nullptr) + return 0; + else if (clk_edge2) + return -1; + else + return 1; +} + +bool +Path::equal(const Path *path1, + const Path *path2, + const StaState *sta) +{ + return (path1 + && path2 + && path1->vertexIndex(sta) == path2->vertexIndex(sta) + // Tag equal implies transition and path ap equal. + && path1->tagIndex(sta) == path2->tagIndex(sta)) + || ((path1 == nullptr || path1->isNull()) + && (path2 == nullptr || path2->isNull())); +} + +//////////////////////////////////////////////////////////////// + +PathLess::PathLess(const StaState *sta) : + sta_(sta) +{ +} + +bool +PathLess::operator()(const Path *path1, + const Path *path2) const +{ + return Path::less(path1, path2, sta_); +} + +bool +Path::less(const Path *path1, + const Path *path2, + const StaState *sta) +{ + return cmp(path1, path2, sta) < 0; +} + +int +Path::cmp(const Path *path1, + const Path *path2, + const StaState *sta) +{ + if (path1 && path2) { + VertexIndex vertex_index1 = path1->vertexIndex(sta); + VertexIndex vertex_index2 = path2->vertexIndex(sta); + if (vertex_index1 == vertex_index2) { + TagIndex tag_index1 = path1->tagIndex(sta); + TagIndex tag_index2 = path2->tagIndex(sta); + if (tag_index1 == tag_index2) + return 0; + else if (tag_index1 < tag_index2) + return -1; + else + return 1; + } + else if (vertex_index1 < vertex_index2) + return -1; + else + return 1; + } + else if (path1 == nullptr + && path2 == nullptr) + return 0; + else if (path1 == nullptr) + return -1; + else + return 1; +} + +int +Path::cmpNoCrpr(const Path *path1, + const Path *path2, + const StaState *sta) +{ + VertexIndex vertex_index1 = path1->vertexIndex(sta); + VertexIndex vertex_index2 = path2->vertexIndex(sta); + if (vertex_index1 == vertex_index2) + return tagMatchCmp(path1->tag(sta), path2->tag(sta), false, sta); + else if (vertex_index1 < vertex_index2) + return -1; + else + return 1; +} + +int +Path::cmpAll(const Path *path1, + const Path *path2, + const StaState *sta) +{ + PathRef p1(path1); + PathRef p2(path2); + bool first = true; + while (!p1.isNull() + && !p2.isNull()) { + int cmp = Path::cmp(&p1, &p2, sta); + if (cmp != 0) + return cmp; + + // Pin and transitions are the same. Keep on look'n. + p1.prevPath(sta, p1); + p2.prevPath(sta, p2); + first = false; + if (!first + && equal(&p1, path1, sta)) + // Equivalent latch loops. + return 0; + } + if (p1.isNull() && p2.isNull()) + return 0; + else if (p1.isNull() && !p2.isNull()) + return -1; + else + return 1; +} + +bool +Path::lessAll(const Path *path1, + const Path *path2, + const StaState *sta) +{ + return cmpAll(path1, path2, sta) < 0; +} + +} // namespace diff --git a/search/Path.hh b/search/Path.hh new file mode 100644 index 0000000..5a4d7dc --- /dev/null +++ b/search/Path.hh @@ -0,0 +1,130 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PATH_H +#define STA_PATH_H + +#include "DisallowCopyAssign.hh" +#include "NetworkClass.hh" +#include "GraphClass.hh" +#include "SdcClass.hh" +#include "MinMax.hh" +#include "Transition.hh" +#include "Delay.hh" +#include "StaState.hh" +#include "SearchClass.hh" + +namespace sta { + +class DcalcAnalysisPt; + +// Abstract base class for Path API. +class Path +{ +public: + Path() {} + virtual ~Path() {} + virtual const char *name(const StaState *sta) const; + virtual bool isNull() const = 0; + virtual Path *path() { return isNull() ? nullptr : this; } + virtual const Path *path() const { return isNull() ? nullptr : this; } + virtual void setRef(PathRef *ref) const = 0; + virtual void setRef(PathRef &ref) const { setRef(&ref); } + virtual Vertex *vertex(const StaState *sta) const = 0; + virtual VertexIndex vertexIndex(const StaState *sta) const = 0; + virtual Pin *pin(const StaState *sta) const; + virtual Tag *tag(const StaState *sta) const = 0; + virtual TagIndex tagIndex(const StaState *sta) const; + virtual ClkInfo *clkInfo(const StaState *sta) const; + virtual ClockEdge *clkEdge(const StaState *sta) const; + virtual Clock *clock(const StaState *sta) const; + virtual bool isClock(const StaState *sta) const; + virtual const TransRiseFall *transition(const StaState *sta) const = 0; + virtual int trIndex(const StaState *sta) const; + virtual const MinMax *minMax(const StaState *sta) const; + virtual PathAnalysisPt *pathAnalysisPt(const StaState *sta) const = 0; + virtual PathAPIndex pathAnalysisPtIndex(const StaState *sta) const; + virtual DcalcAnalysisPt *dcalcAnalysisPt(const StaState *sta) const; + virtual Arrival arrival(const StaState *sta) const = 0; + virtual void setArrival(Arrival arrival, + const StaState *sta) = 0; + virtual void initArrival(const StaState *sta); + virtual bool arrivalIsInitValue(const StaState *sta) const; + virtual const Required &required(const StaState *sta) const = 0; + virtual void setRequired(const Required &required, + const StaState *sta) = 0; + virtual void initRequired(const StaState *sta); + virtual bool requiredIsInitValue(const StaState *sta) const; + virtual Slack slack(const StaState *sta) const; + virtual Slew slew(const StaState *sta) const; + // This takes the same time as prevPath and prevArc combined. + virtual void prevPath(const StaState *sta, + // Return values. + PathRef &prev_path, + TimingArc *&prev_arc) const = 0; + virtual void prevPath(const StaState *sta, + // Return values. + PathRef &prev_path) const; + virtual TimingArc *prevArc(const StaState *sta) const; + // Find the previous edge given the previous arc found above. + Edge *prevEdge(const TimingArc *prev_arc, + const StaState *sta) const; + + static bool less(const Path *path1, + const Path *path2, + const StaState *sta); + static int cmp(const Path *path1, + 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); + // Compare pin name and transition and source clock edge. + static int cmpPinTrClk(const Path *path1, + const Path *path2, + const StaState *sta); + // Compare source clock edge. + static int cmpClk(const Path *path1, + 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); + // Search back on each path until finding a difference. + static int cmpAll(const Path *path1, + const Path *path2, + const StaState *sta); + static bool lessAll(const Path *path1, + const Path *path2, + const StaState *sta); +}; + +// Compare all path attributes (vertex, transition, tag, analysis point). +class PathLess +{ +public: + explicit PathLess(const StaState *sta); + bool operator()(const Path *path1, + const Path *path2) const; + +protected: + const StaState *sta_; +}; + +} // namespace +#endif diff --git a/search/PathAnalysisPt.cc b/search/PathAnalysisPt.cc new file mode 100644 index 0000000..45194a8 --- /dev/null +++ b/search/PathAnalysisPt.cc @@ -0,0 +1,56 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "StringUtil.hh" +#include "Corner.hh" +#include "Search.hh" +#include "PathAnalysisPt.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) +{ +} + +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/PathAnalysisPt.hh b/search/PathAnalysisPt.hh new file mode 100644 index 0000000..7e4cce1 --- /dev/null +++ b/search/PathAnalysisPt.hh @@ -0,0 +1,63 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PATH_ANALYSIS_PT_H +#define STA_PATH_ANALYSIS_PT_H + +#include "DisallowCopyAssign.hh" +#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); + 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: + DISALLOW_COPY_AND_ASSIGN(PathAnalysisPt); + + Corner *corner_; + PathAPIndex index_; + const MinMax *path_min_max_; + PathAnalysisPt *tgt_clk_ap_; + PathAnalysisPt *insertion_aps_[EarlyLate::index_count]; + DcalcAnalysisPt *dcalc_ap_; +}; + +} // namespace +#endif diff --git a/search/PathEnd.cc b/search/PathEnd.cc new file mode 100644 index 0000000..d4a1ce2 --- /dev/null +++ b/search/PathEnd.cc @@ -0,0 +1,2071 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Debug.hh" +#include "TimingRole.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Clock.hh" +#include "PortDelay.hh" +#include "DataCheck.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 "PathEnd.hh" + +namespace sta { + +PathEnd::PathEnd(Path *path) : + path_(path) +{ +} + +PathEnd::~PathEnd() +{ + path_.deleteRep(); +} + +void +PathEnd::setPath(PathEnumed *path, + const StaState *) +{ + path_.init(path); +} + +Vertex * +PathEnd::vertex(const StaState *sta) const +{ + return path_.vertex(sta); +} + +const MinMax * +PathEnd::minMax(const StaState *sta) const +{ + return path_.pathAnalysisPt(sta)->pathMinMax(); +} + +const EarlyLate * +PathEnd::pathEarlyLate(const StaState *sta) const +{ + return path_.pathAnalysisPt(sta)->pathMinMax(); +} + +const EarlyLate * +PathEnd::clkEarlyLate(const StaState *sta) const +{ + return checkRole(sta)->tgtClkEarlyLate(); +} + +const TransRiseFall * +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); +} + +ClockEdge * +PathEnd::sourceClkEdge(const StaState *sta) const +{ + return path_.clkEdge(sta); +} + +Arrival +PathEnd::dataArrivalTime(const StaState *sta) const +{ + return path_.arrival(sta); +} + +Arrival +PathEnd::dataArrivalTimeOffset(const StaState *sta) const +{ + return dataArrivalTime(sta) + sourceClkOffset(sta); +} + +Required +PathEnd::requiredTimeOffset(const StaState *sta) const +{ + return requiredTime(sta) + sourceClkOffset(sta); +} + +const TransRiseFall * +PathEnd::targetClkEndTrans(const StaState *sta) const +{ + const PathVertex *clk_path = targetClkPath(); + if (clk_path) + return clk_path->transition(sta); + else { + ClockEdge *clk_edge = targetClkEdge(sta); + if (clk_edge) + return clk_edge->transition(); + else + return nullptr; + } +} + +const TimingRole * +PathEnd::checkGenericRole(const StaState *sta) const +{ + return checkRole(sta)->genericRole(); +} + +Delay +PathEnd::sourceClkLatency(const StaState *) const +{ + return delay_zero; +} + +Delay +PathEnd::sourceClkInsertionDelay(const StaState *) const +{ + return delay_zero; +} + +Clock * +PathEnd::targetClk(const StaState *) const +{ + return nullptr; +} + +ClockEdge * +PathEnd::targetClkEdge(const StaState *) const +{ + return nullptr; +} + +float +PathEnd::targetClkTime(const StaState *) const +{ + return 0.0; +} + +float +PathEnd::targetClkOffset(const StaState *) const +{ + return 0.0; +} + +Arrival +PathEnd::targetClkArrival(const StaState *) const +{ + return delay_zero; +} + +Delay +PathEnd::targetClkDelay(const StaState *) const +{ + return delay_zero; +} + +Delay +PathEnd::targetClkInsertionDelay(const StaState *) const +{ + return delay_zero; +} + +float +PathEnd::targetNonInterClkUncertainty(const StaState *) const +{ + return 0.0; +} + +float +PathEnd::interClkUncertainty(const StaState *) const +{ + return 0.0; +} + +float +PathEnd::targetClkUncertainty(const StaState *) const +{ + return 0.0; +} + +float +PathEnd::targetClkMcpAdjustment(const StaState *) const +{ + return 0.0; +} + +TimingRole * +PathEnd::checkRole(const StaState *) const +{ + return nullptr; +} + +PathVertex * +PathEnd::targetClkPath() +{ + return nullptr; +} + +const PathVertex * +PathEnd::targetClkPath() const +{ + return nullptr; +} + +bool +PathEnd::pathDelayMarginIsExternal() const +{ + return false; +} + +PathDelay * +PathEnd::pathDelay() const +{ + return nullptr; +} + +Arrival +PathEnd::borrow(const StaState *) const +{ + return 0.0; +} + +Crpr +PathEnd::commonClkPessimism(const StaState *) const +{ + return 0.0; +} + +MultiCyclePath * +PathEnd::multiCyclePath() const +{ + return nullptr; +} + +int +PathEnd::exceptPathCmp(const PathEnd *path_end, + const StaState *) const +{ + Type type1 = type(); + Type type2 = path_end->type(); + if (type1 == type2) + return 0; + else if (type1 < type2) + return -1; + else + return 1; +} + +// PathExpanded::expand() and PathExpanded::clkPath(). +// Similar to srcClkPath but for PathVertex's. +void +PathEnd::clkPath(PathVertex *path, + const StaState *sta, + // Return value. + PathVertex &clk_path) +{ + PathVertex p(path); + while (!p.isNull()) { + PathVertex prev_path; + TimingArc *prev_arc; + p.prevPath(sta, prev_path, prev_arc); + + if (p.isClock(sta)) { + clk_path.copy(p); + return; + } + if (prev_arc) { + TimingRole *prev_role = prev_arc->role(); + if (prev_role == TimingRole::regClkToQ() + || prev_role == TimingRole::latchEnToQ()) { + p.prevPath(sta, prev_path, prev_arc); + clk_path.copy(prev_path); + return; + } + else if (prev_role == TimingRole::latchDtoQ()) { + const Latches *latches = sta->latches(); + Edge *prev_edge = p.prevEdge(prev_arc, sta); + PathVertex enable_path; + latches->latchEnablePath(&p, prev_edge, enable_path); + clk_path.copy(enable_path); + return; + } + } + p.copy(prev_path); + } +} + +//////////////////////////////////////////////////////////////// + +Arrival +PathEnd::checkTgtClkDelay(const PathVertex *tgt_clk_path, + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta) +{ + Arrival insertion, latency; + checkTgtClkDelay(tgt_clk_path, tgt_clk_edge, check_role, sta, + insertion, latency); + return Arrival(insertion + latency); +} + +void +PathEnd::checkTgtClkDelay(const PathVertex *tgt_clk_path, + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta, + // Return values. + Arrival &insertion, + Arrival &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); + 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 TransRiseFall *tgt_clk_tr = tgt_clk_edge->transition(); + insertion = search->clockInsertion(tgt_clk, tgt_src_pin, tgt_clk_tr, + min_max, early_late, tgt_path_ap); + if (clk_info->isPropagated()) { + // Propagated clock. Propagated arrival is seeded with + // early_late==path_min_max insertion delay. + Arrival path_insertion = search->clockInsertion(tgt_clk, tgt_src_pin, + tgt_clk_tr, min_max, + min_max, tgt_path_ap); + latency=tgt_clk_path->arrival(sta)-tgt_clk_edge->time()-path_insertion; + } + else + // Ideal clock. + latency = clk_info->latency(); + } + else { + insertion = 0.0F; + latency = 0.0F; + } +} + +float +PathEnd::checkClkUncertainty(const ClockEdge *src_clk_edge, + const ClockEdge *tgt_clk_edge, + const PathVertex *tgt_clk_path, + const TimingRole *check_role, + const StaState *sta) +{ + + float inter_clk; + bool inter_exists; + checkInterClkUncertainty(src_clk_edge, tgt_clk_edge, check_role, sta, + inter_clk, inter_exists); + if (inter_exists) + return inter_clk; + else + return checkNonInterClkUncertainty(tgt_clk_path, tgt_clk_edge, + check_role, sta); +} + +float +PathEnd::checkNonInterClkUncertainty(const PathVertex *tgt_clk_path, + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta) +{ + MinMax *min_max = check_role->pathMinMax(); + 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(); + float uncertainty = 0.0; + if (uncertainties) { + bool exists; + float uncertainty1; + uncertainties->value(min_max, uncertainty1, exists); + if (exists) + uncertainty = uncertainty1; + } + if (check_role->genericRole() == TimingRole::setup()) + uncertainty = -uncertainty; + return uncertainty; +} + +void +PathEnd::checkInterClkUncertainty(const ClockEdge *src_clk_edge, + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta, + float &uncertainty, + bool &exists) +{ + Sdc *sdc = sta->sdc(); + if (src_clk_edge + && src_clk_edge != sdc->defaultArrivalClockEdge()) { + sdc->clockUncertainty(src_clk_edge->clock(), + src_clk_edge->transition(), + tgt_clk_edge->clock(), + tgt_clk_edge->transition(), + check_role->pathMinMax(), + uncertainty, exists); + if (exists + && check_role->genericRole() == TimingRole::setup()) + uncertainty = -uncertainty; + } + else + exists = false; +} + +//////////////////////////////////////////////////////////////// + +void +PathEndUnconstrained::reportFull(ReportPath *report, + string &result) const +{ + report->reportFull(this, result); +} + +Slack +PathEndUnconstrained::slackNoCrpr(const StaState *) const +{ + return INF; +} + +void +PathEndUnconstrained::reportShort(ReportPath *report, + string &result) const +{ + report->reportShort(this, result); +} + +//////////////////////////////////////////////////////////////// + +PathEndUnconstrained::PathEndUnconstrained(Path *path) : + PathEnd(path) +{ +} + +PathEnd * +PathEndUnconstrained::copy() +{ + return new PathEndUnconstrained(path_.path()); +} + +bool +PathEndUnconstrained::isUnconstrained() const +{ + return true; +} + +Required +PathEndUnconstrained::requiredTime(const StaState *sta) const +{ + return delayInitValue(minMax(sta)->opposite()); +} + +Required +PathEndUnconstrained::requiredTimeOffset(const StaState *sta) const +{ + return delayInitValue(minMax(sta)->opposite()); +} + +ArcDelay +PathEndUnconstrained::margin(const StaState *) const +{ + return delay_zero; +} + +Slack +PathEndUnconstrained::slack(const StaState *) const +{ + return INF; +} + + +float +PathEndUnconstrained::sourceClkOffset(const StaState *) const +{ + return 0.0; +} + +PathEnd::Type +PathEndUnconstrained::type() const +{ + return Type::unconstrained; +} + +const char * +PathEndUnconstrained::typeName() const +{ + return "unconstrained"; +} + +//////////////////////////////////////////////////////////////// + +PathEndClkConstrained::PathEndClkConstrained(Path *path, + PathVertex *clk_path) : + PathEnd(path), + clk_path_(clk_path), + crpr_(0.0), + crpr_valid_(false) +{ +} + +PathEndClkConstrained::PathEndClkConstrained(Path *path, + PathVertex *clk_path, + Crpr crpr, + bool crpr_valid) : + PathEnd(path), + clk_path_(clk_path), + crpr_(crpr), + crpr_valid_(crpr_valid) +{ +} + +void +PathEndClkConstrained::setPath(PathEnumed *path, + const StaState *) +{ + path_.init(path); + crpr_valid_ = false; +} + +float +PathEndClkConstrained::sourceClkOffset(const StaState *sta) const +{ + return sourceClkOffset(sourceClkEdge(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 +{ + Sdc *sdc = sta->sdc(); + CycleAccting *acct = sdc->cycleAccting(src_clk_edge, tgt_clk_edge); + return acct->sourceTimeOffset(check_role); +} + +Arrival +PathEndClkConstrained::sourceClkLatency(const StaState *sta) const +{ + ClkInfo *clk_info = path_.clkInfo(sta); + return clk_info->latency(); +} + +Arrival +PathEndClkConstrained::sourceClkInsertionDelay(const StaState *sta) const +{ + ClkInfo *clk_info = path_.clkInfo(sta); + return clk_info->insertion(); +} + +PathVertex * +PathEndClkConstrained::targetClkPath() +{ + if (clk_path_.isNull()) + return nullptr; + else + return &clk_path_; +} + +const PathVertex * +PathEndClkConstrained::targetClkPath() const +{ + if (clk_path_.isNull()) + return nullptr; + else + return &clk_path_; +} + +float +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(); + CycleAccting *acct = sdc->cycleAccting(src_clk_edge, tgt_clk_edge); + return acct->targetTimeOffset(check_role); +} + +ClockEdge * +PathEndClkConstrained::targetClkEdge(const StaState *sta) const +{ + if (!clk_path_.isNull()) + return clk_path_.clkEdge(sta); + else + return nullptr; +} + +Clock * +PathEndClkConstrained::targetClk(const StaState *sta) const +{ + ClockEdge *clk_edge = targetClkEdge(sta); + if (clk_edge) + return clk_edge->clock(); + else + return nullptr; +} + +float +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(); + CycleAccting *acct = sdc->cycleAccting(src_clk_edge, tgt_clk_edge); + return acct->requiredTime(check_role); +} + +Arrival +PathEndClkConstrained::targetClkArrival(const StaState *sta) const +{ + return targetClkArrivalNoCrpr(sta) + + commonClkPessimism(sta); +} + +Arrival +PathEndClkConstrained::targetClkArrivalNoCrpr(const StaState *sta) const +{ + return targetClkTime(sta) + + targetClkDelay(sta) + + checkClkUncertainty(sourceClkEdge(sta), + targetClkEdge(sta), + targetClkPath(), + checkRole(sta), sta) + + targetClkMcpAdjustment(sta); +} + +Arrival +PathEndClkConstrained::targetClkDelay(const StaState *sta) const +{ + return checkTgtClkDelay(targetClkPath(), targetClkEdge(sta), + checkRole(sta), sta); +} + +Arrival +PathEndClkConstrained::targetClkInsertionDelay(const StaState *sta) const +{ + Arrival insertion, latency; + checkTgtClkDelay(targetClkPath(), targetClkEdge(sta), + checkRole(sta), sta, + insertion, latency); + return insertion; +} + +float +PathEndClkConstrained::targetNonInterClkUncertainty(const StaState *sta) const +{ + const ClockEdge *src_clk_edge = sourceClkEdge(sta); + const ClockEdge *tgt_clk_edge = targetClkEdge(sta); + const TimingRole *check_role = checkRole(sta); + + float inter_clk; + bool inter_exists; + checkInterClkUncertainty(src_clk_edge, tgt_clk_edge, check_role, + sta, inter_clk, inter_exists); + if (inter_exists) + // This returns non inter-clock uncertainty. + return 0.0; + else + return checkNonInterClkUncertainty(targetClkPath(), tgt_clk_edge, + check_role, sta); +} + +float +PathEndClkConstrained::interClkUncertainty(const StaState *sta) const +{ + float uncertainty; + bool exists; + checkInterClkUncertainty(sourceClkEdge(sta), targetClkEdge(sta), + checkRole(sta), sta, + uncertainty, exists); + if (exists) + return uncertainty; + else + return 0.0; +} + +float +PathEndClkConstrained::targetClkUncertainty(const StaState *sta) const +{ + return checkClkUncertainty(sourceClkEdge(sta), targetClkEdge(sta), + targetClkPath(), checkRole(sta), sta); +} + +Crpr +PathEndClkConstrained::commonClkPessimism(const StaState *sta) const +{ + if (!crpr_valid_) { + CheckCrpr *check_crpr = sta->search()->checkCrpr(); + crpr_ = check_crpr->checkCrpr(path_.path(), targetClkPath()); + if (checkRole(sta)->genericRole() == TimingRole::hold()) + crpr_ = -crpr_; + crpr_valid_ = true; + } + return crpr_; +} + +Required +PathEndClkConstrained::requiredTime(const StaState *sta) const +{ + return requiredTimeNoCrpr(sta) + + commonClkPessimism(sta); +} + +Required +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; + else + return tgt_clk_arrival + check_margin; +} + +Slack +PathEndClkConstrained::slack(const StaState *sta) const +{ + Arrival arrival = dataArrivalTime(sta); + Required required = requiredTime(sta); + if (checkGenericRole(sta) == TimingRole::setup()) + return required - arrival; + else + return arrival - required; +} + +int +PathEndClkConstrained::exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const +{ + int cmp = PathEnd::exceptPathCmp(path_end, sta); + if (cmp == 0) { + const PathEndClkConstrained *path_end2 = + dynamic_cast(path_end); + const PathVertex *clk_path2 = path_end2->targetClkPath(); + return Path::cmp(targetClkPath(), clk_path2, sta); + } + else + return cmp; +} + +//////////////////////////////////////////////////////////////// + +PathEndClkConstrainedMcp::PathEndClkConstrainedMcp(Path *path, + PathVertex *clk_path, + MultiCyclePath *mcp) : + PathEndClkConstrained(path, clk_path), + mcp_(mcp) +{ +} + +PathEndClkConstrainedMcp::PathEndClkConstrainedMcp(Path *path, + PathVertex *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 +{ + return checkMcpAdjustment(path_.path(), targetClkEdge(sta), sta); +} + +float +PathEndClkConstrainedMcp::checkMcpAdjustment(const Path *path, + 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(); + if (min_max == MinMax::max()) + return PathEnd::checkSetupMcpAdjustment(src_clk_edge, tgt_clk_edge, + mcp_, 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; + else if (src_clk_edge->clock() == sdc->defaultArrivalClock()) + 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; + } + 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; + } + 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; + } + else + return 0.0; + } + } + else + return 0.0; +} + +float +PathEnd::checkSetupMcpAdjustment(const ClockEdge *src_clk_edge, + const ClockEdge *tgt_clk_edge, + const MultiCyclePath *mcp, + Sdc *sdc) +{ + if (mcp) { + // Default arrival clock is a proxy for the target clock. + if (src_clk_edge == nullptr) + 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()); + if (mcp->minMax()->matches(MinMax::max())) { + int mult = mcp->pathMultiplier(MinMax::max()); + const ClockEdge *clk_edge = + mcp->useEndClk() ? tgt_clk_edge : src_clk_edge; + float period = clk_edge->clock()->period(); + return (mult - 1) * period; + } + else + return 0.0; + } + else + return 0.0; +} + +Slack +PathEndClkConstrained::slackNoCrpr(const StaState *sta) const +{ + Arrival arrival = dataArrivalTime(sta); + Required required = requiredTimeNoCrpr(sta); + if (checkGenericRole(sta) == TimingRole::setup()) + return required - arrival; + else + return arrival - required; +} + +void +PathEndClkConstrainedMcp::findHoldMcps(const ClockEdge *tgt_clk_edge, + const MultiCyclePath *&setup_mcp, + const MultiCyclePath *&hold_mcp, + const StaState *sta) const + +{ + Pin *pin = path_.pin(sta); + const TransRiseFall *tr = path_.transition(sta); + // Mcp may be setup, hold or setup_hold, since all match min paths. + const MinMaxAll *mcp_min_max = mcp_->minMax(); + Search *search = sta->search(); + if (mcp_min_max->matches(MinMax::min())) { + hold_mcp = mcp_; + setup_mcp = + dynamic_cast(search->exceptionTo(ExceptionPathType::multi_cycle, + path_.path(), pin, tr, + tgt_clk_edge, + MinMax::max(), true, + false)); + } + else { + setup_mcp = mcp_; + hold_mcp = + dynamic_cast(search->exceptionTo(ExceptionPathType::multi_cycle, + path_.path(), pin, tr, + tgt_clk_edge, + MinMax::min(), true, + false)); + } +} + +int +PathEndClkConstrainedMcp::exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const +{ + int cmp = PathEndClkConstrained::exceptPathCmp(path_end, sta); + if (cmp == 0) { + const PathEndClkConstrainedMcp *path_end2 = + dynamic_cast(path_end); + const MultiCyclePath *mcp2 = path_end2->mcp_; + if (mcp_ == mcp2) + return 0; + else if (mcp_ < mcp2) + return -1; + else + return 1; + } + else + return cmp; +} + +//////////////////////////////////////////////////////////////// + +PathEndCheck::PathEndCheck(Path *path, + TimingArc *check_arc, + Edge *check_edge, + PathVertex *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, + PathVertex *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() +{ + return new PathEndCheck(path_.path(), check_arc_, check_edge_, + &clk_path_, mcp_, crpr_, crpr_valid_); +} + +PathEnd::Type +PathEndCheck::type() const +{ + return Type::check; +} + +const char * +PathEndCheck::typeName() const +{ + return "check"; +} + +void +PathEndCheck::reportFull(ReportPath *report, + string &result) const +{ + report->reportFull(this, result); +} + +void +PathEndCheck::reportShort(ReportPath *report, + string &result) const +{ + report->reportShort(this, result); +} + +TimingRole * +PathEndCheck::checkRole(const StaState *) const +{ + return check_edge_->role(); +} + +ArcDelay +PathEndCheck::margin(const StaState *sta) const +{ + return sta->search()->deratedDelay(clk_path_.vertex(sta), + check_arc_, check_edge_, false, + pathAnalysisPt(sta)); +} + +int +PathEndCheck::exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const +{ + int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); + if (cmp == 0) { + const PathEndCheck *path_end2=dynamic_cast(path_end); + const TimingArc *check_arc2 = path_end2->check_arc_; + if (check_arc_ == check_arc2) + return 0; + else if (check_arc_ < check_arc2) + return -1; + else + return 1; + } + else + return cmp; +} + +//////////////////////////////////////////////////////////////// + +PathEndLatchCheck::PathEndLatchCheck(Path *path, + TimingArc *check_arc, + Edge *check_edge, + PathVertex *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) +{ + PathVertex enable_path; + Latches *latches = sta->latches(); + latches->latchEnableOtherPath(disable_path, + disable_path->pathAnalysisPt(sta), + enable_path); + clk_path_.copy(enable_path); + Search *search = sta->search(); + // Same as PathEndPathDelay::findRequired. + if (path_delay_ && path_delay_->ignoreClkLatency()) + src_clk_arrival_ = search->pathClkPathArrival(&path_); +} + +PathEndLatchCheck::PathEndLatchCheck(Path *path, + TimingArc *check_arc, + Edge *check_edge, + PathVertex *clk_path, + PathVertex *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() +{ + return new PathEndLatchCheck(path_.path(), check_arc_, check_edge_, + &clk_path_, &disable_path_, mcp_, path_delay_, + src_clk_arrival_, crpr_, crpr_valid_); +} + +PathEnd::Type +PathEndLatchCheck::type() const +{ + return Type::latch_check; +} + +const char * +PathEndLatchCheck::typeName() const +{ + return "latch_check"; +} + +PathVertex * +PathEndLatchCheck::latchDisable() +{ + if (disable_path_.isNull()) + return nullptr; + else + return &disable_path_; +} + +const PathVertex * +PathEndLatchCheck::latchDisable() const +{ + if (disable_path_.isNull()) + return nullptr; + else + return &disable_path_; +} + +void +PathEndLatchCheck::reportFull(ReportPath *report, + string &result) const +{ + report->reportFull(this, result); +} + +void +PathEndLatchCheck::reportShort(ReportPath *report, + string &result) const +{ + report->reportShort(this, result); +} + +float +PathEndLatchCheck::sourceClkOffset(const StaState *sta) const +{ + if (path_delay_) + return pathDelaySrcClkOffset(path_, path_delay_, src_clk_arrival_, sta); + else + return PathEndClkConstrained::sourceClkOffset(sourceClkEdge(sta), + disable_path_.clkEdge(sta), + TimingRole::setup(), + sta); +} + +TimingRole * +PathEndLatchCheck::checkRole(const StaState *sta) const +{ + if (clk_path_.clkInfo(sta)->isPulseClk()) + // Pulse latches use register cycle accounting. + return TimingRole::setup(); + else + // Setup cycle accting is slightly different because it is wrt + // the enable opening edge, not the disable (setup check) edge. + return TimingRole::latchSetup(); +} + +Required +PathEndLatchCheck::requiredTime(const StaState *sta) const +{ + Required required; + Arrival borrow, adjusted_data_arrival, time_given_to_startpoint; + Latches *latches = sta->latches(); + latches->latchRequired(path_.path(), targetClkPath(), latchDisable(), + mcp_, path_delay_, src_clk_arrival_, margin(sta), + required, borrow, adjusted_data_arrival, + time_given_to_startpoint); + return required; +} + +Arrival +PathEndLatchCheck::borrow(const StaState *sta) const +{ + Latches *latches = sta->latches(); + Required required; + Arrival borrow, adjusted_data_arrival, time_given_to_startpoint; + latches->latchRequired(path_.path(), targetClkPath(), latchDisable(), + 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 +{ + Latches *latches = sta->latches(); + latches->latchRequired(path_.path(), targetClkPath(), latchDisable(), + 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 +{ + Latches *latches = sta->latches(); + latches->latchBorrowInfo(path_.path(), targetClkPath(), latchDisable(), + margin(sta), + path_delay_ && path_delay_->ignoreClkLatency(), + nom_pulse_width, open_latency, + latency_diff, open_uncertainty, + open_crpr, crpr_diff, max_borrow, + borrow_limit_exists); +} + +Arrival +PathEndLatchCheck::targetClkWidth(const StaState *sta) const +{ + const Search *search = sta->search(); + Arrival disable_arrival = search->clkPathArrival(&disable_path_); + Arrival enable_arrival = search->clkPathArrival(&clk_path_); + ClkInfo *enable_clk_info = clk_path_.clkInfo(sta); + if (enable_clk_info->isPulseClk()) + return disable_arrival - enable_arrival; + else { + if (enable_arrival > disable_arrival) { + float period = enable_clk_info->clock()->period(); + disable_arrival += period; + } + return disable_arrival - enable_arrival; + } +} + +int +PathEndLatchCheck::exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const +{ + int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); + if (cmp == 0) { + const PathEndLatchCheck *path_end2 = + dynamic_cast(path_end); + const TimingArc *check_arc2 = path_end2->check_arc_; + if (check_arc_ == check_arc2) { + const Path *disable_path2 = path_end2->disable_path_.path(); + return Path::cmp(disable_path_.path(), disable_path2, sta); + } + else if (check_arc_ < check_arc2) + return -1; + else + return 1; + } + else + return cmp; +} + +/////////////////////////////////////////////////////////////// + +PathEndOutputDelay::PathEndOutputDelay(OutputDelay *output_delay, + Path *path, + PathVertex *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, + PathVertex *clk_path, + MultiCyclePath *mcp, + Crpr crpr, + bool crpr_valid) : + PathEndClkConstrainedMcp(path, clk_path, mcp, crpr, crpr_valid), + output_delay_(output_delay) +{ +} + +PathEnd * +PathEndOutputDelay::copy() +{ + return new PathEndOutputDelay(output_delay_, path_.path(), &clk_path_, + mcp_, crpr_, crpr_valid_); +} + +PathEnd::Type +PathEndOutputDelay::type() const +{ + return Type::output_delay; +} + +const char * +PathEndOutputDelay::typeName() const +{ + return "output_delay"; +} + +void +PathEndOutputDelay::reportFull(ReportPath *report, + string &result) const +{ + report->reportFull(this, result); +} + +void +PathEndOutputDelay::reportShort(ReportPath *report, + string &result) const +{ + report->reportShort(this, result); +} + +ArcDelay +PathEndOutputDelay::margin(const StaState *sta) const +{ + return outputDelayMargin(output_delay_, path_.path(), sta); +} + +float +PathEnd::outputDelayMargin(OutputDelay *output_delay, + const Path *path, + const StaState *sta) +{ + const TransRiseFall *tr = path->transition(sta); + const MinMax *min_max = path->minMax(sta); + float margin = output_delay->delays()->value(tr, min_max); + if (min_max == MinMax::max()) + return margin; + else + return -margin; +} + +TimingRole * +PathEndOutputDelay::checkRole(const StaState *sta) const +{ + if (path_.minMax(sta) == MinMax::max()) + return TimingRole::outputSetup(); + else + return TimingRole::outputHold(); +} + +ClockEdge * +PathEndOutputDelay::targetClkEdge(const StaState *sta) const +{ + if (!clk_path_.isNull()) + return clk_path_.clkEdge(sta); + else + return output_delay_->clkEdge(); +} + +Arrival +PathEndOutputDelay::targetClkArrivalNoCrpr(const StaState *sta) const +{ + if (!clk_path_.isNull()) + return PathEndClkConstrained::targetClkArrivalNoCrpr(sta); + 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_.path(), tgt_clk_edge, sta); + } +} + +Crpr +PathEndOutputDelay::commonClkPessimism(const StaState *sta) const +{ + if (!crpr_valid_) { + CheckCrpr *check_crpr = sta->search()->checkCrpr(); + crpr_ = check_crpr->outputDelayCrpr(path_.path(), targetClkEdge(sta)); + if (checkRole(sta)->genericRole() == TimingRole::hold()) + crpr_ = -crpr_; + } + return crpr_; +} + +Arrival +PathEndOutputDelay::targetClkDelay(const StaState *sta) const +{ + if (!clk_path_.isNull()) + return PathEndClkConstrained::targetClkDelay(sta); + else + return tgtClkDelay(targetClkEdge(sta), checkRole(sta), sta); +} + +Arrival +PathEndOutputDelay::tgtClkDelay(const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta) const +{ + Arrival insertion, latency; + tgtClkDelay(tgt_clk_edge, check_role, sta, + insertion, latency); + return insertion + latency; +} + +void +PathEndOutputDelay::tgtClkDelay(const ClockEdge *tgt_clk_edge, + 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(); + Clock *tgt_clk = tgt_clk_edge->clock(); + TransRiseFall *tgt_clk_tr = tgt_clk_edge->transition(); + if (!output_delay_->sourceLatencyIncluded()) + insertion = sta->search()->clockInsertion(tgt_clk, + tgt_clk->defaultPin(), + tgt_clk_tr, + latency_min_max, + early_late, path_ap); + else + insertion = 0.0; + const Sdc *sdc = sta->sdc(); + if (!tgt_clk->isPropagated() + && !output_delay_->networkLatencyIncluded()) + latency = sdc->clockLatency(tgt_clk, tgt_clk_tr, latency_min_max); + else + latency = 0.0; +} + +Arrival +PathEndOutputDelay::targetClkInsertionDelay(const StaState *sta) const +{ + if (!clk_path_.isNull()) + return PathEndClkConstrained::targetClkInsertionDelay(sta); + else { + Arrival insertion, latency; + tgtClkDelay(targetClkEdge(sta), checkRole(sta), sta, + insertion, latency); + return insertion; + } +} + +int +PathEndOutputDelay::exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const +{ + int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); + if (cmp == 0) { + const PathEndOutputDelay *path_end2 = + dynamic_cast(path_end); + OutputDelay *output_delay2 = path_end2->output_delay_; + if (output_delay_ == output_delay2) + return 0; + else if (output_delay_ < output_delay2) + return -1; + else + return 1; + } + else + return cmp; +} + +//////////////////////////////////////////////////////////////// + +PathEndGatedClock::PathEndGatedClock(Path *gating_ref, + PathVertex *clk_path, + 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, + PathVertex *clk_path, + 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() +{ + return new PathEndGatedClock(path_.path(), &clk_path_, check_role_, + mcp_, margin_, crpr_, crpr_valid_); +} + +PathEnd::Type +PathEndGatedClock::type() const +{ + return Type::gated_clk; +} + +const char * +PathEndGatedClock::typeName() const +{ + return "gated_clk"; +} + +TimingRole * +PathEndGatedClock::checkRole(const StaState *) const +{ + return check_role_; +} + +void +PathEndGatedClock::reportFull(ReportPath *report, + string &result) const +{ + report->reportFull(this, result); +} + +void +PathEndGatedClock::reportShort(ReportPath *report, + string &result) const +{ + report->reportShort(this, result); +} + +int +PathEndGatedClock::exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const +{ + int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); + if (cmp == 0) { + const PathEndGatedClock *path_end2 = + dynamic_cast(path_end); + TimingRole *check_role2 = path_end2->check_role_; + if (check_role_ == check_role2) + return 0; + else if (check_role_ < check_role2) + return -1; + else + return 1; + } + else + return cmp; +} + +//////////////////////////////////////////////////////////////// + +PathEndDataCheck::PathEndDataCheck(DataCheck *check, + Path *data_path, + PathVertex *data_clk_path, + MultiCyclePath *mcp, + const StaState *sta) : + PathEndClkConstrainedMcp(data_path, nullptr, mcp), + data_clk_path_(data_clk_path), + check_(check) +{ + clkPath(data_clk_path, sta, clk_path_); +} + +PathEndDataCheck::PathEndDataCheck(DataCheck *check, + Path *data_path, + PathVertex *data_clk_path, + PathVertex *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() +{ + return new PathEndDataCheck(check_, path_.path(), &data_clk_path_, + &clk_path_, mcp_, crpr_, crpr_valid_); +} + +PathEnd::Type +PathEndDataCheck::type() const +{ + return Type::data_check; +} + +const char * +PathEndDataCheck::typeName() const +{ + return "data_check"; +} + +Arrival +PathEndDataCheck::requiredTimeNoCrpr(const StaState *sta) const +{ + Arrival data_clk_arrival = data_clk_path_.arrival(sta); + 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); + + ArcDelay check_margin = margin(sta); + if (checkGenericRole(sta) == TimingRole::setup()) + return tgt_clk_arrival - check_margin; + else + return tgt_clk_arrival + check_margin; +} + +ArcDelay +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); + return margin; +} + +TimingRole * +PathEndDataCheck::checkRole(const StaState *sta) const +{ + if (path_.minMax(sta) == MinMax::max()) + return TimingRole::dataCheckSetup(); + else + return TimingRole::dataCheckHold(); +} + +void +PathEndDataCheck::reportFull(ReportPath *report, + string &result) const +{ + report->reportFull(this, result); +} + +void +PathEndDataCheck::reportShort(ReportPath *report, + string &result) const +{ + report->reportShort(this, result); +} + +int +PathEndDataCheck::exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const +{ + int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta); + if (cmp == 0) { + const PathEndDataCheck *path_end2 = + dynamic_cast(path_end); + const DataCheck *check2 = path_end2->check_; + if (check_ == check2) + return 0; + else if (check_ < check2) + return -1; + else + return 1; + } + else + return cmp; +} + +//////////////////////////////////////////////////////////////// + +PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, + Path *path, + const StaState *sta): + PathEndClkConstrained(path, nullptr), + path_delay_(path_delay), + check_arc_(nullptr), + check_edge_(nullptr), + output_delay_(nullptr) +{ + findSrcClkArrival(sta); +} + +PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, + Path *path, + OutputDelay *output_delay, + const StaState *sta): + PathEndClkConstrained(path, nullptr), + path_delay_(path_delay), + check_arc_(nullptr), + check_edge_(nullptr), + output_delay_(output_delay) +{ + findSrcClkArrival(sta); +} + +PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, + Path *path, + PathVertex *clk_path, + TimingArc *check_arc, + Edge *check_edge, + const StaState *sta) : + PathEndClkConstrained(path, clk_path), + path_delay_(path_delay), + check_arc_(check_arc), + check_edge_(check_edge), + output_delay_(nullptr) +{ + findSrcClkArrival(sta); +} + +PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay, + Path *path, + PathVertex *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() +{ + return new PathEndPathDelay(path_delay_, path_.path(), &clk_path_, + check_arc_, check_edge_, output_delay_, + src_clk_arrival_, crpr_, crpr_valid_); +} + +PathEnd::Type +PathEndPathDelay::type() const +{ + return Type::path_delay; +} + +const char * +PathEndPathDelay::typeName() const +{ + return "path_delay"; +} + +void +PathEndPathDelay::findSrcClkArrival(const StaState *sta) +{ + if (path_delay_->ignoreClkLatency()) { + Search *search = sta->search(); + src_clk_arrival_ = search->pathClkPathArrival(&path_); + } +} + +void +PathEndPathDelay::reportFull(ReportPath *report, + string &result) const +{ + report->reportFull(this, result); +} + +void +PathEndPathDelay::reportShort(ReportPath *report, + string &result) const +{ + report->reportShort(this, result); +} + +bool +PathEndPathDelay::pathDelayMarginIsExternal() const +{ + return check_arc_ == nullptr; +} + +TimingRole * +PathEndPathDelay::checkRole(const StaState *sta) const +{ + if (check_edge_) + return check_edge_->role(); + else if (minMax(sta) == MinMax::max()) + return TimingRole::setup(); + else + return TimingRole::hold(); +} + +ArcDelay +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)); + } + else if (output_delay_) + return outputDelayMargin(output_delay_, path_.path(), sta); + else + return delay_zero; +} + +float +PathEndPathDelay::sourceClkOffset(const StaState *sta) const +{ + return pathDelaySrcClkOffset(path_, path_delay_, src_clk_arrival_, sta); +} + +// Helper shared by PathEndLatchCheck. +float +PathEnd::pathDelaySrcClkOffset(const PathRef &path, + PathDelay *path_delay, + Arrival src_clk_arrival, + const StaState *sta) +{ + float offset = 0.0; + ClockEdge *clk_edge = path.clkEdge(sta); + if (clk_edge) { + if (path_delay->ignoreClkLatency()) + offset = -delayAsFloat(src_clk_arrival); + else + // Arrival includes src clock edge time that is not counted in the + // path delay. + offset = -clk_edge->time(); + } + return offset; +} + +float +PathEndPathDelay::targetClkTime(const StaState *sta) const +{ + const ClockEdge *tgt_clk_edge = targetClkEdge(sta); + if (tgt_clk_edge) + return tgt_clk_edge->time(); + else + return 0.0; +} + +Arrival +PathEndPathDelay::targetClkArrivalNoCrpr(const StaState *sta) const +{ + if (!clk_path_.isNull()) { + ClockEdge *tgt_clk_edge = targetClkEdge(sta); + if (tgt_clk_edge) + return targetClkDelay(sta) + + targetClkUncertainty(sta); + else + return clk_path_.arrival(sta); + } + else + return 0.0; +} + +float +PathEndPathDelay::targetClkOffset(const StaState *) const +{ + return 0.0; +} + +Required +PathEndPathDelay::requiredTime(const StaState *sta) const +{ + float delay = path_delay_->delay(); + if (path_delay_->ignoreClkLatency()) { + if (minMax(sta) == MinMax::max()) + return src_clk_arrival_ + delay - margin(sta); + else + return src_clk_arrival_ + delay + margin(sta); + } + else { + Arrival tgt_clk_arrival = 0.0; + if (!clk_path_.isNull()) + 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. + if (minMax(sta) == MinMax::max()) + return delay - src_clk_offset + tgt_clk_arrival - margin(sta); + else + return delay - src_clk_offset + tgt_clk_arrival + margin(sta); + } +} + +int +PathEndPathDelay::exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const +{ + int cmp = PathEndClkConstrained::exceptPathCmp(path_end, sta); + if (cmp == 0) { + const PathEndPathDelay *path_end2 = + dynamic_cast(path_end); + PathDelay *path_delay2 = path_end2->path_delay_; + if (path_delay_ == path_delay2) { + const TimingArc *check_arc2 = path_end2->check_arc_; + if (check_arc_ == check_arc2) + return 0; + else if (check_arc_ < check_arc2) + return -1; + else + return 1; + } + else if (path_delay_ < path_delay2) + return -1; + else + return 1; + } + else + return cmp; +} + +//////////////////////////////////////////////////////////////// + +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) +{ + return cmp(path_end1, path_end2, sta) < 0; +} + +int +PathEnd::cmp(const PathEnd *path_end1, + const PathEnd *path_end2, + const StaState *sta) +{ + int cmp = path_end1->isUnconstrained() + ? -cmpArrival(path_end1, path_end2, sta) + : cmpSlack(path_end1, path_end2, sta); + if (cmp == 0) { + const Path *path1 = path_end1->path(); + const Path *path2 = path_end2->path(); + cmp = Path::cmpPinTrClk(path1, path2, sta); + if (cmp == 0) + return clkEdgeCmp(path_end1->targetClkEdge(sta), + path_end2->targetClkEdge(sta)); + else + return cmp; + } + else + return cmp; +} + +int +PathEnd::cmpSlack(const PathEnd *path_end1, + const PathEnd *path_end2, + const StaState *sta) +{ + Slack slack1 = path_end1->slack(sta); + Slack slack2 = path_end2->slack(sta); + if (fuzzyZero(slack1) + && fuzzyZero(slack2) + && 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 (fuzzyEqual(borrow1, borrow2)) + return 0; + else if (borrow1 > borrow2) + return -1; + else + return 1; + } + else if (fuzzyEqual(slack1, slack2)) + return 0; + else if (slack1 < slack2) + return -1; + else + return 1; +} + +int +PathEnd::cmpArrival(const PathEnd *path_end1, + 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 (fuzzyEqual(arrival1, arrival2)) + return 0; + else if (fuzzyLess(arrival1, arrival2, min_max)) + return -1; + else + return 1; +} + +int +PathEnd::cmpNoCrpr(const PathEnd *path_end1, + const PathEnd *path_end2, + const StaState *sta) +{ + int cmp = path_end1->exceptPathCmp(path_end2, sta); + if (cmp == 0) { + const Path *path1 = path_end1->path(); + const Path *path2 = path_end2->path(); + return Path::cmpNoCrpr(path1, path2, sta); + } + else + return cmp; +} + +//////////////////////////////////////////////////////////////// + +PathEndSlackLess::PathEndSlackLess(const StaState *sta) : + sta_(sta) +{ +} + +bool +PathEndSlackLess::operator()(const PathEnd *path_end1, + const PathEnd *path_end2) const +{ + int cmp = path_end1->isUnconstrained() + ? -PathEnd::cmpArrival(path_end1, path_end2, sta_) + : PathEnd::cmpSlack(path_end1, path_end2, sta_); + return cmp < 0; +} + +//////////////////////////////////////////////////////////////// + +PathEndNoCrprLess::PathEndNoCrprLess(const StaState *sta) : + sta_(sta) +{ +} + +bool +PathEndNoCrprLess::operator()(const PathEnd *path_end1, + const PathEnd *path_end2) const +{ + int cmp = path_end1->exceptPathCmp(path_end2, sta_); + if (cmp == 0) { + const Path *path1 = path_end1->path(); + const Path *path2 = path_end2->path(); + return Path::cmpNoCrpr(path1, path2, sta_) < 0; + } + else + return cmp < 0; +} + +} // namespace diff --git a/search/PathEnd.hh b/search/PathEnd.hh new file mode 100644 index 0000000..c2778e1 --- /dev/null +++ b/search/PathEnd.hh @@ -0,0 +1,663 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PATH_END_H +#define STA_PATH_END_H + +#include +#include "DisallowCopyAssign.hh" +#include "LibertyClass.hh" +#include "GraphClass.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" +#include "PathRef.hh" +#include "Crpr.hh" +#include "StaState.hh" + +namespace sta { + +class StaState; +class TransRiseFall; +class MinMax; +class ReportPath; + +using std::string; + +// PathEnds represent search endpoints that are either unconstrained +// or constrained by a timing check, output delay, data check, +// or path delay. +// +// Class hierarchy: +// PathEnd (abstract) +// PathEndUnconstrained +// PathEndClkConstrained (abstract) +// PathEndPathDelay (clock is optional) +// PathEndClkConstrainedMcp (abstract) +// PathEndCheck +// PathEndLatchCheck +// PathEndOutputDelay +// PathEndGatedClock +// PathEndDataCheck +// +class PathEnd +{ +public: + enum Type { unconstrained, + check, + data_check, + latch_check, + output_delay, + gated_clk, + path_delay + }; + + virtual PathEnd *copy() = 0; + virtual ~PathEnd(); + void deletePath(); + Path *path() { return &path_; } + const Path *path() const { return &path_; } + PathRef &pathRef() { return path_; } + virtual void setPath(PathEnumed *path, + const StaState *sta); + Vertex *vertex(const StaState *sta) const; + const MinMax *minMax(const StaState *sta) const; + // Synonym for minMax(). + const EarlyLate *pathEarlyLate(const StaState *sta) const; + virtual const EarlyLate *clkEarlyLate(const StaState *sta) const; + const TransRiseFall *transition(const StaState *sta) const; + PathAnalysisPt *pathAnalysisPt(const StaState *sta) const; + PathAPIndex pathIndex(const StaState *sta) const; + virtual void reportShort(ReportPath *report, + string &result) const = 0; + virtual void reportFull(ReportPath *report, + string &result) const = 0; + + // 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; } + 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; + // Arrival time with source clock offset. + Arrival dataArrivalTimeOffset(const StaState *sta) const; + virtual Required requiredTime(const StaState *sta) const = 0; + // Required time with source clock offset. + virtual Required requiredTimeOffset(const StaState *sta) const; + virtual ArcDelay margin(const StaState *sta) const = 0; + virtual Slack slack(const StaState *sta) const = 0; + virtual Slack slackNoCrpr(const StaState *sta) const = 0; + virtual Arrival borrow(const StaState *sta) const; + ClockEdge *sourceClkEdge(const StaState *sta) const; + // Time offset for the path start so the path begins in the correct + // source cycle. + virtual float sourceClkOffset(const StaState *sta) const = 0; + virtual Delay sourceClkLatency(const StaState *sta) const; + virtual Delay sourceClkInsertionDelay(const StaState *sta) const; + virtual PathVertex *targetClkPath(); + virtual const PathVertex *targetClkPath() const; + virtual Clock *targetClk(const StaState *sta) const; + virtual ClockEdge *targetClkEdge(const StaState *sta) const; + const TransRiseFall *targetClkEndTrans(const StaState *sta) const; + // Target clock with cycle accounting and source clock offsets. + virtual float targetClkTime(const StaState *sta) const; + // Time offset for the target clock. + virtual float targetClkOffset(const StaState *sta) const; + // Target clock with source clock offset. + virtual Arrival targetClkArrival(const StaState *sta) const; + // Target clock tree delay. + virtual Delay targetClkDelay(const StaState *sta) const; + virtual Delay targetClkInsertionDelay(const StaState *sta) const; + // Does NOT include inter-clk uncertainty. + virtual float targetNonInterClkUncertainty(const StaState *sta) const; + virtual float interClkUncertainty(const StaState *sta) const; + // Target clock uncertainty + inter-clk uncertainty. + virtual float targetClkUncertainty(const StaState *sta) const; + virtual float targetClkMcpAdjustment(const StaState *sta) const; + virtual TimingRole *checkRole(const StaState *sta) const; + const TimingRole *checkGenericRole(const StaState *sta) const; + virtual bool pathDelayMarginIsExternal() const; + virtual PathDelay *pathDelay() const; + virtual Crpr commonClkPessimism(const StaState *sta) const; + virtual MultiCyclePath *multiCyclePath() const; + virtual TimingArc *checkArc() const { return nullptr; } + // PathEndDataCheck data clock path. + virtual const PathVertex *dataClkPath() const { return nullptr; } + + static bool less(const PathEnd *path_end1, + const PathEnd *path_end2, + const StaState *sta); + static int cmp(const PathEnd *path_end1, + const PathEnd *path_end2, + const StaState *sta); + static int cmpSlack(const PathEnd *path_end1, + const PathEnd *path_end2, + const StaState *sta); + static int cmpArrival(const PathEnd *path_end1, + const PathEnd *path_end2, + const StaState *sta); + static int cmpNoCrpr(const PathEnd *path_end1, + const PathEnd *path_end2, + const StaState *sta); + + // Helper common to multiple PathEnd classes and used + // externally. + // Target clock insertion delay + latency. + static Arrival checkTgtClkDelay(const PathVertex *tgt_clk_path, + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta); + static void checkTgtClkDelay(const PathVertex *tgt_clk_path, + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta, + // Return values. + Arrival &insertion, + Arrival &latency); + static float checkClkUncertainty(const ClockEdge *src_clk_edge, + const ClockEdge *tgt_clk_edge, + const PathVertex *tgt_clk_path, + const TimingRole *check_role, + const StaState *sta); + static float checkSetupMcpAdjustment(const ClockEdge *src_clk_edge, + const ClockEdge *tgt_clk_edge, + const MultiCyclePath *mcp, + Sdc *sdc); + +protected: + PathEnd(Path *path); + void clkPath(PathVertex *path, + const StaState *sta, + // Return value. + PathVertex &clk_path); + static float checkNonInterClkUncertainty(const PathVertex *tgt_clk_path, + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta); + static void checkInterClkUncertainty(const ClockEdge *src_clk_edge, + const ClockEdge *tgt_clk_edge, + const TimingRole *check_role, + const StaState *sta, + float &uncertainty, + bool &exists); + static float outputDelayMargin(OutputDelay *output_delay, + const Path *path, + const StaState *sta); + static float pathDelaySrcClkOffset(const PathRef &path, + PathDelay *path_delay, + Arrival src_clk_arrival, + const StaState *sta); + PathRef path_; + +private: + DISALLOW_COPY_AND_ASSIGN(PathEnd); +}; + +class PathEndUnconstrained : public PathEnd +{ +public: + explicit PathEndUnconstrained(Path *path); + virtual Type type() const; + virtual const char *typeName() const; + virtual PathEnd *copy(); + virtual void reportShort(ReportPath *report, + string &result) const; + virtual void reportFull(ReportPath *report, + string &result) 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; + +private: + DISALLOW_COPY_AND_ASSIGN(PathEndUnconstrained); +}; + +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 Clock *targetClk(const StaState *sta) const; + virtual ClockEdge *targetClkEdge(const StaState *sta) const; + virtual PathVertex *targetClkPath(); + virtual const PathVertex *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 commonClkPessimism(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(PathEnumed *path, + const StaState *sta); + +protected: + PathEndClkConstrained(Path *path, + PathVertex *clk_path); + PathEndClkConstrained(Path *path, + PathVertex *clk_path, + Crpr crpr, + bool crpr_valid); + + float sourceClkOffset(const ClockEdge *src_clk_edge, + 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; + + PathVertex clk_path_; + mutable Crpr crpr_; + mutable bool crpr_valid_; + +private: + DISALLOW_COPY_AND_ASSIGN(PathEndClkConstrained); +}; + +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; + +protected: + PathEndClkConstrainedMcp(Path *path, + PathVertex *clk_path, + MultiCyclePath *mcp); + PathEndClkConstrainedMcp(Path *path, + PathVertex *clk_path, + MultiCyclePath *mcp, + Crpr crpr, + bool crpr_valid); + float checkMcpAdjustment(const Path *path, + 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; + + MultiCyclePath *mcp_; + +private: + DISALLOW_COPY_AND_ASSIGN(PathEndClkConstrainedMcp); +}; + +// Path constrained by timing check. +class PathEndCheck : public PathEndClkConstrainedMcp +{ +public: + PathEndCheck(Path *path, + TimingArc *check_arc, + Edge *check_edge, + PathVertex *clk_path, + MultiCyclePath *mcp, + const StaState *sta); + virtual PathEnd *copy(); + virtual Type type() const; + virtual const char *typeName() const; + virtual void reportShort(ReportPath *report, string &result) const; + virtual void reportFull(ReportPath *report, string &result) const; + virtual bool isCheck() const { return true; } + virtual ArcDelay margin(const StaState *sta) const; + virtual TimingRole *checkRole(const StaState *sta) const; + virtual TimingArc *checkArc() const { return check_arc_; } + virtual int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const; + +protected: + PathEndCheck(Path *path, + TimingArc *check_arc, + Edge *check_edge, + PathVertex *clk_path, + MultiCyclePath *mcp, + Crpr crpr, + bool crpr_valid); + + TimingArc *check_arc_; + Edge *check_edge_; + +private: + DISALLOW_COPY_AND_ASSIGN(PathEndCheck); +}; + +// PathEndClkConstrained::clk_path_ is the latch enable. +class PathEndLatchCheck : public PathEndCheck +{ +public: + PathEndLatchCheck(Path *path, + TimingArc *check_arc, + Edge *check_edge, + PathVertex *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(); + PathVertex *latchDisable(); + const PathVertex *latchDisable() const; + virtual void reportShort(ReportPath *report, + string &result) const; + virtual void reportFull(ReportPath *report, + string &result) const; + virtual TimingRole *checkRole(const StaState *sta) const; + virtual Required requiredTime(const StaState *sta) const; + virtual Arrival borrow(const StaState *sta) const; + Arrival targetClkWidth(const StaState *sta) const; + virtual int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const; + void latchRequired(const StaState *sta, + // 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; + +protected: + PathEndLatchCheck(Path *path, + TimingArc *check_arc, + Edge *check_edge, + PathVertex *clk_path, + PathVertex *disable, + MultiCyclePath *mcp, + PathDelay *path_delay, + Delay src_clk_arrival, + Crpr crpr, + bool crpr_valid); + +private: + PathVertex disable_path_; + PathDelay *path_delay_; + // Source clk arrival for set_max_delay -ignore_clk_latency. + Arrival src_clk_arrival_; + + DISALLOW_COPY_AND_ASSIGN(PathEndLatchCheck); +}; + +// Path constrained by an output delay. +// If there is a reference pin, clk_path_ is the reference pin clock. +class PathEndOutputDelay : public PathEndClkConstrainedMcp +{ +public: + PathEndOutputDelay(OutputDelay *output_delay, + Path *path, + PathVertex *clk_path, + MultiCyclePath *mcp, + const StaState *sta); + virtual PathEnd *copy(); + virtual Type type() const; + virtual const char *typeName() const; + virtual void reportShort(ReportPath *report, + string &result) const; + virtual void reportFull(ReportPath *report, + string &result) const; + virtual bool isOutputDelay() const { return true; } + virtual ArcDelay margin(const StaState *sta) const; + virtual TimingRole *checkRole(const StaState *sta) const; + virtual 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 commonClkPessimism(const StaState *sta) const; + virtual int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const; + +protected: + PathEndOutputDelay(OutputDelay *output_delay, + Path *path, + PathVertex *clk_path, + MultiCyclePath *mcp, + Crpr crpr, + bool crpr_valid); + Arrival tgtClkDelay(const ClockEdge *tgt_clk_edge, + 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; + + OutputDelay *output_delay_; + +private: + DISALLOW_COPY_AND_ASSIGN(PathEndOutputDelay); +}; + +// Clock path constrained clock gating signal. +class PathEndGatedClock : public PathEndClkConstrainedMcp +{ +public: + PathEndGatedClock(Path *gating_ref, + PathVertex *clk_path, + TimingRole *check_role, + MultiCyclePath *mcp, + ArcDelay margin, + const StaState *sta); + virtual PathEnd *copy(); + virtual Type type() const; + virtual const char *typeName() const; + virtual void reportShort(ReportPath *report, + string &result) const; + virtual void reportFull(ReportPath *report, + string &result) const; + virtual bool isGatedClock() const { return true; } + virtual ArcDelay margin(const StaState *) const { return margin_; } + virtual TimingRole *checkRole(const StaState *sta) const; + virtual int exceptPathCmp(const PathEnd *path_end, + const StaState *sta) const; + +protected: + PathEndGatedClock(Path *gating_ref, + PathVertex *clk_path, + TimingRole *check_role, + MultiCyclePath *mcp, + ArcDelay margin, + Crpr crpr, + bool crpr_valid); + + TimingRole *check_role_; + ArcDelay margin_; + +private: + DISALLOW_COPY_AND_ASSIGN(PathEndGatedClock); +}; + +class PathEndDataCheck : public PathEndClkConstrainedMcp +{ +public: + PathEndDataCheck(DataCheck *check, + Path *data_path, + PathVertex *data_clk_path, + MultiCyclePath *mcp, + const StaState *sta); + virtual PathEnd *copy(); + virtual Type type() const; + virtual const char *typeName() const; + virtual void reportShort(ReportPath *report, + string &result) const; + virtual void reportFull(ReportPath *report, + string &result) const; + virtual bool isDataCheck() const { return true; } + virtual 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 PathVertex *dataClkPath() const { return &data_clk_path_; } + +protected: + PathEndDataCheck(DataCheck *check, + Path *data_path, + PathVertex *data_clk_path, + PathVertex *clk_path, + MultiCyclePath *mcp, + Crpr crpr, + bool crpr_valid); + Arrival requiredTimeNoCrpr(const StaState *sta) const; + +private: + PathVertex data_clk_path_; + DataCheck *check_; + +private: + DISALLOW_COPY_AND_ASSIGN(PathEndDataCheck); +}; + +// Path constrained by set_min/max_delay. +// "Clocked" when path delay ends at timing check pin. +class PathEndPathDelay : public PathEndClkConstrained +{ +public: + // Vanilla path delay. + PathEndPathDelay(PathDelay *path_delay, + Path *path, + const StaState *sta); + // Path delay to timing check. + PathEndPathDelay(PathDelay *path_delay, + Path *path, + PathVertex *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(); + virtual Type type() const; + virtual const char *typeName() const; + virtual void reportShort(ReportPath *report, + string &result) const; + virtual void reportFull(ReportPath *report, + string &result) const; + virtual bool isPathDelay() const { return true; } + virtual 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 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; + +protected: + PathEndPathDelay(PathDelay *path_delay, + Path *path, + PathVertex *clk_path, + TimingArc *check_arc, + Edge *check_edge, + OutputDelay *output_delay, + Arrival src_clk_arrival, + Crpr crpr, + bool crpr_valid); + void findSrcClkArrival(const StaState *sta); + + PathDelay *path_delay_; + TimingArc *check_arc_; + Edge *check_edge_; + // Output delay is nullptr when there is no timing check or output + // delay at the endpoint. + OutputDelay *output_delay_; + // Source clk arrival for set_min/max_delay -ignore_clk_latency. + Arrival src_clk_arrival_; + +private: + DISALLOW_COPY_AND_ASSIGN(PathEndPathDelay); +}; + +//////////////////////////////////////////////////////////////// + +// Compare slack or arrival for unconstrained path ends and pin names, +// transitions along the source path. +class PathEndLess +{ +public: + explicit PathEndLess(const StaState *sta); + bool operator()(const PathEnd *path_end1, + const PathEnd *path_end2) const; + +protected: + const StaState *sta_; +}; + +// Compare slack or arrival for unconstrained path ends. +class PathEndSlackLess +{ +public: + explicit PathEndSlackLess(const StaState *sta); + bool operator()(const PathEnd *path_end1, + const PathEnd *path_end2) const; + +protected: + const StaState *sta_; +}; + +class PathEndNoCrprLess +{ +public: + explicit PathEndNoCrprLess(const StaState *sta); + bool operator()(const PathEnd *path_end1, + const PathEnd *path_end2) const; + +protected: + const StaState *sta_; +}; + +} // namespace +#endif diff --git a/search/PathEnum.cc b/search/PathEnum.cc new file mode 100644 index 0000000..4ab0bea --- /dev/null +++ b/search/PathEnum.cc @@ -0,0 +1,614 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "Debug.hh" +#include "Error.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 "PathEnd.hh" +#include "PathRef.hh" +#include "PathEnumed.hh" +#include "PathEnum.hh" +#include "Fuzzy.hh" + +namespace sta { + +//////////////////////////////////////////////////////////////// + +// 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<--------+ +// | +// <--...--before_div<--...--path<---path_end +class Diversion +{ +public: + Diversion(PathEnd *path_end, + Path *after_div); + PathEnd *pathEnd() const { return path_end_; } + Path *divPath() const { return after_div_; } + +private: + DISALLOW_COPY_AND_ASSIGN(Diversion); + + PathEnd *path_end_; + Path *after_div_; +}; + +Diversion::Diversion(PathEnd *path_end, + Path *after_div) : + path_end_(path_end), + after_div_(after_div) +{ +} + +//////////////////////////////////////////////////////////////// + +// Default constructor required for DiversionQueue template. +DiversionGreater::DiversionGreater() : + sta_(nullptr) +{ +} + +DiversionGreater::DiversionGreater(const StaState *sta) : + sta_(sta) +{ +} + +// It is important to break all ties in this comparison so that no two +// diversions are equal. Otherwise only one of a set of paths with +// the same delay is kept in the queue. +bool +DiversionGreater::operator()(Diversion *div1, + Diversion *div2) const +{ + PathEnd *path_end1 = div1->pathEnd(); + PathEnd *path_end2 = div2->pathEnd(); + return PathEnd::cmp(path_end1, path_end2, sta_) > 0; +} + +static void +deleteDiversionPathEnd(Diversion *div) +{ + delete div->pathEnd(); + delete div; +} + +//////////////////////////////////////////////////////////////// + +PathEnum::PathEnum(int group_count, + int endpoint_count, + bool unique_pins, + bool cmp_slack, + const StaState *sta) : + StaState(sta), + cmp_slack_(cmp_slack), + group_count_(group_count), + endpoint_count_(endpoint_count), + unique_pins_(unique_pins), + div_queue_(DiversionGreater(sta)), + div_count_(0), + inserts_pruned_(false), + next_(nullptr) +{ +} + +void +PathEnum::insert(PathEnd *path_end) +{ + debugPrint1(debug_, "path_enum", 1, "insert %s\n", + path_end->path()->name(this)); + debugPrint3(debug_, "path_enum", 2, "diversion %s %s %s\n", + path_end->path()->name(this), + cmp_slack_ ? "slack" : "delay", + 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_++; +} + +PathEnum::~PathEnum() +{ + while (!div_queue_.empty()) { + Diversion *div = div_queue_.top(); + deleteDiversionPathEnd(div); + div_queue_.pop(); + } + // PathEnd on deck may not have been consumed. + delete next_; +} + +bool +PathEnum::hasNext() +{ + if (unique_pins_ + && !inserts_pruned_) { + pruneDiversionQueue(); + inserts_pruned_ = true; + } + if (next_ == nullptr + && !div_queue_.empty()) + findNext(); + return next_ != nullptr; +} + +PathEnd * +PathEnum::next() +{ + PathEnd *next = next_; + findNext(); + return next; +} + +void +PathEnum::findNext() +{ + next_ = nullptr; + // Pop the next slowest path off the queue. + while (!div_queue_.empty()) { + Diversion *div = div_queue_.top(); + div_queue_.pop(); + PathEnd *path_end = div->pathEnd(); + Vertex *vertex = path_end->vertex(this); + if (debug_->check("path_enum", 2)) { + Path *path = path_end->path(); + debug_->print("path_enum: next path %s delay %s slack %s\n", + path->name(this), + delayAsString(path_end->dataArrivalTime(this), this), + delayAsString(path_end->slack(this), this)); + reportDiversionPath(div); + } + + path_counts_[vertex]++; + if (path_counts_[vertex] <= endpoint_count_) { + // Add diversions for all arcs converging on the path up to the + // diversion. + makeDiversions(path_end, div->divPath()); + // Caller owns the path end now, so don't delete it. + next_ = path_end; + delete div; + break; + } + else { + // We have endpoint_count paths for this endpoint, so we are done with it. + debugPrint1(debug_, "path_enum", 1, "endpoint_count reached for %s\n", + vertex->name(sdc_network_)); + deleteDiversionPathEnd(div); + } + } +} + +void +PathEnum::reportDiversionPath(Diversion *div) +{ + PathEnd *path_end = div->pathEnd(); + Path *path = path_end->path(); + PathRef p; + path->prevPath(this, p); + Path *after_div = div->divPath(); + while (!p.isNull()) { + debug_->print("path_enum: %s %s%s\n", + p.name(this), + delayAsString(p.arrival(this), this), + Path::equal(&p, after_div, this) ? " <-diversion" : ""); + if (network_->isLatchData(p.pin(this))) + break; + p.prevPath(this, p); + } +} + +//////////////////////////////////////////////////////////////// + +class PathEnumFaninVisitor : public PathVisitor +{ +public: + PathEnumFaninVisitor(PathEnd *path_end, + PathRef &before_div, + bool unique_pins, + PathEnum *path_enum); + virtual VertexVisitor *copy(); + virtual void visit(Vertex *) {} // Not used. + void visitFaninPathsThru(Vertex *vertex, + Vertex *prev_vertex, + TimingArc *prev_arc); + virtual bool visitFromToPath(const Pin *from_pin, + Vertex *from_vertex, + const TransRiseFall *from_tr, + Tag *from_tag, + PathVertex *from_path, + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex *to_vertex, + const TransRiseFall *to_tr, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max, + const PathAnalysisPt *path_ap); + +private: + DISALLOW_COPY_AND_ASSIGN(PathEnumFaninVisitor); + void makeDivertedPathEnd(Path *after_div, + TimingArc *div_arc, + // Return values. + PathEnd *&div_end, + PathEnumed *&after_div_copy); + void reportDiversion(TimingArc *div_arc, + Path *after_div); + + PathEnd *path_end_; + Slack path_end_slack_; + PathRef &before_div_; + bool unique_pins_; + int before_div_tr_index_; + Tag *before_div_tag_; + PathAPIndex before_div_ap_index_; + Arrival before_div_arrival_; + TimingArc *prev_arc_; + Vertex *prev_vertex_; + PathEnum *path_enum_; + bool crpr_active_; +}; + +PathEnumFaninVisitor::PathEnumFaninVisitor(PathEnd *path_end, + PathRef &before_div, + bool unique_pins, + PathEnum *path_enum) : + PathVisitor(path_enum), + path_end_(path_end), + path_end_slack_(path_end->slack(sta_)), + before_div_(before_div), + unique_pins_(unique_pins), + before_div_tr_index_(before_div_.trIndex(sta_)), + before_div_tag_(before_div_.tag(sta_)), + before_div_ap_index_(before_div_.pathAnalysisPtIndex(sta_)), + before_div_arrival_(before_div_.arrival(sta_)), + path_enum_(path_enum), + crpr_active_(sta_->sdc()->crprActive()) +{ +} + +void +PathEnumFaninVisitor::visitFaninPathsThru(Vertex *vertex, + Vertex *prev_vertex, + TimingArc *prev_arc) +{ + before_div_tr_index_ = before_div_.trIndex(sta_); + before_div_tag_ = before_div_.tag(sta_); + before_div_ap_index_ = before_div_.pathAnalysisPtIndex(sta_); + before_div_arrival_ = before_div_.arrival(sta_); + prev_arc_ = prev_arc; + prev_vertex_ = prev_vertex; + visitFaninPaths(vertex); +} + +VertexVisitor * +PathEnumFaninVisitor::copy() +{ + return new PathEnumFaninVisitor(path_end_, before_div_, unique_pins_, + path_enum_); +} + +bool +PathEnumFaninVisitor::visitFromToPath(const Pin *, + Vertex *from_vertex, + const TransRiseFall *, + Tag *, + PathVertex *from_path, + Edge *edge, + TimingArc *arc, + ArcDelay, + Vertex *to_vertex, + const TransRiseFall *to_tr, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max, + const PathAnalysisPt *path_ap) +{ + const Debug *debug = sta_->debug(); + debugPrint4(debug, "path_enum", 3, "visit fanin %s -> %s %s %s\n", + from_path->name(sta_), + to_vertex->name(sta_->network()), + to_tr->asString(), + delayAsString(sta_->search()->deratedDelay(from_vertex, arc, edge, + false,path_ap),sta_)); + // These paths fanin to before_div_ so we know to_vertex matches. + if (to_tr->index() == before_div_tr_index_ + && path_ap->index() == before_div_ap_index_ + && arc != prev_arc_ + && (!unique_pins_ || from_vertex != prev_vertex_) + && tagMatchNoCrpr(to_tag, before_div_tag_)) { + if (crpr_active_) { + PathEnd *div_end; + PathEnumed *after_div_copy; + // Make the diverted path end to check slack with from_path crpr. + makeDivertedPathEnd(from_path, arc, div_end, after_div_copy); + // Only enumerate paths with greater slack. + if (fuzzyGreaterEqual(div_end->slack(sta_), path_end_slack_)) { + reportDiversion(arc, from_path); + path_enum_->makeDiversion(div_end, after_div_copy); + } + else + delete div_end; + } + // Only enumerate slower/faster paths. + else if (fuzzyLessEqual(to_arrival, before_div_arrival_, min_max)) { + PathEnd *div_end; + PathEnumed *after_div_copy; + makeDivertedPathEnd(from_path, arc, div_end, after_div_copy); + reportDiversion(arc, from_path); + path_enum_->makeDiversion(div_end, after_div_copy); + } + } + return true; +} + +void +PathEnumFaninVisitor::makeDivertedPathEnd(Path *after_div, + TimingArc *div_arc, + // Return values. + PathEnd *&div_end, + PathEnumed *&after_div_copy) +{ + PathEnumed *div_path; + path_enum_->makeDivertedPath(path_end_->path(), &before_div_, after_div, + div_arc, div_path, after_div_copy); + div_end = path_end_->copy(); + div_end->setPath(div_path, sta_); +} + +void +PathEnumFaninVisitor::reportDiversion(TimingArc *div_arc, + Path *after_div) +{ + Debug *debug = sta_->debug(); + if (debug->check("path_enum", 3)) { + Path *path = path_end_->path(); + const PathAnalysisPt *path_ap = path->pathAnalysisPt(sta_); + Arrival path_delay = path_enum_->cmp_slack_ + ? path_end_->slack(sta_) + : path_end_->dataArrivalTime(sta_); + Arrival div_delay = path_delay - path_enum_->divSlack(&before_div_, + after_div, + div_arc, path_ap); + PathRef div_prev; + before_div_.prevPath(sta_, div_prev); + debug->print("path_enum: diversion %s %s %s -> %s\n", + path->name(sta_), + path_enum_->cmp_slack_ ? "slack" : "delay", + delayAsString(path_delay, sta_), + delayAsString(div_delay, sta_)); + debug->print("path_enum: from %s -> %s\n", + div_prev.name(sta_), + before_div_.name(sta_)); + debug->print("path_enum: to %s ->\n", + after_div->name(sta_)); + } +} + +// 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<--------+ +// | +// <--...--before_div<--...--path<---path_end +void +PathEnum::makeDiversion(PathEnd *div_end, + PathEnumed *after_div_copy) +{ + Diversion *div = new Diversion(div_end, after_div_copy); + div_queue_.push(div); + div_count_++; + + if (static_cast(div_queue_.size()) > group_count_ * 2) + // We have more potenial paths than we will need. + pruneDiversionQueue(); +} + +void +PathEnum::pruneDiversionQueue() +{ + debugPrint0(debug_, "path_enum", 2, "prune queue\n"); + VertexPathCountMap path_counts; + int end_count = 0; + // Collect endpoint_count diversions per vertex. + DiversionSeq divs; + while (!div_queue_.empty()) { + Diversion *div = div_queue_.top(); + Vertex *vertex = div->pathEnd()->vertex(this); + if (end_count < group_count_ + && ((unique_pins_ && path_counts[vertex] == 0) + || (!unique_pins_ && path_counts[vertex] < endpoint_count_))) { + divs.push_back(div); + path_counts[vertex]++; + end_count++; + } + else + deleteDiversionPathEnd(div); + div_queue_.pop(); + } + + // Add the top diversions back. + DiversionSeq::Iterator div_iter(divs); + while (div_iter.hasNext()) { + Diversion *div = div_iter.next(); + div_queue_.push(div); + } +} + +Arrival +PathEnum::divSlack(Path *before_div, + Path *after_div, + TimingArc *div_arc, + const PathAnalysisPt *path_ap) +{ + Arrival arc_arrival = before_div->arrival(this); + Edge *div_edge = divEdge(before_div, div_arc); + 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 - arc_arrival; +} + +Edge * +PathEnum::divEdge(Path *before_div, + TimingArc *div_arc) +{ + TimingArcSet *arc_set = div_arc->set(); + VertexInEdgeIterator edge_iter(before_div->vertex(this), graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->timingArcSet() == arc_set) + return edge; + } + return nullptr; +} + +// Make diversions for all arcs that merge into path for paths +// starting at "before" to the beginning of the path. +void +PathEnum::makeDiversions(PathEnd *path_end, + Path *before) +{ + PathRef path(before); + TimingArc *prev_arc; + PathEnumFaninVisitor fanin_visitor(path_end, path, unique_pins_, this); + do { + // Fanin visitor does all the work. + // While visiting the fanins the fanin_visitor finds the + // previous path and arc as well as diversions. + PathRef prev_path; + path.prevPath(this, prev_path, prev_arc); + if (prev_arc) { + fanin_visitor.visitFaninPathsThru(path.vertex(this), + prev_path.vertex(this), prev_arc); + path.init(prev_path); + } + } while (prev_arc + // Do not enumerate beyond latch D to Q edges. + // This breaks latch loop paths. + && prev_arc->role() != TimingRole::latchDtoQ() + // Do not enumerate paths in the clk network. + && !path.isClock(this)); +} + +void +PathEnum::makeDivertedPath(Path *path, + Path *before_div, + Path *after_div, + TimingArc *div_arc, + // Returned values. + PathEnumed *&div_path, + PathEnumed *&after_div_copy) +{ + // Copy the diversion path. + bool found_div = false; + PathEnumedSeq copies; + PathRef p(path); + bool first = true; + bool is_latch_data = false; + PathEnumed *prev_copy = nullptr; + while (!p.isNull()) { + PathRef prev; + TimingArc *prev_arc; + p.prevPath(this, prev, prev_arc); + PathEnumed *copy = new PathEnumed(p.vertexIndex(this), + p.tagIndex(this), + p.arrival(this), + nullptr, // prev_path made in next pass. + prev_arc); + if (prev_copy) + prev_copy->setPrevPath(copy); + copies.push_back(copy); + + if (first) + div_path = copy; + if (Path::equal(&p, after_div, this)) + after_div_copy = copy; + // Include latch D input in the diverted path but do not enumerate + // beyond it. + if (is_latch_data) + break; + if (Path::equal(&p, before_div, this)) { + copy->setPrevArc(div_arc); + // Update the delays forward from before_div to the end of the path. + updatePathHeadDelays(copies, after_div); + p.init(after_div); + found_div = true; + } + else + p.init(prev); + prev_copy = copy; + first = false; + if (prev_arc + && prev_arc->role() == TimingRole::latchDtoQ()) + is_latch_data = true; + } + if (!found_div) + internalError("diversion path not found"); +} + +void +PathEnum::updatePathHeadDelays(PathEnumedSeq &paths, + Path *after_div) +{ + Tag *prev_tag = after_div->tag(this); + ClkInfo *clk_info = prev_tag->clkInfo(); + Arrival prev_arrival = search_->clkPathArrival(after_div); + for (int i = paths.size() - 1; i >= 0; i--) { + PathEnumed *path = paths[i]; + TimingArc *arc = path->prevArc(this); + Edge *edge = path->prevEdge(arc, this); + PathAnalysisPt *path_ap = path->pathAnalysisPt(this); + ArcDelay arc_delay = search_->deratedDelay(edge->from(graph_), + arc, edge, false, path_ap); + Arrival arrival = prev_arrival + arc_delay; + debugPrint3(debug_, "path_enum", 3, "update arrival %s %s -> %s\n", + path->name(this), + delayAsString(path->arrival(this), this), + delayAsString(arrival, this)); + path->setArrival(arrival, this); + prev_arrival = arrival; + if (sdc_->crprActive()) { + // When crpr is enabled the diverion may be from another crpr clk pin, + // so update the tags to use the corresponding ClkInfo. + Tag *tag = path->tag(this); + Tag *updated_tag = search_->findTag(path->transition(this), + path_ap, + clk_info, + tag->isClock(), + tag->inputDelay(), + tag->isSegmentStart(), + tag->states(), false); + path->setTag(updated_tag); + } + } +} + +} // namespace diff --git a/search/PathEnum.hh b/search/PathEnum.hh new file mode 100644 index 0000000..a1c1da3 --- /dev/null +++ b/search/PathEnum.hh @@ -0,0 +1,107 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PATH_ENUM_H +#define STA_PATH_ENUM_H + +#include +#include "DisallowCopyAssign.hh" +#include "Iterator.hh" +#include "Vector.hh" +#include "StaState.hh" +#include "SearchClass.hh" +#include "Path.hh" + +namespace sta { + +class Diversion; +class PathEnumFaninVisitor; +class PathEnumed; +class DiversionGreater; + +typedef Vector DiversionSeq; +typedef Vector PathEnumedSeq; +typedef std::priority_queue DiversionQueue; + +class DiversionGreater +{ +public: + DiversionGreater(); + DiversionGreater(const StaState *sta); + bool operator()(Diversion *div1, + Diversion *div2) const; + +private: + const StaState *sta_; +}; + +// Iterator to enumerate sucessively slower paths. +class PathEnum : public Iterator, StaState +{ +public: + PathEnum(int group_count, + int endpoint_count, + bool unique_pins, + 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(); + +private: + DISALLOW_COPY_AND_ASSIGN(PathEnum); + void makeDiversions(PathEnd *path_end, + Path *before); + void makeDiversion(PathEnd *div_end, + PathEnumed *after_div_copy); + void makeDivertedPath(Path *path, + Path *before_div, + Path *after_div, + TimingArc *div_arc, + // Returned values. + PathEnumed *&div_path, + PathEnumed *&after_div_copy); + void updatePathHeadDelays(PathEnumedSeq &path, + Path *after_div); + Arrival divSlack(Path *path, + Path *after_div, + TimingArc *div_arc, + const PathAnalysisPt *path_ap); + void reportDiversionPath(Diversion *div); + void pruneDiversionQueue(); + Edge *divEdge(Path *before_div, + TimingArc *div_arc); + void findNext(); + + bool cmp_slack_; + int group_count_; + int endpoint_count_; + bool unique_pins_; + DiversionQueue div_queue_; + int div_count_; + // Number of paths returned for each endpoint (limited to endpoint_count). + VertexPathCountMap path_counts_; + bool inserts_pruned_; + PathEnd *next_; + + friend class PathEnumFaninVisitor; +}; + +} // namespace +#endif diff --git a/search/PathEnumed.cc b/search/PathEnumed.cc new file mode 100644 index 0000000..646b5bc --- /dev/null +++ b/search/PathEnumed.cc @@ -0,0 +1,183 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Set.hh" +#include "Graph.hh" +#include "Corner.hh" +#include "Search.hh" +#include "Tag.hh" +#include "PathRef.hh" +#include "PathEnumed.hh" + +namespace sta { + +PathEnumed:: PathEnumed(VertexIndex vertex_index, + TagIndex tag_index, + Arrival arrival, + PathEnumed *prev_path, + TimingArc *prev_arc) : + Path(), + prev_path_(prev_path), + prev_arc_(prev_arc), + arrival_(arrival), + vertex_index_(vertex_index), + tag_index_(tag_index) +{ +} + +void +deletePathEnumed(PathEnumed *path) +{ + while (path) { + PathEnumed *prev = path->prevPathEnumed(); + delete path; + path = prev; + } +} + +void +PathEnumed::setRef(PathRef *ref) const +{ + ref->init(const_cast(this)); +} + +Vertex * +PathEnumed::vertex(const StaState *sta) const +{ + const Graph *graph = sta->graph(); + return graph->vertex(vertex_index_); +} + +VertexIndex +PathEnumed::vertexIndex(const StaState *) const +{ + return vertex_index_; +} + +Tag * +PathEnumed::tag(const StaState *sta) const +{ + const Search *search = sta->search(); + return search->tag(tag_index_); +} + +void +PathEnumed::setTag(Tag *tag) +{ + tag_index_ = tag->index(); +} + +const TransRiseFall * +PathEnumed::transition(const StaState *sta) const +{ + return tag(sta)->transition(); +} + +int +PathEnumed::trIndex(const StaState *sta) const +{ + return tag(sta)->trIndex(); +} + +PathAnalysisPt * +PathEnumed::pathAnalysisPt(const StaState *sta) const +{ + const Corners *corners = sta->corners(); + return corners->findPathAnalysisPt(pathAnalysisPtIndex(sta)); +} + +PathAPIndex +PathEnumed::pathAnalysisPtIndex(const StaState *sta) const +{ + return tag(sta)->pathAPIndex(); +} + +Arrival +PathEnumed::arrival(const StaState *) const +{ + return arrival_; +} + +void +PathEnumed::setArrival(Arrival arrival, + const StaState *) +{ + arrival_ = arrival; +} + +const Required & +PathEnumed::required(const StaState *) const +{ + // Required times are never needed for enumerated paths. + internalError("enumerated path required time\n"); +} + +void +PathEnumed::setRequired(const Required &, + const StaState *) +{ + // Required times are never needed for enumerated paths. + internalError("enumerated path required time\n"); +} + +Path * +PathEnumed::prevPath(const StaState *) const +{ + return prev_path_; +} + +void +PathEnumed::prevPath(const StaState *, + // Return values. + PathRef &prev_path, + TimingArc *&prev_arc) const +{ + if (prev_path_) { + prev_path_->setRef(prev_path); + prev_arc = prev_arc_; + } + else { + prev_path.init(); + prev_arc = nullptr; + } +} + +TimingArc * +PathEnumed::prevArc(const StaState *) const +{ + return prev_arc_; +} + +PathEnumed * +PathEnumed::prevPathEnumed() const +{ + return prev_path_; +} + +void +PathEnumed::setPrevPath(PathEnumed *prev) +{ + prev_path_ = prev; +} + +void +PathEnumed::setPrevArc(TimingArc *arc) +{ + prev_arc_ = arc; +} + +} // namespace diff --git a/search/PathEnumed.hh b/search/PathEnumed.hh new file mode 100644 index 0000000..9310f57 --- /dev/null +++ b/search/PathEnumed.hh @@ -0,0 +1,78 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PATH_ENUMED_H +#define STA_PATH_ENUMED_H + +#include "DisallowCopyAssign.hh" +#include "Path.hh" + +namespace sta { + +// Implements Path API for paths returned by PathEnum. +class PathEnumed : public Path +{ +public: + PathEnumed(VertexIndex vertex_index, + TagIndex tag_index, + Arrival arrival, + PathEnumed *prev_path, + TimingArc *prev_arc); + virtual void setRef(PathRef *ref) const; + virtual bool isNull() const { return vertex_index_ == 0; } + virtual Vertex *vertex(const StaState *sta) const; + virtual VertexIndex vertexIndex(const StaState *sta) const; + virtual Tag *tag(const StaState *sta) const; + virtual const TransRiseFall *transition(const StaState *sta) const; + virtual int trIndex(const StaState *) const; + virtual PathAnalysisPt *pathAnalysisPt(const StaState *sta) const; + virtual PathAPIndex pathAnalysisPtIndex(const StaState *sta) const; + virtual Arrival arrival(const StaState *sta) const; + virtual void setArrival(Arrival arrival, const StaState *sta); + virtual const Required &required(const StaState *sta) const; + virtual void setRequired(const Required &required, + const StaState *sta); + virtual Path *prevPath(const StaState *sta) const; + virtual void prevPath(const StaState *sta, + // Return values. + PathRef &prev_path, + TimingArc *&prev_arc) const; + virtual TimingArc *prevArc(const StaState *sta) const; + PathEnumed *prevPathEnumed() const; + void setPrevPath(PathEnumed *prev); + void setPrevArc(TimingArc *arc); + void setTag(Tag *tag); + + using Path::setRef; + using Path::prevPath; + +protected: + // Pointer to previous path. + // A path is traversed by following prev_path/arcs. + PathEnumed *prev_path_; + TimingArc *prev_arc_; + Arrival arrival_; + VertexIndex vertex_index_; + unsigned int tag_index_:tag_index_bits; + +private: + DISALLOW_COPY_AND_ASSIGN(PathEnumed); +}; + +void deletePathEnumed(PathEnumed *path); + +} // namespace +#endif diff --git a/search/PathExpanded.cc b/search/PathExpanded.cc new file mode 100644 index 0000000..e19a69d --- /dev/null +++ b/search/PathExpanded.cc @@ -0,0 +1,239 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "TimingRole.hh" +#include "PortDirection.hh" +#include "Network.hh" +#include "Clock.hh" +#include "Search.hh" +#include "PathRef.hh" +#include "Latches.hh" +#include "Genclks.hh" +#include "PathExpanded.hh" + +namespace sta { + +PathExpanded::PathExpanded(const StaState *sta) : + sta_(sta) +{ +} + +PathExpanded::PathExpanded(const Path *path, + // 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) : + sta_(sta) +{ + expand(path, false); +} + +void +PathExpanded::expand(const Path *path, + bool expand_genclks) +{ + const Latches *latches = sta_->latches(); + // Push the paths from the end into an array of PathRefs. + PathRef p(path); + PathRef last_path; + size_t i = 0; + bool found_start = false; + while (!p.isNull()) { + PathRef prev_path; + TimingArc *prev_arc; + p.prevPath(sta_, prev_path, prev_arc); + + if (!found_start) { + if (prev_arc) { + 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()) { + Edge *prev_edge = p.prevEdge(prev_arc, sta_); + if (latches->isLatchDtoQ(prev_edge)) { + start_index_ = i; + found_start = true; + + paths_.push_back(p); + prev_arcs_.push_back(prev_arc); + // Push latch D path. + paths_.push_back(prev_path); + prev_arcs_.push_back(nullptr); + // This breaks latch loop paths. + break; + } + } + } + } + paths_.push_back(p); + prev_arcs_.push_back(prev_arc); + last_path.init(p); + p.init(prev_path); + i++; + } + if (!found_start) + start_index_ = i - 1; + + if (expand_genclks) + expandGenclk(&last_path); +} + +void +PathExpanded::expandGenclk(PathRef *clk_path) +{ + if (!clk_path->isNull()) { + Clock *src_clk = clk_path->clock(sta_); + if (src_clk && src_clk->isGenerated()) { + PathVertex src_path; + sta_->search()->genclks()->srcPath(clk_path, src_path); + if (!src_path.isNull()) { + // The head of the genclk src path is already in paths_, + // so skip past it. + PathRef prev_path; + TimingArc *prev_arc; + src_path.prevPath(sta_, prev_path, prev_arc); + + PathRef p(prev_path); + PathRef last_path; + while (!p.isNull()) { + p.prevPath(sta_, prev_path, prev_arc); + + paths_.push_back(p); + prev_arcs_.push_back(prev_arc); + last_path.init(p); + p.init(prev_path); + } + expandGenclk(&last_path); + } + } + } +} + +// Convert external index that starts at the path root +// and increases to an index for paths_ (reversed). +size_t +PathExpanded::pathsIndex(size_t index) const +{ + return paths_.size() - index - 1; +} + +size_t +PathExpanded::startIndex() const +{ + return pathsIndex(start_index_); +} + +PathRef * +PathExpanded::path(size_t index) +{ + if (index < paths_.size()) + return &paths_[pathsIndex(index)]; + else + return nullptr; +} + +TimingArc * +PathExpanded::prevArc(size_t index) +{ + return prev_arcs_[pathsIndex(index)]; +} + +PathRef * +PathExpanded::startPath() +{ + return &paths_[start_index_]; +} + +PathRef * +PathExpanded::endPath() +{ + return &paths_[0]; +} + +TimingArc * +PathExpanded::startPrevArc() +{ + return prev_arcs_[start_index_]; +} + +PathRef * +PathExpanded::startPrevPath() +{ + size_t start1 = start_index_ + 1; + if (start1 < paths_.size()) + return &paths_[start1]; + else + return nullptr; +} + +void +PathExpanded::clkPath(PathRef &clk_path) +{ + const Latches *latches = sta_->latches(); + PathRef *start = startPath(); + TimingArc *prev_arc = startPrevArc(); + if (prev_arc) { + TimingRole *role = prev_arc->role(); + if (role == TimingRole::latchDtoQ()) { + Edge *prev_edge = start->prevEdge(prev_arc, sta_); + if (latches->isLatchDtoQ(prev_edge)) { + PathVertex enable_path; + latches->latchEnablePath(start, prev_edge, enable_path); + clk_path.init(enable_path); + } + } + else if (role == TimingRole::regClkToQ() + || role == TimingRole::latchEnToQ()) + clk_path.init(startPrevPath()); + } + else if (start->isClock(sta_)) + clk_path.init(start); +} + +void +PathExpanded::latchPaths(// Return values. + PathRef *&d_path, + PathRef *&q_path, + Edge *&d_q_edge) +{ + d_path = nullptr; + q_path = nullptr; + d_q_edge = nullptr; + PathRef *start = startPath(); + TimingArc *prev_arc = startPrevArc(); + if (prev_arc + && prev_arc->role() == TimingRole::latchDtoQ()) { + Edge *prev_edge = start->prevEdge(prev_arc, sta_); + // This breaks latch loop paths. + if (sta_->latches()->isLatchDtoQ(prev_edge)) { + d_path = startPrevPath(); + q_path = start; + d_q_edge = prev_edge; + } + } +} + +} // namespace diff --git a/search/PathExpanded.hh b/search/PathExpanded.hh new file mode 100644 index 0000000..b36f5fc --- /dev/null +++ b/search/PathExpanded.hh @@ -0,0 +1,80 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PATH_EXPANDED_H +#define STA_PATH_EXPANDED_H + +#include "DisallowCopyAssign.hh" +#include "GraphClass.hh" +#include "SearchClass.hh" +#include "TimingArc.hh" +#include "StaState.hh" + +namespace sta { + +class PathExpanded +{ +public: + PathExpanded(const StaState *sta); + // Expand path for lookup by index. + PathExpanded(const Path *path, + const StaState *sta); + PathExpanded(const Path *path, + // Expand generated clk source paths. + bool expand_genclks, + const StaState *sta); + void expand(const Path *path, + bool expand_genclks); + size_t size() const { return paths_.size(); } + // path(0) is the startpoint. + // path(size()-1) is the endpoint. + PathRef *path(size_t index); + TimingArc *prevArc(size_t index); + // Returns the path start point. + // Register/Latch Q pin + // Input pin + PathRef *startPath(); + PathRef *startPrevPath(); + PathRef *endPath(); + TimingArc *startPrevArc(); + size_t startIndex() const; + void clkPath(PathRef &clk_path); + void latchPaths(// Return values. + PathRef *&d_path, + PathRef *&q_path, + Edge *&d_q_edge); + +protected: + void expandGenclk(PathRef *clk_path); + // Convert external index that starts at the path root + // and increases to an index for paths_ (reversed). + size_t pathsIndex(size_t index) const; + + // The PathRefs in paths_ are in reverse order. + // paths_[0] is the endpoint. + // paths_[size-1] is the beginning of the path. + PathRefSeq paths_; + TimingArcSeq prev_arcs_; + // Index of the startpoint. + size_t start_index_; + const StaState *sta_; + +private: + DISALLOW_COPY_AND_ASSIGN(PathExpanded); +}; + +} // namespace +#endif diff --git a/search/PathGroup.cc b/search/PathGroup.cc new file mode 100644 index 0000000..109e465 --- /dev/null +++ b/search/PathGroup.cc @@ -0,0 +1,909 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include "Machine.hh" +#include "Stats.hh" +#include "Debug.hh" +#include "Mutex.hh" +#include "Fuzzy.hh" +#include "MinMax.hh" +#include "ExceptionPath.hh" +#include "Sdc.hh" +#include "Graph.hh" +#include "PathEnd.hh" +#include "PathAnalysisPt.hh" +#include "Tag.hh" +#include "Corner.hh" +#include "Search.hh" +#include "VisitPathEnds.hh" +#include "PathEnum.hh" +#include "ThreadForEach.hh" +#include "PathGroup.hh" + +namespace sta { + +int PathGroup::group_count_max = std::numeric_limits::max(); + +PathGroup * +PathGroup::makePathGroupSlack(const char *name, + int group_count, + int endpoint_count, + bool unique_pins, + float slack_min, + float slack_max, + const StaState *sta) +{ + return new PathGroup(name, group_count, endpoint_count, unique_pins, + slack_min, slack_max, true, MinMax::min(), sta); +} + +PathGroup * +PathGroup::makePathGroupArrival(const char *name, + int group_count, + int endpoint_count, + bool unique_pins, + const MinMax *min_max, + const StaState *sta) +{ + return new PathGroup(name, group_count, endpoint_count, unique_pins, + 0.0, 0.0, false, min_max, sta); +} + +PathGroup::PathGroup(const char *name, + int group_count, + int endpoint_count, + bool unique_pins, + float slack_min, + float slack_max, + bool cmp_slack, + const MinMax *min_max, + const StaState *sta) : + name_(name), + group_count_(group_count), + endpoint_count_(endpoint_count), + unique_pins_(unique_pins), + slack_min_(slack_min), + slack_max_(slack_max), + min_max_(min_max), + compare_slack_(cmp_slack), + threshold_(min_max->initValue()), + sta_(sta) +{ +} + +PathGroup::~PathGroup() +{ + path_ends_.deleteContents(); +} + +bool +PathGroup::savable(PathEnd *path_end) +{ + bool savable = false; + if (compare_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_) + && fuzzyLessEqual(slack, threshold_) + && fuzzyLessEqual(slack, slack_max_)) { + // Now check with crpr. + slack = path_end->slack(sta_); + savable = fuzzyLessEqual(slack, threshold_) + && fuzzyLessEqual(slack, slack_max_) + && fuzzyGreaterEqual(slack, slack_min_); + } + } + else { + const Arrival &arrival = path_end->dataArrivalTime(sta_); + savable = !delayIsInitValue(arrival, min_max_) + && fuzzyGreaterEqual(arrival, threshold_, min_max_); + } + return savable; +} + +void +PathGroup::insert(PathEnd *path_end) +{ + UniqueLock lock(lock_); + path_ends_.push_back(path_end); + if (group_count_ != group_count_max + && static_cast(path_ends_.size()) > group_count_ * 2) + prune(); +} + +void +PathGroup::prune() +{ + sort(); + VertexPathCountMap path_counts; + int end_count = 0; + for (unsigned i = 0; i < path_ends_.size(); i++) { + PathEnd *path_end = path_ends_[i]; + Vertex *vertex = path_end->vertex(sta_); + // Squish up to endpoint_count path ends per vertex up to the front of path_ends_. + if (end_count < group_count_ + && path_counts[vertex] < endpoint_count_) { + path_ends_[end_count++] = path_end; + path_counts[vertex]++; + } + else + delete path_end; + } + path_ends_.resize(end_count); + + // 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_) + threshold_ = delayAsFloat(last_end->slack(sta_)); + else + threshold_ = delayAsFloat(last_end->dataArrivalTime(sta_)); +} + +void +PathGroup::pushEnds(PathEndSeq *path_ends) +{ + ensureSortedMaxPaths(); + PathEndSeq::Iterator iter(path_ends_); + while (iter.hasNext()) { + PathEnd *path_end = iter.next(); + path_ends->push_back(path_end); + } +} + +PathGroupIterator * +PathGroup::iterator() +{ + ensureSortedMaxPaths(); + return new PathGroupIterator(path_ends_); +} + +void +PathGroup::ensureSortedMaxPaths() +{ + if (static_cast(path_ends_.size()) > group_count_) + prune(); + else + sort(); +} + +void +PathGroup::sort() +{ + sta::sort(path_ends_, PathEndLess(sta_)); +} + +void +PathGroup::clear() +{ + UniqueLock lock(lock_); + threshold_ = min_max_->initValue(); + path_ends_.clear(); +} + +//////////////////////////////////////////////////////////////// + +const char *PathGroups::path_delay_group_name_ = "**default**"; +const char *PathGroups::gated_clk_group_name_ = "**clock_gating_default**"; +const char *PathGroups::async_group_name_ = "**async_default**"; +const char *PathGroups::unconstrained_group_name_ = "(none)"; + +bool +PathGroups::isGroupPathName(const char *group_name) +{ + return stringEq(group_name, path_delay_group_name_) + || stringEq(group_name, gated_clk_group_name_) + || stringEq(group_name, async_group_name_) + || stringEq(group_name, unconstrained_group_name_); +} + +PathGroups::PathGroups(int group_count, + int endpoint_count, + bool unique_pins, + 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), + group_count_(group_count), + endpoint_count_(endpoint_count), + unique_pins_(unique_pins), + slack_min_(slack_min), + slack_max_(slack_max) +{ + makeGroups(group_count, endpoint_count, unique_pins, slack_min, slack_max, group_names, + setup, recovery, clk_gating_setup, unconstrained, + MinMax::max()); + makeGroups(group_count, endpoint_count, unique_pins, slack_min, slack_max, group_names, + hold, removal, clk_gating_hold, unconstrained, + MinMax::min()); +} + +void +PathGroups::makeGroups(int group_count, + int endpoint_count, + bool unique_pins, + float slack_min, + float slack_max, + PathGroupNameSet *group_names, + bool setup_hold, + bool async, + bool gated_clk, + bool unconstrained, + const MinMax *min_max) +{ + int mm_index = min_max->index(); + if (setup_hold) { + GroupPathIterator group_path_iter(sdc_); + while (group_path_iter.hasNext()) { + const char *name; + GroupPathSet *groups; + group_path_iter.next(name, groups); + if (reportGroup(name, group_names)) { + PathGroup *group = PathGroup::makePathGroupSlack(name, group_count, + endpoint_count, unique_pins, + slack_min, slack_max, + this); + named_map_[mm_index][name] = group; + } + } + + for (auto clk : sdc_->clks()) { + const char *clk_name = clk->name(); + if (reportGroup(clk_name, group_names)) { + PathGroup *group = PathGroup::makePathGroupSlack(clk_name, group_count, + endpoint_count, unique_pins, + slack_min, slack_max, + this); + clk_map_[mm_index][clk] = group; + } + } + } + + if (setup_hold + && reportGroup(path_delay_group_name_, group_names)) + path_delay_[mm_index] = PathGroup::makePathGroupSlack(path_delay_group_name_, + group_count, endpoint_count, + unique_pins, + slack_min, slack_max, + this); + else + path_delay_[mm_index] = nullptr; + + if (gated_clk + && reportGroup(gated_clk_group_name_, group_names)) + gated_clk_[mm_index] = PathGroup::makePathGroupSlack(gated_clk_group_name_, + group_count, endpoint_count, + unique_pins, + slack_min, slack_max, + this); + else + gated_clk_[mm_index] = nullptr; + + if (async + && reportGroup(async_group_name_, group_names)) + async_[mm_index] = PathGroup::makePathGroupSlack(async_group_name_, + group_count, endpoint_count, + unique_pins, + slack_min, slack_max, + this); + else + async_[mm_index] = nullptr; + + if (unconstrained + && reportGroup(unconstrained_group_name_, group_names)) + unconstrained_[mm_index] = + PathGroup::makePathGroupArrival(unconstrained_group_name_, + group_count, endpoint_count, unique_pins, + min_max, this); + else + unconstrained_[mm_index] = nullptr; +} + +PathGroups::~PathGroups() +{ + for (auto mm_index : MinMax::rangeIndex()) { + named_map_[mm_index].deleteContents(); + clk_map_[mm_index].deleteContents(); + delete path_delay_[mm_index]; + delete gated_clk_[mm_index]; + delete async_[mm_index]; + delete unconstrained_[mm_index]; + } +} + +PathGroup * +PathGroups::findPathGroup(const char *name, + const MinMax *min_max) const +{ + return named_map_[min_max->index()].findKey(name); +} + +PathGroup * +PathGroups::findPathGroup(const Clock *clock, + const MinMax *min_max) const +{ + return clk_map_[min_max->index()].findKey(clock); +} + +bool +PathGroups::reportGroup(const char *group_name, + PathGroupNameSet *group_names) const +{ + return group_names == nullptr + || group_names->empty() + || group_names->hasKey(group_name); +} + +PathGroup * +PathGroups::pathGroup(const PathEnd *path_end) const +{ + const MinMax *min_max = path_end->minMax(this); + int mm_index = min_max->index(); + // GroupPaths have precedence. + GroupPath *group_path = groupPathTo(path_end); + if (group_path) { + if (group_path->isDefault()) + return path_delay_[mm_index]; + else { + const char *group_name = group_path->name(); + return findPathGroup(group_name, min_max); + } + } + else if (path_end->isCheck() || path_end->isLatchCheck()) { + 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()) + return async_[mm_index]; + else + return findPathGroup(tgt_clk, min_max); + } + else if (path_end->isOutputDelay() + || path_end->isDataCheck()) + return findPathGroup(path_end->targetClk(this), min_max); + else if (path_end->isGatedClock()) + return gated_clk_[mm_index]; + else if (path_end->isPathDelay()) { + // Path delays that end at timing checks are part of the target clk group + // unless -ignore_clock_latency is true. + PathDelay *path_delay = path_end->pathDelay(); + Clock *tgt_clk = path_end->targetClk(this); + if (tgt_clk + && !path_delay->ignoreClkLatency()) + return findPathGroup(tgt_clk, min_max); + else + return path_delay_[mm_index]; + } + else if (path_end->isUnconstrained()) + return unconstrained_[mm_index]; + else { + internalError("unknown path end type"); + return nullptr; + } +} + +GroupPath * +PathGroups::groupPathTo(const PathEnd *path_end) const +{ + const Path *path = path_end->path(); + const Pin *pin = path->pin(this); + ExceptionPath *exception = + search_->exceptionTo(ExceptionPathType::group_path, path, + pin, path->transition(this), + path_end->targetClkEdge(this), + path->minMax(this), false, false); + return dynamic_cast(exception); +} + +void +PathGroups::pushGroupPathEnds(PathEndSeq *path_ends) +{ + for (auto min_max : MinMax::range()) { + int mm_index = min_max->index(); + GroupPathIterator group_path_iter(sdc_); + while (group_path_iter.hasNext()) { + const char *name; + GroupPathSet *groups; + group_path_iter.next(name, groups); + PathGroup *path_group = findPathGroup(name, min_max); + if (path_group) + path_group->pushEnds(path_ends); + } + + if (async_[mm_index]) + async_[mm_index]->pushEnds(path_ends); + + if (gated_clk_[mm_index]) + gated_clk_[mm_index]->pushEnds(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(); + PathGroup *path_group = findPathGroup(clk, min_max); + if (path_group) + path_group->pushEnds(path_ends); + } + } +} + +void +PathGroups::pushUnconstrainedPathEnds(PathEndSeq *path_ends, + 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); + } + } + } +} + +//////////////////////////////////////////////////////////////// + +typedef Map PathGroupEndMap; +typedef Map PathGroupEndsMap; +typedef Set PathEndNoCrprSet; + +static bool +exceptionToEmpty(ExceptionTo *to); + +PathEndSeq * +PathGroups::makePathEnds(ExceptionTo *to, + bool unconstrained_paths, + const Corner *corner, + const MinMaxAll *min_max, + bool sort_by_slack) +{ + Stats stats(this->debug()); + makeGroupPathEnds(to, group_count_, endpoint_count_, unique_pins_, + corner, min_max); + + PathEndSeq *path_ends = new PathEndSeq; + pushGroupPathEnds(path_ends); + if (sort_by_slack) { + sort(path_ends, PathEndLess(this)); + if (static_cast(path_ends->size()) > group_count_) + path_ends->resize(group_count_); + } + + if (unconstrained_paths + && path_ends->empty()) + // No constrained paths, so report unconstrained paths. + pushUnconstrainedPathEnds(path_ends, min_max); + + stats.report("Make path ends"); + return path_ends; +} + +//////////////////////////////////////////////////////////////// + +// Visit each path end for a vertex and add the worst one in each +// path group to the group. +class MakePathEnds1 : public PathEndVisitor +{ +public: + explicit MakePathEnds1(PathGroups *path_groups); + virtual PathEndVisitor *copy(); + virtual void visit(PathEnd *path_end); + virtual void vertexEnd(Vertex *vertex); + +private: + DISALLOW_COPY_AND_ASSIGN(MakePathEnds1); + void visitPathEnd(PathEnd *path_end, + PathGroup *group); + + PathGroups *path_groups_; + PathGroupEndMap ends_; + PathEndLess cmp_; +}; + +MakePathEnds1::MakePathEnds1(PathGroups *path_groups) : + path_groups_(path_groups), + cmp_(path_groups){ + +} + +PathEndVisitor * +MakePathEnds1::copy() +{ + return new MakePathEnds1(path_groups_); +} + +void +MakePathEnds1::visit(PathEnd *path_end) +{ + PathGroup *group = path_groups_->pathGroup(path_end); + if (group) + visitPathEnd(path_end, group); +} + +void +MakePathEnds1::visitPathEnd(PathEnd *path_end, + PathGroup *group) +{ + if (group->savable(path_end)) { + // Only keep the path end with the smallest slack/latest arrival. + PathEnd *worst_end = ends_.findKey(group); + if (worst_end) { + if (cmp_(path_end, worst_end)) { + ends_[group] = path_end->copy(); + delete worst_end; + } + } + else + ends_[group] = path_end->copy(); + } +} + +// Save the worst end for each path group. +void +MakePathEnds1::vertexEnd(Vertex *) +{ + PathGroupEndMap::Iterator group_iter(ends_); + while (group_iter.hasNext()) { + PathGroup *group; + PathEnd *end; + group_iter.next(group, end); + // visitPathEnd already confirmed slack is savable. + if (end) { + group->insert(end); + // Clear ends_ for next vertex. + ends_[group] = nullptr; + } + } +} + +//////////////////////////////////////////////////////////////// + +// Visit each path end and add it to the corresponding path group. +// After collecting the ends do parallel path enumeration to find the +// path ends for the group. +class MakePathEndsAll : public PathEndVisitor +{ +public: + explicit MakePathEndsAll(int endpoint_count, + PathGroups *path_groups); + virtual ~MakePathEndsAll(); + virtual PathEndVisitor *copy(); + virtual void visit(PathEnd *path_end); + virtual void vertexEnd(Vertex *vertex); + +private: + DISALLOW_COPY_AND_ASSIGN(MakePathEndsAll); + void visitPathEnd(PathEnd *path_end, + PathGroup *group); + + int endpoint_count_; + PathGroups *path_groups_; + const StaState *sta_; + PathGroupEndsMap ends_; + PathEndSlackLess slack_cmp_; + PathEndNoCrprLess path_no_crpr_cmp_; +}; + +MakePathEndsAll::MakePathEndsAll(int endpoint_count, + PathGroups *path_groups) : + endpoint_count_(endpoint_count), + path_groups_(path_groups), + sta_(path_groups), + slack_cmp_(path_groups), + path_no_crpr_cmp_(path_groups) +{ +} + + +PathEndVisitor * +MakePathEndsAll::copy() +{ + return new MakePathEndsAll(endpoint_count_, path_groups_); +} + +MakePathEndsAll::~MakePathEndsAll() +{ + PathGroupEndsMap::Iterator group_iter(ends_); + while (group_iter.hasNext()) { + PathGroup *group; + PathEndSeq *ends; + group_iter.next(group, ends); + delete ends; + } +} + +void +MakePathEndsAll::visit(PathEnd *path_end) +{ + PathGroup *group = path_groups_->pathGroup(path_end); + if (group) + visitPathEnd(path_end, group); +} + +void +MakePathEndsAll::visitPathEnd(PathEnd *path_end, + PathGroup *group) +{ + PathEndSeq *ends = ends_.findKey(group); + if (ends == nullptr) { + ends = new PathEndSeq; + ends_[group] = ends; + } + ends->push_back(path_end->copy()); +} + +void +MakePathEndsAll::vertexEnd(Vertex *) +{ + Debug *debug = sta_->debug(); + Network *network = sta_->network(); + PathGroupEndsMap::Iterator group_iter(ends_); + while (group_iter.hasNext()) { + PathGroup *group; + PathEndSeq *ends; + group_iter.next(group, 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_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)) { + debugPrint4(debug, "path_enum", 5, "insert %s %s %s %d\n", + path_end->vertex(sta_)->name(network), + path_end->typeName(), + path_end->transition(sta_)->asString(), + path_end->path()->tag(sta_)->index()); + // Give the group a copy of the path end because + // it may delete it during pruning. + if (group->savable(path_end)) { + group->insert(path_end->copy()); + unique_ends.insert(path_end); + n++; + } + } + else + debugPrint4(debug, "path_enum", 5, "prune %s %s %s %d\n", + path_end->vertex(sta_)->name(network), + path_end->typeName(), + path_end->transition(sta_)->asString(), + 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(); + } + } +} + +//////////////////////////////////////////////////////////////// + +void +PathGroups::makeGroupPathEnds(ExceptionTo *to, + int group_count, + int endpoint_count, + bool unique_pins, + const Corner *corner, + const MinMaxAll *min_max) +{ + if (endpoint_count == 1) { + MakePathEnds1 make_path_ends(this); + makeGroupPathEnds(to, corner, min_max, &make_path_ends); + } + else { + MakePathEndsAll make_path_ends(endpoint_count, this); + makeGroupPathEnds(to, corner, min_max, &make_path_ends); + + for (auto path_min_max : MinMax::range()) { + int mm_index = path_min_max->index(); + GroupPathIterator group_path_iter(sdc_); + while (group_path_iter.hasNext()) { + const char *name; + GroupPathSet *groups; + group_path_iter.next(name, groups); + PathGroup *group = findPathGroup(name, path_min_max); + if (group) + enumPathEnds(group, group_count, endpoint_count, unique_pins, true); + } + + for (auto clk : sdc_->clks()) { + PathGroup *group = findPathGroup(clk, path_min_max); + if (group) + enumPathEnds(group, group_count, endpoint_count, unique_pins, true); + } + + PathGroup *group = unconstrained_[mm_index]; + if (group) + enumPathEnds(group, group_count, endpoint_count, unique_pins, false); + group = path_delay_[mm_index]; + if (group) + enumPathEnds(group, group_count, endpoint_count, unique_pins, true); + group = gated_clk_[mm_index]; + if (group) + enumPathEnds(group, group_count, endpoint_count, unique_pins, true); + group = async_[mm_index]; + if (group) + enumPathEnds(group, group_count, endpoint_count, unique_pins, true); + } + } +} + +void +PathGroups::enumPathEnds(PathGroup *group, + int group_count, + int endpoint_count, + bool unique_pins, + bool cmp_slack) +{ + // Insert the worst max_path path ends in the group into a path + // enumerator. + PathEnum path_enum(group_count, endpoint_count, unique_pins, cmp_slack, this); + PathGroupIterator *end_iter = group->iterator(); + while (end_iter->hasNext()) { + PathEnd *end = end_iter->next(); + if (group->savable(end)) + path_enum.insert(end); + } + delete end_iter; + group->clear(); + + // Parallel path enumeratation to find the endpoint_count/max path ends. + for (int n = 0; path_enum.hasNext() && n < group_count; n++) { + PathEnd *end = path_enum.next(); + group->insert(end); + } +} + +void +PathGroups::makeGroupPathEnds(ExceptionTo *to, + const Corner *corner, + 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); + else { + // Only visit -to filter pins. + VertexSet endpoints; + PinSet pins; + to->allPins(network, &pins); + PinSet::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + const Pin *pin = pin_iter.next(); + Vertex *vertex, *bidirect_drvr_vertex; + graph->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex + && search->isEndpoint(vertex)) + endpoints.insert(vertex); + if (bidirect_drvr_vertex + && search->isEndpoint(bidirect_drvr_vertex)) + endpoints.insert(bidirect_drvr_vertex); + } + makeGroupPathEnds(&endpoints, corner, min_max, visitor); + } +} + +static bool +exceptionToEmpty(ExceptionTo *to) +{ + return to == nullptr + || (to->pins() == nullptr + && to->instances() == nullptr); +} + +//////////////////////////////////////////////////////////////// + +class MakeEndpointPathEnds : public VertexVisitor +{ +public: + MakeEndpointPathEnds(PathEndVisitor *path_end_visitor, + const Corner *corner, + const MinMaxAll *min_max, + const StaState *sta); + ~MakeEndpointPathEnds(); + virtual VertexVisitor *copy(); + virtual void visit(Vertex *vertex); + +private: + DISALLOW_COPY_AND_ASSIGN(MakeEndpointPathEnds); + + VisitPathEnds *visit_path_ends_; + PathEndVisitor *path_end_visitor_; + const Corner *corner_; + const MinMaxAll *min_max_; + const StaState *sta_; +}; + +MakeEndpointPathEnds::MakeEndpointPathEnds(PathEndVisitor *path_end_visitor, + const Corner *corner, + const MinMaxAll *min_max, + const StaState *sta) : + visit_path_ends_(new VisitPathEnds(sta)), + path_end_visitor_(path_end_visitor->copy()), + corner_(corner), + min_max_(min_max), + sta_(sta) +{ +} + +MakeEndpointPathEnds::~MakeEndpointPathEnds() +{ + delete visit_path_ends_; + delete path_end_visitor_; +} + +VertexVisitor * +MakeEndpointPathEnds::copy() +{ + return new MakeEndpointPathEnds(path_end_visitor_, corner_, min_max_, sta_); +} + +void +MakeEndpointPathEnds::visit(Vertex *vertex) +{ + visit_path_ends_->visitPathEnds(vertex, corner_, min_max_, true, + path_end_visitor_); +} + +//////////////////////////////////////////////////////////////// + +void +PathGroups::makeGroupPathEnds(VertexSet *endpoints, + const Corner *corner, + const MinMaxAll *min_max, + PathEndVisitor *visitor) +{ + VertexSet::Iterator end_iter(endpoints); + MakeEndpointPathEnds make_path_ends(visitor, corner, min_max, this); + forEach(&end_iter, + &make_path_ends, + thread_count_); +} + +} // namespace diff --git a/search/PathGroup.hh b/search/PathGroup.hh new file mode 100644 index 0000000..120e39c --- /dev/null +++ b/search/PathGroup.hh @@ -0,0 +1,204 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PATHGROUP_H +#define STA_PATHGROUP_H + +#include "DisallowCopyAssign.hh" +#include "Map.hh" +#include "Vector.hh" +#include "StaState.hh" +#include "SearchClass.hh" + +namespace sta { + +class MinMax; +class PathEndVisitor; + +typedef PathEndSeq::Iterator PathGroupIterator; +typedef Map PathGroupClkMap; +typedef Map PathGroupNamedMap; + +// A collection of PathEnds grouped and sorted for reporting. +class PathGroup +{ +public: + virtual ~PathGroup(); + // Path group that compares compare slacks. + static PathGroup *makePathGroupArrival(const char *name, + int group_count, + int endpoint_count, + bool unique_pins, + 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_count, + int endpoint_count, + bool unique_pins, + float min_slack, + float max_slack, + const StaState *sta); + const char *name() const { return name_; } + const MinMax *minMax() const { return min_max_;} + const PathEndSeq &pathEnds() const { return path_ends_; } + void insert(PathEnd *path_end); + // Push group_count into path_ends. + void pushEnds(PathEndSeq *path_ends); + // Predicates to determine if a PathEnd is worth saving. + virtual bool savable(PathEnd *path_end); + int maxPaths() const { return group_count_; } + PathGroupIterator *iterator(); + // This does NOT delete the path ends. + void clear(); + static int group_count_max; + +protected: + PathGroup(const char *name, + int group_count, + int endpoint_count, + bool unique_pins, + float min_slack, + float max_slack, + bool cmp_slack, + const MinMax *min_max, + const StaState *sta); + void ensureSortedMaxPaths(); + void prune(); + void sort(); + + const char *name_; + int group_count_; + int endpoint_count_; + bool unique_pins_; + float slack_min_; + float slack_max_; + PathEndSeq path_ends_; + const MinMax *min_max_; + bool compare_slack_; + float threshold_; + std::mutex lock_; + const StaState *sta_; + +private: + DISALLOW_COPY_AND_ASSIGN(PathGroup); +}; + +class PathGroups : public StaState +{ +public: + PathGroups(int group_count, + int endpoint_count, + bool unique_pins, + 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. + // Returned PathEndSeq is owned by the caller. + // 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; + PathGroup *findPathGroup(const Clock *clock, + const MinMax *min_max) const; + PathGroup *pathGroup(const PathEnd *path_end) const; + static bool isGroupPathName(const char *group_name); + static const char *asyncPathGroupName() { return async_group_name_; } + +protected: + void makeGroupPathEnds(ExceptionTo *to, + int group_count, + int endpoint_count, + bool unique_pins, + const Corner *corner, + 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); + void enumPathEnds(PathGroup *group, + int group_count, + int endpoint_count, + bool unique_pins, + bool cmp_slack); + + void pushGroupPathEnds(PathEndSeq *path_ends); + void pushUnconstrainedPathEnds(PathEndSeq *path_ends, + const MinMaxAll *min_max); + + void makeGroups(int group_count, + int endpoint_count, + bool unique_pins, + 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; + GroupPath *groupPathTo(const PathEnd *path_end) const; + + int group_count_; + int endpoint_count_; + bool unique_pins_; + float slack_min_; + float slack_max_; + + // Paths grouped by SDC group_path command. + // name -> PathGroup + PathGroupNamedMap named_map_[MinMax::index_count]; + // clock -> PathGroup + PathGroupClkMap clk_map_[MinMax::index_count]; + // Min/max path delays. + PathGroup *path_delay_[MinMax::index_count]; + // Gated clock checks. + PathGroup *gated_clk_[MinMax::index_count]; + // Asynchronous (recovery/removal) checks. + PathGroup *async_[MinMax::index_count]; + // 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_; + +private: + DISALLOW_COPY_AND_ASSIGN(PathGroups); +}; + +} // namespace +#endif diff --git a/search/PathRef.cc b/search/PathRef.cc new file mode 100644 index 0000000..cc1c7d0 --- /dev/null +++ b/search/PathRef.cc @@ -0,0 +1,253 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Graph.hh" +#include "TagGroup.hh" +#include "PathEnumed.hh" +#include "PathVertex.hh" +#include "Search.hh" +#include "PathRef.hh" + +namespace sta { + +PathRef::PathRef() : + path_enumed_(nullptr) +{ +} + +PathRef::PathRef(const Path *path) : + path_enumed_(nullptr) +{ + if (path) + path->setRef(this); +} + +PathRef::PathRef(const PathRef &path) : + path_vertex_(path.path_vertex_), + path_enumed_(path.path_enumed_) +{ +} + +PathRef::PathRef(const PathRef *path) : + path_vertex_(path->path_vertex_), + path_enumed_(path->path_enumed_) +{ +} + +PathRef::PathRef(const PathVertex &path) : + path_vertex_(&path), + path_enumed_(nullptr) +{ +} + +void +PathRef::init() +{ + path_vertex_.init(); + path_enumed_ = nullptr; +} + +void +PathRef::init(const PathRef &path) +{ + path_vertex_ = path.path_vertex_; + path_enumed_ = path.path_enumed_; +} + +void +PathRef::init(const PathRef *path) +{ + path_vertex_ = path->path_vertex_; + path_enumed_ = path->path_enumed_; +} + +void +PathRef::init(const PathVertex *path) +{ + path_vertex_.copy(path); +} + +void +PathRef::init(const PathVertex &path) +{ + path_vertex_.copy(path); +} + +void +PathRef::init(Vertex *vertex, + Tag *tag, + int arrival_index) +{ + path_vertex_.init(vertex, tag, arrival_index); + path_enumed_ = nullptr; +} + +void +PathRef::init(PathEnumed *path) +{ + path_enumed_ = path; +} + +void +PathRef::setRef(PathRef *ref) const +{ + ref->path_vertex_.copy(path_vertex_); + ref->path_enumed_ = path_enumed_; +} + +void +PathRef::deleteRep() +{ + if (path_enumed_) + deletePathEnumed(path_enumed_); +} + +bool +PathRef::isNull() const +{ + return path_enumed_ == nullptr + && path_vertex_.isNull(); +} + +Vertex * +PathRef::vertex(const StaState *sta) const +{ + if (path_enumed_) + return path_enumed_->vertex(sta); + else + return path_vertex_.vertex(sta); +} + +VertexIndex +PathRef::vertexIndex(const StaState *sta) const +{ + if (path_enumed_) + return path_enumed_->vertexIndex(sta); + else + return path_vertex_.vertexIndex(sta); +} + +Tag * +PathRef::tag(const StaState *sta) const +{ + if (path_enumed_) + return path_enumed_->tag(sta); + else + return path_vertex_.tag(sta); +} + +TagIndex +PathRef::tagIndex(const StaState *sta) const +{ + if (path_enumed_) + return path_enumed_->tagIndex(sta); + else + return path_vertex_.tagIndex(sta); +} + +const TransRiseFall * +PathRef::transition(const StaState *sta) const +{ + if (path_enumed_) + return path_enumed_->transition(sta); + else + return path_vertex_.transition(sta); +} + +int +PathRef::trIndex(const StaState *sta) const +{ + if (path_enumed_) + return path_enumed_->trIndex(sta); + else + return path_vertex_.trIndex(sta); +} + +PathAnalysisPt * +PathRef::pathAnalysisPt(const StaState *sta) const +{ + if (path_enumed_) + return path_enumed_->pathAnalysisPt(sta); + else + return path_vertex_.pathAnalysisPt(sta); +} + +PathAPIndex +PathRef::pathAnalysisPtIndex(const StaState *sta) const +{ + if (path_enumed_) + return path_enumed_->pathAnalysisPtIndex(sta); + else + return path_vertex_.pathAnalysisPtIndex(sta); +} + +Arrival +PathRef::arrival(const StaState *sta) const +{ + if (path_enumed_) + return path_enumed_->arrival(sta); + else + return path_vertex_.arrival(sta); +} + +void +PathRef::setArrival(Arrival arrival, + const StaState *sta) +{ + if (path_enumed_) + return path_enumed_->setArrival(arrival, sta); + else + return path_vertex_.setArrival(arrival, sta); +} + +const Required & +PathRef::required(const StaState *sta) const +{ + if (path_enumed_) + return path_enumed_->required(sta); + else + return path_vertex_.required(sta); +} + +void +PathRef::setRequired(const Required &required, const StaState *sta) +{ + if (path_enumed_) + return path_enumed_->setRequired(required, sta); + else + return path_vertex_.setRequired(required, sta); +} + +void +PathRef::prevPath(const StaState *sta, + // Return values. + PathRef &prev_path, TimingArc *&prev_arc) const +{ + if (path_enumed_) + path_enumed_->prevPath(sta, prev_path, prev_arc); + else + path_vertex_.prevPath(sta, prev_path, prev_arc); +} + +void +PathRef::arrivalIndex(int &arrival_index, + bool &arrival_exists) const +{ + return path_vertex_.arrivalIndex(arrival_index, arrival_exists); +} + +} // namespace diff --git a/search/PathRef.hh b/search/PathRef.hh new file mode 100644 index 0000000..74c80a1 --- /dev/null +++ b/search/PathRef.hh @@ -0,0 +1,87 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PATH_REF_H +#define STA_PATH_REF_H + +#include "DisallowCopyAssign.hh" +#include "Vector.hh" +#include "SearchClass.hh" +#include "Path.hh" +#include "PathVertex.hh" + +namespace sta { + +// Path reference to either a PathVertex or PathEnum. +// This "could" be made smaller by using a union for +// path_vertex_.vertex_ and path_enumed_ and a non-legal +// value for path_vertex_.arrival_index_ (because a nullptr tag +// in PathVertex is valid). +class PathRef : public Path +{ +public: + PathRef(); + PathRef(const Path *path); + PathRef(const PathRef &path); + PathRef(const PathRef *path); + PathRef(const PathVertex &path); + void init(); + void init(const PathRef &path); + void init(const PathRef *path); + void init(const PathVertex &path); + void init(const PathVertex *path); + void init(Vertex *vertex, + Tag *tag, + int arrival_index); + void init(PathEnumed *path); + virtual void setRef(PathRef *ref) const; + virtual bool isNull() const; + virtual Vertex *vertex(const StaState *sta) const; + virtual VertexIndex vertexIndex(const StaState *sta) const; + virtual Tag *tag(const StaState *sta) const; + virtual TagIndex tagIndex(const StaState *sta) const; + virtual const TransRiseFall *transition(const StaState *sta) const; + virtual int trIndex(const StaState *sta) const; + virtual PathAnalysisPt *pathAnalysisPt(const StaState *sta) const; + virtual PathAPIndex pathAnalysisPtIndex(const StaState *sta) const; + void arrivalIndex(int &arrival_index, + bool &arrival_exists) const; + virtual Arrival arrival(const StaState *sta) const; + virtual void setArrival(Arrival arrival, + const StaState *sta); + virtual const Required &required(const StaState *sta) const; + virtual void setRequired(const Required &required, + const StaState *sta); + virtual void prevPath(const StaState *sta, + // Return values. + PathRef &prev_path, + TimingArc *&prev_arc) const; + void deleteRep(); + + using Path::setRef; + using Path::prevPath; + +protected: + PathVertex path_vertex_; + PathEnumed *path_enumed_; + +private: + friend class PathVertex; + friend class PathEnumed; +}; + +} // namespace +#endif diff --git a/search/PathVertex.cc b/search/PathVertex.cc new file mode 100644 index 0000000..32b3bbe --- /dev/null +++ b/search/PathVertex.cc @@ -0,0 +1,606 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "Fuzzy.hh" +#include "Graph.hh" +#include "ExceptionPath.hh" +#include "Sdc.hh" +#include "Corner.hh" +#include "GraphDelayCalc.hh" +#include "Tag.hh" +#include "TagGroup.hh" +#include "PathAnalysisPt.hh" +#include "PathRef.hh" +#include "PathVertexRep.hh" +#include "Search.hh" +#include "PathVertex.hh" + +namespace sta { + +PathVertex::PathVertex() : + vertex_(nullptr), + tag_(nullptr), + arrival_index_(0) +{ +} + +PathVertex::PathVertex(const PathVertex &path) : + vertex_(path.vertex_), + tag_(path.tag_), + arrival_index_(path.arrival_index_) +{ +} + +PathVertex::PathVertex(const PathVertex *path) : + vertex_(nullptr), + tag_(nullptr), + arrival_index_(0) +{ + if (path) { + vertex_ = path->vertex_; + tag_ = path->tag_; + arrival_index_ = path->arrival_index_; + } +} + +PathVertex::PathVertex(Vertex *vertex, + Tag *tag, + const StaState *sta) +{ + init(vertex, tag, sta); +} + +PathVertex::PathVertex(Vertex *vertex, + Tag *tag, + int arrival_index) : + vertex_(vertex), + tag_(tag), + arrival_index_(arrival_index) +{ +} + +PathVertex::PathVertex(const PathVertexRep *path, + const StaState *sta) +{ + if (path) + init(path->vertex(sta), path->tag(sta), sta); +} + +PathVertex::PathVertex(const PathVertexRep &path, + const StaState *sta) +{ + if (path.isNull()) + init(); + else + init(path.vertex(sta), path.tag(sta), sta); +} + +void +PathVertex::init() +{ + vertex_ = nullptr; + tag_ = nullptr; + arrival_index_ = 0; +} + +void +PathVertex::init(Vertex *vertex, + Tag *tag, + const StaState *sta) +{ + vertex_ = nullptr; + tag_ = nullptr; + arrival_index_ = 0; + const Search *search = sta->search(); + TagGroup *tag_group = search->tagGroup(vertex); + if (tag_group) { + bool arrival_exists; + tag_group->arrivalIndex(tag, arrival_index_, arrival_exists); + if (arrival_exists) { + vertex_ = vertex; + tag_ = tag; + } + } +} + +void +PathVertex::init(Vertex *vertex, + Tag *tag, + int arrival_index) +{ + vertex_ = vertex; + tag_ = tag; + arrival_index_ = arrival_index; +} + +void +PathVertex::init(const PathVertexRep *path, + const StaState *sta) +{ + if (path) + init(path->vertex(sta), path->tag(sta), sta); + else + init(); +} + +void +PathVertex::init(const PathVertexRep &path, + const StaState *sta) +{ + if (!path.isNull()) + init(path.vertex(sta), path.tag(sta), sta); + else + init(); +} + +void +PathVertex::copy(const PathVertex &path) +{ + vertex_ = path.vertex_; + tag_ = path.tag_; + arrival_index_ = path.arrival_index_; +} + +void +PathVertex::copy(const PathVertex *path) +{ + if (path) { + vertex_ = path->vertex_; + tag_ = path->tag_; + arrival_index_ = path->arrival_index_; + } + else { + vertex_ = nullptr; + tag_ = nullptr; + arrival_index_ = 0; + } +} + +bool +PathVertex::isNull() const +{ + return tag_ == nullptr; +} + +void +PathVertex::setRef(PathRef *ref) const +{ + ref->init(vertex_, tag_, arrival_index_); +} + +VertexIndex +PathVertex::vertexIndex(const StaState *sta) const +{ + const Graph *graph = sta->graph(); + return graph->index(vertex_); +} + +TagIndex +PathVertex::tagIndex(const StaState *) const +{ + return tag_->index(); +} + +const TransRiseFall * +PathVertex::transition(const StaState *) const +{ + return tag_->transition(); +} + +int +PathVertex::trIndex(const StaState *) const +{ + return tag_->trIndex(); +} + +PathAnalysisPt * +PathVertex::pathAnalysisPt(const StaState *sta) const +{ + return tag_->pathAnalysisPt(sta); +} + +PathAPIndex +PathVertex::pathAnalysisPtIndex(const StaState *) const +{ + return tag_->pathAPIndex(); +} + +void +PathVertex::arrivalIndex(int &arrival_index, + bool &arrival_exists) const +{ + if (tag_) { + arrival_index = arrival_index_; + arrival_exists = true; + } + else + arrival_exists = false; +} + +void +PathVertex::setArrivalIndex(int arrival_index) +{ + arrival_index_ = arrival_index; +} + +Arrival +PathVertex::arrival(const StaState *) const +{ + Arrival *arrivals = vertex_->arrivals(); + return arrivals[arrival_index_]; +} + +void +PathVertex::setArrival(Arrival arrival, + const StaState *) +{ + if (tag_) { + Arrival *arrivals = vertex_->arrivals(); + arrivals[arrival_index_] = arrival; + } +} + +const Required & +PathVertex::required(const StaState *sta) const +{ + if (tag_ && vertex_->hasRequireds()) { + const Search *search = sta->search(); + TagGroup *tag_group = search->tagGroup(vertex_); + int req_index = tag_group->requiredIndex(arrival_index_); + Arrival *arrivals = vertex_->arrivals(); + return arrivals[req_index]; + } + else + return delayInitValue(minMax(sta)->opposite()); +} + +void +PathVertex::setRequired(const Required &required, + const StaState *sta) +{ + const Search *search = sta->search(); + TagGroup *tag_group = search->tagGroup(vertex_); + Arrival *arrivals = vertex_->arrivals(); + int arrival_count = tag_group->arrivalCount(); + if (!vertex_->hasRequireds()) { + Arrival *new_arrivals = new Arrival[arrival_count * 2]; + memcpy(new_arrivals, arrivals, arrival_count * sizeof(Arrival)); + vertex_->setArrivals(new_arrivals); + vertex_->setHasRequireds(true); + delete [] arrivals; + arrivals = new_arrivals; + } + int req_index = arrival_index_ + arrival_count; + arrivals[req_index] = required; +} + +void +PathVertex::deleteRequireds(Vertex *vertex, + const StaState *sta) +{ + if (vertex->hasRequireds()) { + const Search *search = sta->search(); + TagGroup *tag_group = search->tagGroup(vertex); + Arrival *arrivals = vertex->arrivals(); + int arrival_count = tag_group->arrivalCount(); + Arrival *new_arrivals = new Arrival[arrival_count]; + memcpy(new_arrivals, arrivals, arrival_count * sizeof(Arrival)); + vertex->setArrivals(new_arrivals); + vertex->setHasRequireds(false); + delete [] arrivals; + } +} + +bool +PathVertex::equal(const PathVertex *path1, + const PathVertex *path2) +{ + return path1->vertex_ == path2->vertex_ + && path1->tag_ == path2->tag_; +} + +//////////////////////////////////////////////////////////////// + +// EvalPred but search to clk source pin. +class PrevPred2 : public SearchPred0 +{ +public: + explicit PrevPred2(const StaState *sta); + virtual bool searchThru(Edge *edge); + +private: + DISALLOW_COPY_AND_ASSIGN(PrevPred2); +}; + +PrevPred2::PrevPred2(const StaState *sta) : + SearchPred0(const_cast(sta)) +{ +} + +bool +PrevPred2::searchThru(Edge *edge) +{ + const Sdc *sdc = sta_->sdc(); + TimingRole *role = edge->role(); + return SearchPred0::searchThru(edge) + && (sdc->dynamicLoopBreaking() + || !edge->isDisabledLoop()) + && !role->isTimingCheck(); +} + +class PrevPathVisitor : public PathVisitor +{ +public: + PrevPathVisitor(const Path *path, + SearchPred *pred, + const StaState *sta); + virtual VertexVisitor *copy(); + virtual void visit(Vertex *) {} + virtual bool visitFromToPath(const Pin *from_pin, + Vertex *from_vertex, + const TransRiseFall *from_tr, + Tag *from_tag, + PathVertex *from_path, + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex *to_vertex, + const TransRiseFall *to_tr, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max, + const PathAnalysisPt *path_ap); + PathVertex &prevPath() { return prev_path_; } + TimingArc *prevArc() const { return prev_arc_; } + +protected: + Tag *unfilteredTag(const Tag *tag) const; + + const Path *path_; + Arrival path_arrival_; + Tag *path_tag_; + int path_tr_index_; + PathAPIndex path_ap_index_; + PathVertex prev_path_; + TimingArc *prev_arc_; + float dcalc_tol_; + +private: + DISALLOW_COPY_AND_ASSIGN(PrevPathVisitor); +}; + +PrevPathVisitor::PrevPathVisitor(const Path *path, + SearchPred *pred, + const StaState *sta) : + PathVisitor(pred, sta), + path_(path), + path_arrival_(path->arrival(sta)), + path_tag_(path->tag(sta)), + path_tr_index_(path->trIndex(sta)), + path_ap_index_(path->pathAnalysisPtIndex(sta)), + prev_path_(), + prev_arc_(nullptr), + dcalc_tol_(sta->graphDelayCalc()->incrementalDelayTolerance()) +{ +} + +VertexVisitor * +PrevPathVisitor::copy() +{ + return new PrevPathVisitor(path_, pred_, sta_); +} + +bool +PrevPathVisitor::visitFromToPath(const Pin *, + Vertex *, + const TransRiseFall *, + Tag *from_tag, + PathVertex *from_path, + Edge *, + TimingArc *arc, + ArcDelay, + Vertex *, + const TransRiseFall *to_tr, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *, + const PathAnalysisPt *path_ap) +{ + PathAPIndex path_ap_index = path_ap->index(); + if (to_tr->index() == path_tr_index_ + && path_ap_index == path_ap_index_ + && (dcalc_tol_ > 0.0 + ? std::abs(delayAsFloat(to_arrival - path_arrival_)) < dcalc_tol_ + : fuzzyEqual(to_arrival, path_arrival_)) + && (tagMatch(to_tag, path_tag_, sta_) + // If the filter exception became active searching from + // from_path to to_path the tag includes the filter, but + // to_vertex still has paths from previous searches that do + // not have the filter. + || (!from_tag->isFilter() + && to_tag->isFilter() + && tagMatch(unfilteredTag(to_tag), path_tag_, sta_)))) { + int arrival_index; + bool arrival_exists; + from_path->arrivalIndex(arrival_index, arrival_exists); + if (arrival_exists) { + prev_path_.copy(from_path); + prev_arc_ = arc; + // Stop looking for the previous path/arc. + return false; + } + } + return true; +} + +Tag * +PrevPathVisitor::unfilteredTag(const Tag *tag) const +{ + Search *search = sta_->search(); + const Corners *corners = sta_->corners(); + ExceptionStateSet *unfiltered_states = nullptr; + const ExceptionStateSet *states = tag->states(); + ExceptionStateSet::ConstIterator state_iter(states); + while (state_iter.hasNext()) { + ExceptionState *state = state_iter.next(); + ExceptionPath *except = state->exception(); + if (!except->isFilter()) { + if (unfiltered_states == nullptr) + unfiltered_states = new ExceptionStateSet; + unfiltered_states->insert(state); + } + } + return search->findTag(tag->transition(), + corners->findPathAnalysisPt(tag->pathAPIndex()), + tag->clkInfo(), + tag->isClock(), + tag->inputDelay(), + tag->isSegmentStart(), + unfiltered_states, true); +} + +//////////////////////////////////////////////////////////////// + +void +PathVertex::prevPath(const StaState *sta, + // Return values. + PathVertex &prev_path, + TimingArc *&prev_arc) const +{ + PrevPred2 pred(sta); + PrevPathVisitor visitor(this, &pred, sta); + visitor.visitFaninPaths(vertex(sta)); + prev_path.copy(visitor.prevPath()); + prev_arc = visitor.prevArc(); +} + +void +PathVertex::prevPath(const StaState *sta, + // Return values. + PathVertex &prev_path) const +{ + PrevPred2 pred(sta); + PrevPathVisitor visitor(this, &pred, sta); + visitor.visitFaninPaths(vertex(sta)); + prev_path.copy(visitor.prevPath()); +} + +void +PathVertex::prevPath(const StaState *sta, + // Return values. + PathRef &prev_path, + TimingArc *&prev_arc) const +{ + PathVertex prev; + prevPath(sta, prev, prev_arc); + prev.setRef(prev_path); +} + +//////////////////////////////////////////////////////////////// + +VertexPathIterator::VertexPathIterator(Vertex *vertex, + const StaState *sta) : + search_(sta->search()), + vertex_(vertex), + tr_(nullptr), + path_ap_(nullptr), + min_max_(nullptr) +{ + TagGroup *tag_group = search_->tagGroup(vertex); + if (tag_group) { + arrival_iter_.init(tag_group->arrivalMap()); + findNext(); + } +} + +// Iterate over vertex paths with the same transition and +// analysis pt but different but different tags. +VertexPathIterator::VertexPathIterator(Vertex *vertex, + const TransRiseFall *tr, + const PathAnalysisPt *path_ap, + const StaState *sta) : + search_(sta->search()), + vertex_(vertex), + tr_(tr), + path_ap_(path_ap), + min_max_(nullptr) +{ + TagGroup *tag_group = search_->tagGroup(vertex); + if (tag_group) { + arrival_iter_.init(tag_group->arrivalMap()); + findNext(); + } +} + +VertexPathIterator::VertexPathIterator(Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max, + const StaState *sta) : + search_(sta->search()), + vertex_(vertex), + tr_(tr), + path_ap_(nullptr), + min_max_(min_max) +{ + TagGroup *tag_group = search_->tagGroup(vertex); + if (tag_group) { + arrival_iter_.init(tag_group->arrivalMap()); + findNext(); + } +} + +VertexPathIterator::~VertexPathIterator() +{ +} + +bool +VertexPathIterator::hasNext() +{ + return !next_.isNull(); +} + +void +VertexPathIterator::findNext() +{ + while (arrival_iter_.hasNext()) { + Tag *tag; + int arrival_index; + arrival_iter_.next(tag, arrival_index); + if ((tr_ == nullptr + || tag->trIndex() == tr_->index()) + && (path_ap_ == nullptr + || tag->pathAPIndex() == path_ap_->index()) + && (min_max_ == nullptr + || tag->pathAnalysisPt(search_)->pathMinMax() == min_max_)) { + next_.init(vertex_, tag, arrival_index); + return; + } + } + next_.init(); +} + +PathVertex * +VertexPathIterator::next() +{ + path_.copy(next_); + findNext(); + return &path_; +} + +} // namespace diff --git a/search/PathVertex.hh b/search/PathVertex.hh new file mode 100644 index 0000000..80b120e --- /dev/null +++ b/search/PathVertex.hh @@ -0,0 +1,147 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PATH_VERTEX_H +#define STA_PATH_VERTEX_H + +#include "DisallowCopyAssign.hh" +#include "SearchClass.hh" +#include "Path.hh" + +namespace sta { + +class PathVertexRep; + +// Implements Path API for a vertex. +class PathVertex : public Path +{ +public: + PathVertex(); + PathVertex(const PathVertex &path); + PathVertex(const PathVertex *path); + PathVertex(const PathVertexRep *path, + const StaState *sta); + PathVertex(const PathVertexRep &path, + const StaState *sta); + // If tag is not in the vertex tag group isNull() is true. + PathVertex(Vertex *vertex, + Tag *tag, + const StaState *sta); + PathVertex(Vertex *vertex, + Tag *tag, + int arrival_index); + void init(); + void init(const PathVertexRep *path, + const StaState *sta); + void init(const PathVertexRep &path, + const StaState *sta); + void init(Vertex *vertex, + Tag *tag, + const StaState *sta); + void init(Vertex *vertex, + Tag *tag, + int arrival_index); + void copy(const PathVertex &path); + void copy(const PathVertex *path); + virtual bool isNull() const; + virtual void setRef(PathRef *ref) const; + virtual Vertex *vertex(const StaState *) const { return vertex_; } + virtual VertexIndex vertexIndex(const StaState *sta) const; + virtual Tag *tag(const StaState *) const { return tag_; } + virtual TagIndex tagIndex(const StaState *sta) const; + virtual const TransRiseFall *transition(const StaState *) const; + virtual int trIndex(const StaState *sta) const; + virtual PathAnalysisPt *pathAnalysisPt(const StaState *sta) const; + virtual PathAPIndex pathAnalysisPtIndex(const StaState *sta) const; + void arrivalIndex(int &arrival_index, + bool &arrival_exists) const; + void setArrivalIndex(int arrival_index); + virtual Arrival arrival(const StaState *sta) const; + virtual void setArrival(Arrival arrival, + const StaState *sta); + virtual const Required &required(const StaState *sta) const; + virtual void setRequired(const Required &required, + const StaState *sta); + virtual void prevPath(const StaState *sta, + // Return values. + PathRef &prev_path, + TimingArc *&prev_arc) const; + void prevPath(const StaState *sta, + // Return values. + PathVertex &prev_path) const; + void prevPath(const StaState *sta, + // Return values. + PathVertex &prev_path, + TimingArc *&prev_arc) const; + static void deleteRequireds(Vertex *vertex, + const StaState *sta); + static bool equal(const PathVertex *path1, + const PathVertex *path2); + + using Path::setRef; + using Path::prevPath; + +protected: + Vertex *vertex_; + Tag *tag_; + int arrival_index_; + +private: + friend class PathRef; + friend class VertexPathIterator; + friend bool pathVertexEqual(const PathVertex *path1, + const PathVertex *path2); +}; + +// Iterator for vertex paths. +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. + VertexPathIterator(Vertex *vertex, + const TransRiseFall *tr, + const PathAnalysisPt *path_ap, + const StaState *sta); + // Iterate over vertex paths with the same transition and + // analysis pt min/max but different tags. + VertexPathIterator(Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max, + const StaState *sta); + virtual ~VertexPathIterator(); + virtual bool hasNext(); + virtual PathVertex *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(VertexPathIterator); + void findNext(); + + const Search *search_; + Vertex *vertex_; + const TransRiseFall *tr_; + const PathAnalysisPt *path_ap_; + const MinMax *min_max_; + ArrivalMap::Iterator arrival_iter_; + PathVertex path_; + PathVertex next_; +}; + +} // namespace +#endif diff --git a/search/PathVertexRep.cc b/search/PathVertexRep.cc new file mode 100644 index 0000000..46ab1d1 --- /dev/null +++ b/search/PathVertexRep.cc @@ -0,0 +1,230 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Graph.hh" +#include "SearchClass.hh" +#include "Tag.hh" +#include "TagGroup.hh" +#include "Search.hh" +#include "PathVertex.hh" +#include "PathVertexRep.hh" + +namespace sta { + +PathVertexRep::PathVertexRep() +{ + init(); +} + +PathVertexRep::PathVertexRep(const PathVertexRep *path) +{ + init(path); +} + +PathVertexRep::PathVertexRep(const PathVertexRep &path) +{ + init(path); +} + +PathVertexRep::PathVertexRep(const PathVertex *path, + const StaState *sta) +{ + init(path, sta); +} + +PathVertexRep::PathVertexRep(const PathVertex &path, + const StaState *sta) +{ + init(path, sta); +} + +PathVertexRep::PathVertexRep(VertexIndex vertex_index, + TagIndex tag_index, + bool is_enum) : + vertex_index_(vertex_index), + tag_index_(tag_index), + is_enum_(is_enum) +{ +} + +void +PathVertexRep::init() +{ + vertex_index_ = 0; + tag_index_ = tag_index_null; + is_enum_ = false; +} + +void +PathVertexRep::init(const PathVertexRep *path) +{ + if (path) { + vertex_index_ = path->vertex_index_; + tag_index_ = path->tag_index_; + is_enum_ = false; + } + else + init(); +} + +void +PathVertexRep::init(const PathVertexRep &path) +{ + vertex_index_ = path.vertex_index_; + tag_index_ = path.tag_index_; + is_enum_ = false; +} + +void +PathVertexRep::init(const PathVertex *path, + const StaState *sta) +{ + if (path == nullptr || path->isNull()) + init(); + else { + vertex_index_ = sta->graph()->index(path->vertex(sta)); + tag_index_ = path->tag(sta)->index(); + is_enum_ = false; + } +} + +void +PathVertexRep::init(const PathVertex &path, + const StaState *sta) +{ + if (path.isNull()) + init(); + else { + vertex_index_ = sta->graph()->index(path.vertex(sta)); + tag_index_ = path.tag(sta)->index(); + is_enum_ = false; + } +} + +Vertex * +PathVertexRep::vertex(const StaState *sta) const +{ + const Graph *graph = sta->graph(); + return graph->vertex(vertex_index_); +} + +Tag * +PathVertexRep::tag(const StaState *sta) const +{ + const Search *search = sta->search(); + return search->tag(tag_index_); +} + +Arrival +PathVertexRep::arrival(const StaState *sta) const +{ + const Graph *graph = sta->graph(); + const Search *search = sta->search(); + Tag *tag = search->tag(tag_index_); + Vertex *vertex = graph->vertex(vertex_index_); + TagGroup *tag_group = search->tagGroup(vertex); + int arrival_index; + bool arrival_exists; + tag_group->arrivalIndex(tag, arrival_index, arrival_exists); + if (!arrival_exists) + internalError("tag group missing tag"); + Arrival *arrivals = vertex->arrivals(); + return arrivals[arrival_index]; +} + +void +PathVertexRep::prevPath(const StaState *sta, + // Return values. + PathRef &prev_path, + TimingArc *&prev_arc) const +{ + PathVertex path_vertex(this, sta); + path_vertex.prevPath(sta, prev_path, prev_arc); +} + +//////////////////////////////////////////////////////////////// + +bool +PathVertexRep::equal(const PathVertexRep *path1, + const PathVertexRep *path2) +{ + return path1->vertex_index_ == path2->vertex_index_ + && path1->tag_index_ == path2->tag_index_; +} + +bool +PathVertexRep::equal(const PathVertexRep &path1, + const PathVertexRep &path2) +{ + return path1.vertex_index_ == path2.vertex_index_ + && path1.tag_index_ == path2.tag_index_; +} + +int +PathVertexRep::cmp(const PathVertexRep *path1, + const PathVertexRep *path2) +{ + if (path1 && path2) { + VertexIndex vertex_index1 = path1->vertexIndex(); + VertexIndex vertex_index2 = path2->vertexIndex(); + if (vertex_index1 == vertex_index2) { + TagIndex tag_index1 = path1->tagIndex(); + TagIndex tag_index2 = path2->tagIndex(); + if (tag_index1 == tag_index2) + return 0; + else if (tag_index1 < tag_index2) + return -1; + else + return 1; + } + else if (vertex_index1 < vertex_index2) + return -1; + else + return 1; + } + else if (path1 == nullptr + && path2 == nullptr) + return 0; + else if (path1 == nullptr) + return -1; + else + return 1; +} + +int +PathVertexRep::cmp(const PathVertexRep &path1, + const PathVertexRep &path2) +{ + VertexIndex vertex_index1 = path1.vertexIndex(); + VertexIndex vertex_index2 = path2.vertexIndex(); + if (vertex_index1 == vertex_index2) { + TagIndex tag_index1 = path1.tagIndex(); + TagIndex tag_index2 = path2.tagIndex(); + if (tag_index1 == tag_index2) + return 0; + else if (tag_index1 < tag_index2) + return -1; + else + return 1; + } + else if (vertex_index1 < vertex_index2) + return -1; + else + return 1; +} + +} // namespace diff --git a/search/PathVertexRep.hh b/search/PathVertexRep.hh new file mode 100644 index 0000000..6a6fdca --- /dev/null +++ b/search/PathVertexRep.hh @@ -0,0 +1,77 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PATH_VERTEX_REP_H +#define STA_PATH_VERTEX_REP_H + +namespace sta { + +class StaState; +class PathVertex; + +// Path representation that references a vertex arrival via a tag. +// This does not implement the Path API which uses virtual functions +// that would make it larger. +class PathVertexRep +{ +public: + explicit PathVertexRep(); + explicit PathVertexRep(const PathVertexRep *path); + PathVertexRep(const PathVertexRep &path); + explicit PathVertexRep(const PathVertex *path, + const StaState *sta); + explicit PathVertexRep(const PathVertex &path, + const StaState *sta); + explicit PathVertexRep(VertexIndex vertex_index, + TagIndex tag_index, + bool is_enum); + void init(); + void init(const PathVertexRep *path); + void init(const PathVertexRep &path); + void init(const PathVertex *path, + const StaState *sta); + void init(const PathVertex &path, + const StaState *sta); + bool isNull() const { return vertex_index_ == 0; } + Vertex *vertex(const StaState *) const; + VertexIndex vertexIndex() const { return vertex_index_; } + Tag *tag(const StaState *sta) const; + TagIndex tagIndex() const { return tag_index_; } + Arrival arrival(const StaState *sta) const; + void prevPath(const StaState *sta, + // Return values. + PathRef &prev_path, + TimingArc *&prev_arc) const; + + static bool equal(const PathVertexRep *path1, + const PathVertexRep *path2); + static bool equal(const PathVertexRep &path1, + const PathVertexRep &path2); + static int cmp(const PathVertexRep *path1, + const PathVertexRep *path2); + static int cmp(const PathVertexRep &path1, + const PathVertexRep &path2); + +protected: + VertexIndex vertex_index_; + unsigned int tag_index_:tag_index_bits; + bool is_enum_:1; + +private: +}; + +} // namespace +#endif diff --git a/search/Power.cc b/search/Power.cc new file mode 100644 index 0000000..74d63a7 --- /dev/null +++ b/search/Power.cc @@ -0,0 +1,998 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include // max +#include "Machine.hh" +#include "Debug.hh" +#include "EnumNameMap.hh" +#include "Units.hh" +#include "Transition.hh" +#include "MinMax.hh" +#include "Clock.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 "Network.hh" +#include "Graph.hh" +#include "Corner.hh" +#include "Sdc.hh" +#include "DcalcAnalysisPt.hh" +#include "GraphDelayCalc.hh" +#include "PathVertex.hh" +#include "Levelize.hh" +#include "Sim.hh" +#include "Search.hh" +#include "Bfs.hh" +#include "Power.hh" + +// Related liberty not supported: +// library +// default_cell_leakage_power : 0; +// output_voltage (default_VDD_VSS_output) { +// leakage_power +// related_pg_pin : VDD; +// internal_power +// input_voltage : default_VDD_VSS_input; +// pin +// output_voltage : default_VDD_VSS_output; + +namespace sta { + +static int +funcExprPortCount(FuncExpr *expr); + +Power::Power(Sta *sta) : + StaState(sta), + global_activity_{0.0, 0.0, PwrActivityOrigin::unknown}, + input_activity_{0.1, 0.5, PwrActivityOrigin::input}, + activities_valid_(false) +{ +} + +void +Power::setGlobalActivity(float activity, + float duty) +{ + global_activity_.set(activity, duty, PwrActivityOrigin::global); + activities_valid_ = false; +} + +void +Power::setInputActivity(float activity, + float duty) +{ + input_activity_.set(activity, duty, PwrActivityOrigin::input); + activities_valid_ = false; +} + +void +Power::setInputPortActivity(const Port *input_port, + float activity, + float duty) +{ + Instance *top_inst = network_->topInstance(); + const Pin *pin = network_->findPin(top_inst, input_port); + if (pin) { + activity_map_[pin] = {activity, duty, PwrActivityOrigin::user}; + activities_valid_ = false; + } +} + +PwrActivity & +Power::pinActivity(const Pin *pin) +{ + return activity_map_[pin]; +} + +bool +Power::hasPinActivity(const Pin *pin) +{ + return activity_map_.hasKey(pin); +} + +void +Power::setPinActivity(const Pin *pin, + PwrActivity &activity) +{ + activity_map_[pin] = activity; + activities_valid_ = false; +} + +void +Power::setPinActivity(const Pin *pin, + float activity, + float duty, + PwrActivityOrigin origin) +{ + activity_map_[pin] = {activity, duty, origin}; + activities_valid_ = false; +} + +void +Power::power(const Corner *corner, + // Return values. + PowerResult &total, + PowerResult &sequential, + PowerResult &combinational, + PowerResult ¯o, + PowerResult &pad) +{ + total.clear(); + sequential.clear(); + combinational.clear(); + macro.clear(); + pad.clear(); + + preamble(); + LeafInstanceIterator *inst_iter = network_->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + LibertyCell *cell = network_->libertyCell(inst); + if (cell) { + PowerResult inst_power; + power(inst, corner, inst_power); + if (cell->isMacro()) + macro.incr(inst_power); + else if (cell->isPad()) + pad.incr(inst_power); + else if (cell->hasSequentials()) + sequential.incr(inst_power); + else + combinational.incr(inst_power); + total.incr(inst_power); + } + } + delete inst_iter; +} + +void +Power::power(const Instance *inst, + const Corner *corner, + // Return values. + PowerResult &result) +{ + LibertyCell *cell = network_->libertyCell(inst); + if (cell) { + preamble(); + power(inst, cell, corner, result); + } +} + +//////////////////////////////////////////////////////////////// + +class ActivitySrchPred : public SearchPred +{ +public: + explicit ActivitySrchPred(const StaState *sta); + virtual bool searchFrom(const Vertex *from_vertex); + virtual bool searchThru(Edge *edge); + virtual bool searchTo(const Vertex *); + +protected: + const StaState *sta_; +}; + +ActivitySrchPred::ActivitySrchPred(const StaState *sta) : + sta_(sta) +{ +} + +bool +ActivitySrchPred::searchFrom(const Vertex *) +{ + return true; +} + +bool +ActivitySrchPred::searchThru(Edge *edge) +{ + auto role = edge->role(); + return !(edge->isDisabledLoop() + || role->isTimingCheck() + || role == TimingRole::regClkToQ()); +} + +bool +ActivitySrchPred::searchTo(const Vertex *) +{ + return true; +} + +//////////////////////////////////////////////////////////////// + +class PropActivityVisitor : public VertexVisitor, StaState +{ +public: + PropActivityVisitor(Power *power, + BfsFwdIterator *bfs); + ~PropActivityVisitor(); + virtual VertexVisitor *copy(); + virtual void visit(Vertex *vertex); + void init(); + bool foundRegWithoutActivity() const; + InstanceSet *stealVisitedRegs(); + +private: + InstanceSet *visited_regs_; + bool found_reg_without_activity_; + Power *power_; + BfsFwdIterator *bfs_; +}; + +PropActivityVisitor::PropActivityVisitor(Power *power, + BfsFwdIterator *bfs) : + StaState(power), + visited_regs_(nullptr), + power_(power), + bfs_(bfs) +{ +} + +PropActivityVisitor::~PropActivityVisitor() +{ + delete visited_regs_; +} + +VertexVisitor * +PropActivityVisitor::copy() +{ + return new PropActivityVisitor(power_, bfs_); +} + +void +PropActivityVisitor::init() +{ + visited_regs_ = new InstanceSet; + found_reg_without_activity_ = false; +} + +InstanceSet * +PropActivityVisitor::stealVisitedRegs() +{ + InstanceSet *visited_regs = visited_regs_; + visited_regs_ = nullptr; + return visited_regs; +} + +bool +PropActivityVisitor::foundRegWithoutActivity() const +{ + return found_reg_without_activity_; +} + +void +PropActivityVisitor::visit(Vertex *vertex) +{ + auto pin = vertex->pin(); + debugPrint1(debug_, "power_activity", 3, "visit %s\n", + vertex->name(network_)); + bool input_without_activity = false; + 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_); + const Pin *from_pin = from_vertex->pin(); + PwrActivity &from_activity = power_->pinActivity(from_pin); + PwrActivity to_activity(from_activity.activity(), + from_activity.duty(), + PwrActivityOrigin::propagated); + if (!power_->hasPinActivity(pin)) + input_without_activity = true; + power_->setPinActivity(pin, to_activity); + } + } + Instance *inst = network_->instance(pin); + auto cell = network_->libertyCell(inst); + if (cell && cell->hasSequentials()) { + debugPrint1(debug_, "power_activity", 3, "pending reg %s\n", + network_->pathName(inst)); + visited_regs_->insert(inst); + found_reg_without_activity_ = input_without_activity; + } + } + if (network_->isDriver(pin)) { + LibertyPort *port = network_->libertyPort(pin); + if (port) { + FuncExpr *func = port->function(); + Instance *inst = network_->instance(pin); + PwrActivity activity = power_->evalActivity(func, inst); + power_->setPinActivity(pin, activity); + debugPrint3(debug_, "power_activity", 3, "set %s %.2e %.2f\n", + vertex->name(network_), + activity.activity(), + activity.duty()); + } + } + bfs_->enqueueAdjacentVertices(vertex); +} + +PwrActivity +Power::evalActivity(FuncExpr *expr, + const Instance *inst) +{ + switch (expr->op()) { + case FuncExpr::op_port: { + Pin *pin = network_->findPin(inst, expr->port()->name()); + if (pin) + return findActivity(pin); + else + return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant); + } + case FuncExpr::op_not: { + PwrActivity activity1 = evalActivity(expr->left(), inst); + return PwrActivity(activity1.activity(), + 1.0 - activity1.duty(), + PwrActivityOrigin::propagated); + } + case FuncExpr::op_or: { + PwrActivity activity1 = evalActivity(expr->left(), inst); + PwrActivity activity2 = evalActivity(expr->right(), inst); + float p1 = 1.0 - activity1.duty(); + float p2 = 1.0 - activity2.duty(); + return PwrActivity(activity1.activity() * p2 + + activity2.activity() * p1, + 1.0 - p1 * p2, + PwrActivityOrigin::propagated); + } + case FuncExpr::op_and: { + PwrActivity activity1 = evalActivity(expr->left(), inst); + PwrActivity activity2 = evalActivity(expr->right(), inst); + float p1 = activity1.duty(); + float p2 = activity2.duty(); + return PwrActivity(activity1.activity() * p2 + activity2.activity() * p1, + p1 * p2, + PwrActivityOrigin::propagated); + } + case FuncExpr::op_xor: { + PwrActivity activity1 = evalActivity(expr->left(), inst); + PwrActivity activity2 = evalActivity(expr->right(), inst); + float p1 = activity1.duty() * (1.0 - activity2.duty()); + float p2 = activity2.duty() * (1.0 - activity1.duty()); + return PwrActivity(activity1.activity() * p1 + activity2.activity() * p2, + p1 + p2, + PwrActivityOrigin::propagated); + } + case FuncExpr::op_one: + return PwrActivity(0.0, 1.0, PwrActivityOrigin::constant); + case FuncExpr::op_zero: + return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant); + } + return PwrActivity(); +} + +//////////////////////////////////////////////////////////////// + +void +Power::preamble() +{ + ensureActivities(); +} + +void +Power::ensureActivities() +{ + if (!global_activity_.isSet()) { + if (!activities_valid_) { + ActivitySrchPred activity_srch_pred(this); + BfsFwdIterator bfs(BfsIndex::other, &activity_srch_pred, this); + seedActivities(bfs); + PropActivityVisitor visitor(this, &bfs); + visitor.init(); + bfs.visit(levelize_->maxLevel(), &visitor); + while (visitor.foundRegWithoutActivity()) { + InstanceSet *regs = visitor.stealVisitedRegs(); + InstanceSet::Iterator reg_iter(regs); + while (reg_iter.hasNext()) { + auto reg = reg_iter.next(); + // Propagate activiities across register D->Q. + seedRegOutputActivities(reg, bfs); + } + delete regs; + visitor.init(); + bfs.visit(levelize_->maxLevel(), &visitor); + } + activities_valid_ = true; + } + } +} + +void +Power::seedActivities(BfsFwdIterator &bfs) +{ + for (auto vertex : levelize_->roots()) { + const Pin *pin = vertex->pin(); + // Clock activities are baked in. + if (!sdc_->isVertexPinClock(pin) + && network_->direction(pin) != PortDirection::internal()) { + debugPrint1(debug_, "power_activity", 3, "seed %s\n", + vertex->name(network_)); + PwrActivity &activity = pinActivity(pin); + PwrActivityOrigin origin = activity.origin(); + // Default inputs without explicit activities to the input default. + if (origin != PwrActivityOrigin::user) + setPinActivity(pin, input_activity_); + Vertex *vertex = graph_->pinDrvrVertex(pin); + bfs.enqueueAdjacentVertices(vertex); + } + } +} + +void +Power::seedRegOutputActivities(const Instance *inst, + BfsFwdIterator &bfs) +{ + auto cell = network_->libertyCell(inst); + LibertyCellSequentialIterator seq_iter(cell); + while (seq_iter.hasNext()) { + Sequential *seq = seq_iter.next(); + 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); + while (pin_iter->hasNext()) { + auto pin = pin_iter->next(); + auto port = network_->libertyPort(pin); + auto func = port->function(); + if (func) { + Vertex *vertex = graph_->pinDrvrVertex(pin); + if (func->port() == seq->output() + || func->port() == seq->outputInv()) { + debugPrint1(debug_, "power_activity", 3, "enqueue reg output %s\n", + vertex->name(network_)); + bfs.enqueue(vertex); + } + } + } + delete pin_iter; + } +} + +void +Power::seedRegOutputActivities(const Instance *reg, + Sequential *seq, + LibertyPort *output, + bool invert) +{ + const Pin *pin = network_->findPin(reg, output); + if (pin) { + PwrActivity activity = evalActivity(seq->data(), reg); + if (invert) + activity.set(activity.activity(), + 1.0 - activity.duty(), + activity.origin()); + setPinActivity(pin, activity); + } +} + +//////////////////////////////////////////////////////////////// + +void +Power::power(const Instance *inst, + LibertyCell *cell, + const Corner *corner, + // Return values. + PowerResult &result) +{ + MinMax *mm = MinMax::max(); + const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm); + const Clock *inst_clk = findInstClk(inst); + 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); + float load_cap = to_port->direction()->isAnyOutput() + ? graph_delay_calc_->loadCap(to_pin, dcalc_ap) + : 0.0; + PwrActivity activity = findClkedActivity(to_pin, inst_clk); + if (to_port->direction()->isAnyOutput()) + findSwitchingPower(cell, to_port, activity, load_cap, + dcalc_ap, result); + findInternalPower(to_pin, to_port, inst, cell, activity, + load_cap, dcalc_ap, result); + } + delete pin_iter; + findLeakagePower(inst, cell, result); +} + +const Clock * +Power::findInstClk(const Instance *inst) +{ + const Clock *inst_clk = nullptr; + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + const Clock *clk = findClk(pin); + if (clk) + inst_clk = clk; + } + delete pin_iter; + return inst_clk; +} + +void +Power::findInternalPower(const Pin *to_pin, + const LibertyPort *to_port, + const Instance *inst, + LibertyCell *cell, + PwrActivity &to_activity, + float load_cap, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + PowerResult &result) +{ + debugPrint3(debug_, "power", 2, "internal %s/%s (%s)\n", + network_->pathName(inst), + to_port->name(), + cell->name()); + const Pvt *pvt = dcalc_ap->operatingConditions(); + debugPrint1(debug_, "power", 2, " cap = %s\n", + units_->capacitanceUnit()->asString(load_cap)); + debugPrint0(debug_, "power", 2, " when act/ns duty energy power\n"); + float internal = 0.0; + LibertyCellInternalPowerIterator pwr_iter(cell); + while (pwr_iter.hasNext()) { + InternalPower *pwr = pwr_iter.next(); + if (pwr->port() == to_port) { + const char *related_pg_pin = pwr->relatedPgPin(); + const LibertyPort *from_port = pwr->relatedPort(); + FuncExpr *when = pwr->when(); + FuncExpr *infered_when = nullptr; + if (from_port) { + if (when == nullptr) { + FuncExpr *func = to_port->function(); + if (func) + infered_when = inferedWhen(func, from_port); + } + } + else + from_port = to_port; + // If all the "when" clauses exist VSS internal power is ignored. + const Pin *from_pin = network_->findPin(inst, from_port); + if (from_pin + && ((when && internalPowerMissingWhen(cell, to_port, related_pg_pin)) + || pgNameVoltage(cell, related_pg_pin, dcalc_ap) != 0.0)) { + Vertex *from_vertex = graph_->pinLoadVertex(from_pin); + float duty; + if (infered_when) { + PwrActivity from_activity = findActivity(from_pin); + PwrActivity to_activity = findActivity(to_pin); + float duty1 = evalActivity(infered_when, inst).duty(); + if (to_activity.activity() == 0.0) + duty = 0.0; + else + duty = from_activity.activity() / to_activity.activity() * duty1; + } + else if (when) + duty = evalActivity(when, inst).duty(); + else if (search_->isClock(from_vertex)) + duty = 1.0; + else + duty = 0.5; + float port_energy = 0.0; + for (auto to_tr : TransRiseFall::range()) { + // Should use unateness to find from_tr. + TransRiseFall *from_tr = to_tr; + float slew = delayAsFloat(graph_->slew(from_vertex, + from_tr, + dcalc_ap->index())); + float table_energy = pwr->power(to_tr, pvt, slew, load_cap); + float tr_energy = table_energy * duty; + debugPrint4(debug_, "power", 3, " %s energy = %9.2e * %.2f = %9.2e\n", + to_tr->shortName(), + table_energy, + duty, + tr_energy); + port_energy += tr_energy; + } + float port_internal = port_energy * to_activity.activity(); + debugPrint8(debug_, "power", 2, " %s -> %s %s %.2f %.2f %9.2e %9.2e %s\n", + from_port->name(), + to_port->name(), + when ? when->asString() : (infered_when ? infered_when->asString() : " "), + to_activity.activity() * 1e-9, + duty, + port_energy, + port_internal, + related_pg_pin ? related_pg_pin : "no pg_pin"); + internal += port_internal; + } + if (infered_when) + infered_when->deleteSubexprs(); + } + } + result.setInternal(result.internal() + internal); +} + +//////////////////////////////////////////////////////////////// + +static bool +isPortRef(FuncExpr *expr, + const LibertyPort *port) +{ + return (expr->op() == FuncExpr::op_port + && expr->port() == port) + || (expr->op() == FuncExpr::op_not + && expr->left()->op() == FuncExpr::op_port + && expr->left()->port() == port); +} + +static FuncExpr * +negate(FuncExpr *expr) +{ + if (expr->op() == FuncExpr::op_not) + return expr->left()->copy(); + else + return FuncExpr::makeNot(expr->copy()); +} + +FuncExpr * +Power::inferedWhen(FuncExpr *expr, + const LibertyPort *from_port) +{ + switch (expr->op()) { + case FuncExpr::op_port: { + if (expr->port() == from_port) + return FuncExpr::makeOne(); + else + return nullptr; + } + case FuncExpr::op_not: + return inferedWhen(expr->left(), from_port); + case FuncExpr::op_or: + case FuncExpr::op_xor: { + if (isPortRef(expr->left(), from_port)) + return negate(expr->right()); + if (isPortRef(expr->right(), from_port)) + return negate(expr->left()); + break; + } + case FuncExpr::op_and: { + if (isPortRef(expr->left(), from_port)) + return expr->right()->copy(); + if (isPortRef(expr->right(), from_port)) + return expr->left()->copy(); + break; + } + case FuncExpr::op_one: + case FuncExpr::op_zero: + break; + } + return nullptr; +} + +//////////////////////////////////////////////////////////////// + +// Return true if some a "when" clause for the internal power to_port +// is missing. +bool +Power::internalPowerMissingWhen(LibertyCell *cell, + const LibertyPort *to_port, + const char *related_pg_pin) +{ + int when_input_count = 0; + int when_count = 0; + LibertyCellInternalPowerIterator pwr_iter(cell); + while (pwr_iter.hasNext()) { + auto pwr = pwr_iter.next(); + auto when = pwr->when(); + if (pwr->port() == to_port + && pwr->relatedPort() == nullptr + && stringEqIf(pwr->relatedPgPin(), related_pg_pin) + && when) { + when_count++; + when_input_count = funcExprPortCount(when); + } + } + return when_count != (1 << when_input_count); +} + +static int +funcExprPortCount(FuncExpr *expr) +{ + int port_count = 0; + FuncExprPortIterator port_iter(expr); + while (port_iter.hasNext()) { + port_iter.next(); + port_count++; + } + return port_count; +} + +void +Power::findLeakagePower(const Instance *, + LibertyCell *cell, + // Return values. + PowerResult &result) +{ + float cond_leakage = 0.0; + bool found_cond = false; + float default_leakage = 0.0; + bool found_default = false; + LibertyCellLeakagePowerIterator pwr_iter(cell); + while (pwr_iter.hasNext()) { + LeakagePower *leak = pwr_iter.next(); + FuncExpr *when = leak->when(); + if (when) { + FuncExprPortIterator port_iter(when); + float duty = 2.0; + while (port_iter.hasNext()) { + auto port = port_iter.next(); + if (port->direction()->isAnyInput()) + duty *= .5; + } + debugPrint4(debug_, "power", 2, "leakage %s %s %.3e * %.2f\n", + cell->name(), + when->asString(), + leak->power(), + duty); + cond_leakage += leak->power() * duty; + found_cond = true; + } + else { + debugPrint2(debug_, "power", 2, "leakage default %s %.3e\n", + cell->name(), + leak->power()); + default_leakage += leak->power(); + found_default = true; + } + } + float leakage = 0.0; + float leak; + bool exists; + cell->leakagePower(leak, exists); + if (exists) { + // Prefer cell_leakage_power until propagated activities exist. + debugPrint2(debug_, "power", 2, "leakage cell %s %.3e\n", + cell->name(), + leak); + leakage = leak; + } + // Ignore default leakages unless there are no conditional leakage groups. + else if (found_cond) + leakage = cond_leakage; + else if (found_default) + leakage = default_leakage; + result.setLeakage(result.leakage() + leakage); +} + +void +Power::findSwitchingPower(LibertyCell *cell, + const LibertyPort *to_port, + PwrActivity &activity, + float load_cap, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + PowerResult &result) +{ + float volt = portVoltage(cell, to_port, dcalc_ap); + float switching = .5 * load_cap * volt * volt * activity.activity(); + debugPrint5(debug_, "power", 2, "switching %s/%s activity = %.2e volt = %.2f %.3e\n", + cell->name(), + to_port->name(), + activity.activity(), + volt, + switching); + result.setSwitching(result.switching() + switching); +} + +PwrActivity +Power::findClkedActivity(const Pin *pin) +{ + const Instance *inst = network_->instance(pin); + const Clock *inst_clk = findInstClk(inst); + return findClkedActivity(pin, inst_clk); +} + +PwrActivity +Power::findClkedActivity(const Pin *pin, + const Clock *inst_clk) +{ + const Clock *clk = findClk(pin); + if (clk == nullptr) + clk = inst_clk; + if (clk) { + float period = clk->period(); + if (period > 0.0) { + PwrActivity activity = findActivity(pin); + return PwrActivity(activity.activity() / period, + activity.duty(), + activity.origin()); + } + } + // gotta find a clock someplace... + return PwrActivity(input_activity_.activity(), + input_activity_.duty(), + PwrActivityOrigin::defaulted); +} + +PwrActivity +Power::findActivity(const Pin *pin) +{ + Vertex *vertex = graph_->pinLoadVertex(pin); + if (search_->isClock(vertex)) + return PwrActivity(2.0, 0.5, 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; + } + } + return input_activity_; +} + +float +Power::portVoltage(LibertyCell *cell, + const LibertyPort *port, + const DcalcAnalysisPt *dcalc_ap) +{ + return pgNameVoltage(cell, port->relatedPowerPin(), dcalc_ap); +} + +float +Power::pgNameVoltage(LibertyCell *cell, + const char *pg_port_name, + const DcalcAnalysisPt *dcalc_ap) +{ + if (pg_port_name) { + auto pg_port = cell->findPgPort(pg_port_name); + if (pg_port) { + auto volt_name = pg_port->voltageName(); + auto library = cell->libertyLibrary(); + float voltage; + bool exists; + library->supplyVoltage(volt_name, voltage, exists); + if (exists) + return voltage; + } + } + + const Pvt *pvt = dcalc_ap->operatingConditions(); + if (pvt == nullptr) + pvt = cell->libertyLibrary()->defaultOperatingConditions(); + if (pvt) + return pvt->voltage(); + else + return 0.0; +} + +const Clock * +Power::findClk(const Pin *to_pin) +{ + const Clock *clk = nullptr; + Vertex *to_vertex = graph_->pinDrvrVertex(to_pin); + VertexPathIterator path_iter(to_vertex, this); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + const Clock *path_clk = path->clock(this); + if (path_clk + && (clk == nullptr + || path_clk->period() < clk->period())) + clk = path_clk; + } + return clk; +} + +//////////////////////////////////////////////////////////////// + +PowerResult::PowerResult() : + internal_(0.0), + switching_(0.0), + leakage_(0.0) +{ +} + +void +PowerResult::clear() +{ + internal_ = 0.0; + switching_ = 0.0; + leakage_ = 0.0; +} + +float +PowerResult::total() const +{ + return internal_ + switching_ + leakage_; +} + +void +PowerResult::setInternal(float internal) +{ + internal_ = internal; +} + +void +PowerResult::setLeakage(float leakage) +{ + leakage_ = leakage; +} + +void +PowerResult::setSwitching(float switching) +{ + switching_ = switching; +} + +void +PowerResult::incr(PowerResult &result) +{ + internal_ += result.internal_; + switching_ += result.switching_; + leakage_ += result.leakage_; +} + +//////////////////////////////////////////////////////////////// + +static EnumNameMap pwr_activity_origin_map = + {{PwrActivityOrigin::global, "global"}, + {PwrActivityOrigin::input, "input"}, + {PwrActivityOrigin::user, "user"}, + {PwrActivityOrigin::propagated, "propagated"}, + {PwrActivityOrigin::clock, "clock"}, + {PwrActivityOrigin::constant, "constant"}, + {PwrActivityOrigin::defaulted, "defaulted"}, + {PwrActivityOrigin::unknown, "unknown"}}; + +PwrActivity::PwrActivity(float activity, + float duty, + PwrActivityOrigin origin) : + activity_(activity), + duty_(duty), + origin_(origin) +{ +} + +PwrActivity::PwrActivity() : + activity_(0.0), + duty_(0.0), + origin_(PwrActivityOrigin::unknown) +{ +} + +void +PwrActivity::set(float activity, + float duty, + PwrActivityOrigin origin) +{ + activity_ = activity; + duty_ = duty; + origin_ = origin; +} + +bool +PwrActivity::isSet() const +{ + return origin_ != PwrActivityOrigin::unknown; +} + +const char * +PwrActivity::originName() const +{ + return pwr_activity_origin_map.find(origin_); +} + +} // namespace diff --git a/search/Power.hh b/search/Power.hh new file mode 100644 index 0000000..bbb47b9 --- /dev/null +++ b/search/Power.hh @@ -0,0 +1,186 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_POWER_H +#define STA_POWER_H + +#include "Sta.hh" + +namespace sta { + +class PowerResult; +class PwrActivity; +class PropActivityVisitor; +class BfsFwdIterator; + +typedef UnorderedMap PwrActivityMap; + +enum class PwrActivityOrigin +{ + global, + input, + user, + propagated, + clock, + constant, + defaulted, + unknown +}; + +class PwrActivity +{ +public: + PwrActivity(); + PwrActivity(float activity, + float duty, + PwrActivityOrigin origin); + float activity() const { return activity_; } + float duty() const { return duty_; } + PwrActivityOrigin origin() { return origin_; } + const char *originName() const; + void set(float activity, + float duty, + PwrActivityOrigin origin); + bool isSet() const; + +private: + // In general activity is per clock cycle, NOT per second. + float activity_; + float duty_; + PwrActivityOrigin origin_; +}; + +// The Power class has access to Sta components directly for +// convenience but also requires access to the Sta class member functions. +class Power : public StaState +{ +public: + Power(Sta *sta); + void power(const Corner *corner, + // Return values. + PowerResult &total, + PowerResult &sequential, + PowerResult &combinational, + PowerResult ¯o, + PowerResult &pad); + void power(const Instance *inst, + const Corner *corner, + // Return values. + PowerResult &result); + void setGlobalActivity(float activity, + float duty); + void setInputActivity(float activity, + float duty); + void setInputPortActivity(const Port *input_port, + float activity, + float duty); + PwrActivity &pinActivity(const Pin *pin); + bool hasPinActivity(const Pin *pin); + void setPinActivity(const Pin *pin, + PwrActivity &activity); + void setPinActivity(const Pin *pin, + float activity, + float duty, + PwrActivityOrigin origin); + // Activity is toggles per second. + PwrActivity findClkedActivity(const Pin *pin); + +protected: + void preamble(); + void ensureActivities(); + + void power(const Instance *inst, + LibertyCell *cell, + const Corner *corner, + // Return values. + PowerResult &result); + void findInternalPower(const Pin *to_pin, + const LibertyPort *to_port, + const Instance *inst, + LibertyCell *cell, + PwrActivity &to_activity, + float load_cap, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + PowerResult &result); + void findLeakagePower(const Instance *inst, + LibertyCell *cell, + // Return values. + PowerResult &result); + void findSwitchingPower(LibertyCell *cell, + const LibertyPort *to_port, + PwrActivity &activity, + float load_cap, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + PowerResult &result); + const Clock *findInstClk(const Instance *inst); + const Clock *findClk(const Pin *to_pin); + PwrActivity findClkedActivity(const Pin *pin, + const Clock *inst_clk); + PwrActivity findActivity(const Pin *pin); + float portVoltage(LibertyCell *cell, + const LibertyPort *port, + const DcalcAnalysisPt *dcalc_ap); + float pgNameVoltage(LibertyCell *cell, + const char *pg_port_name, + const DcalcAnalysisPt *dcalc_ap); + void seedActivities(BfsFwdIterator &bfs); + void seedRegOutputActivities(const Instance *reg, + Sequential *seq, + LibertyPort *output, + bool invert); + void seedRegOutputActivities(const Instance *inst, + BfsFwdIterator &bfs); + PwrActivity evalActivity(FuncExpr *expr, + const Instance *inst); + bool internalPowerMissingWhen(LibertyCell *cell, + const LibertyPort *to_port, + const char *related_pg_pin); + FuncExpr *inferedWhen(FuncExpr *expr, + const LibertyPort *from_port); + +private: + PwrActivity global_activity_; + PwrActivity input_activity_; + PwrActivityMap activity_map_; + bool activities_valid_; + + friend class PropActivityVisitor; +}; + +class PowerResult +{ +public: + PowerResult(); + void clear(); + float internal() const { return internal_; } + void setInternal(float internal); + float switching() const { return switching_; } + void setSwitching(float switching); + float leakage() const { return leakage_; } + void setLeakage(float leakage); + float total() const; + void incr(PowerResult &result); + +private: + float internal_; + float switching_; + float leakage_; +}; + +} // namespace +#endif diff --git a/search/Property.cc b/search/Property.cc new file mode 100644 index 0000000..00c53c6 --- /dev/null +++ b/search/Property.cc @@ -0,0 +1,979 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "StringUtil.hh" +#include "MinMax.hh" +#include "Transition.hh" +#include "PortDirection.hh" +#include "Units.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Clock.hh" +#include "Corner.hh" +#include "PathEnd.hh" +#include "PathExpanded.hh" +#include "PathRef.hh" +#include "Power.hh" +#include "Sta.hh" +#include "Property.hh" + +namespace sta { + +using std::string; + +static PropertyValue +pinSlewProperty(const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max, + Sta *sta); +static PropertyValue +pinSlackProperty(const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max, + Sta *sta); +static PropertyValue +portSlewProperty(const Port *port, + const TransRiseFall *tr, + const MinMax *min_max, + Sta *sta); +static PropertyValue +portSlackProperty(const Port *port, + const TransRiseFall *tr, + const MinMax *min_max, + Sta *sta); +static PropertyValue +edgeDelayProperty(Edge *edge, + const TransRiseFall *tr, + const MinMax *min_max, + Sta *sta); +static float +delayPropertyValue(Delay delay, + Sta *sta); + +//////////////////////////////////////////////////////////////// + +class PropertyUnknown : public StaException +{ +public: + PropertyUnknown(const char *type, + const char *property); + virtual ~PropertyUnknown() THROW_DCL {} + virtual const char *what() const throw(); + +private: + const char *type_; + const char *property_; +}; + +PropertyUnknown::PropertyUnknown(const char *type, + const char *property) : + type_(type), + property_(property) +{ +} + +const char * +PropertyUnknown::what() const throw() +{ + return stringPrint("Error: %s objects do not have a %s property.", + type_, property_); +} + +//////////////////////////////////////////////////////////////// + +PropertyValue::PropertyValue() : + type_(type_none) +{ +} + +PropertyValue::PropertyValue(const char *value) : + type_(type_string), + string_(stringCopy(value)) +{ +} + +PropertyValue::PropertyValue(std::string &value) : + type_(type_string), + string_(stringCopy(value.c_str())) +{ +} + +PropertyValue::PropertyValue(float value) : + type_(type_float), + float_(value) +{ +} + +PropertyValue::PropertyValue(bool value) : + type_(type_bool), + bool_(value) +{ +} + +PropertyValue::PropertyValue(LibertyLibrary *value) : + type_(type_liberty_library), + liberty_library_(value) +{ +} + +PropertyValue::PropertyValue(LibertyCell *value) : + type_(type_liberty_cell), + liberty_cell_(value) +{ +} + +PropertyValue::PropertyValue(LibertyPort *value) : + type_(type_liberty_port), + liberty_port_(value) +{ +} + +PropertyValue::PropertyValue(Library *value) : + type_(type_library), + library_(value) +{ +} + +PropertyValue::PropertyValue(Cell *value) : + type_(type_cell), + cell_(value) +{ +} + +PropertyValue::PropertyValue(Port *value) : + type_(type_port), + port_(value) +{ +} + +PropertyValue::PropertyValue(Instance *value) : + type_(type_instance), + inst_(value) +{ +} + +PropertyValue::PropertyValue(Pin *value) : + type_(type_pin), + pin_(value) +{ +} + +PropertyValue::PropertyValue(PinSeq *value) : + type_(type_pins), + pins_(value) +{ +} + +PropertyValue::PropertyValue(PinSet *value) : + type_(type_pins), + pins_(new PinSeq) +{ + PinSet::Iterator pin_iter(value); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + pins_->push_back( pin); + } +} + +PropertyValue::PropertyValue(Net *value) : + type_(type_net), + net_(value) +{ +} + +PropertyValue::PropertyValue(Clock *value) : + type_(type_clk), + clk_(value) +{ +} + +PropertyValue::PropertyValue(ClockSeq *value) : + type_(type_clks), + clks_(new ClockSeq(*value)) +{ +} + +PropertyValue::PropertyValue(ClockSet *value) : + type_(type_clks), + clks_(new ClockSeq) +{ + ClockSet::Iterator clk_iter(value); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + clks_->push_back(clk); + } +} + +PropertyValue::PropertyValue(PathRefSeq *value) : + type_(type_path_refs), + path_refs_(new PathRefSeq(*value)) +{ +} + +PropertyValue::PropertyValue(PwrActivity *value) : + type_(type_pwr_activity), + pwr_activity_(*value) +{ +} + +PropertyValue::PropertyValue(const PropertyValue &value) : + type_(value.type_) +{ + switch (type_) { + case Type::type_none: + break; + case Type::type_string: + string_ = stringCopy(value.string_); + break; + case Type::type_float: + float_ = value.float_; + break; + case Type::type_bool: + bool_ = value.bool_; + break; + case Type::type_liberty_library: + liberty_library_ = value.liberty_library_; + break; + case Type::type_liberty_cell: + liberty_cell_ = value.liberty_cell_; + break; + case Type::type_liberty_port: + liberty_port_ = value.liberty_port_; + break; + case Type::type_library: + library_ = value.library_; + break; + case Type::type_cell: + cell_ = value.cell_; + break; + case Type::type_port: + port_ = value.port_; + break; + case Type::type_instance: + inst_ = value.inst_; + break; + case Type::type_pin: + pin_ = value.pin_; + break; + case Type::type_pins: + pins_ = value.pins_ ? new PinSeq(*value.pins_) : nullptr; + break; + case Type::type_net: + net_ = value.net_; + break; + case Type::type_clk: + clk_ = value.clk_; + break; + case Type::type_clks: + clks_ = value.clks_ ? new ClockSeq(*value.clks_) : nullptr; + break; + case Type::type_path_refs: + path_refs_ = value.path_refs_ ? new PathRefSeq(*value.path_refs_) : nullptr; + break; + case Type::type_pwr_activity: + pwr_activity_ = value.pwr_activity_; + break; + } +} + +PropertyValue::PropertyValue(PropertyValue &&value) : + type_(value.type_) +{ + switch (type_) { + case Type::type_none: + break; + case Type::type_string: + string_ = value.string_; + value.string_ = nullptr; + break; + case Type::type_float: + float_ = value.float_; + break; + case Type::type_bool: + bool_ = value.bool_; + break; + case Type::type_library: + library_ = value.library_; + break; + case Type::type_cell: + cell_ = value.cell_; + break; + case Type::type_port: + port_ = value.port_; + break; + case Type::type_liberty_library: + liberty_library_ = value.liberty_library_; + break; + case Type::type_liberty_cell: + liberty_cell_ = value.liberty_cell_; + break; + case Type::type_liberty_port: + liberty_port_ = value.liberty_port_; + break; + case Type::type_instance: + inst_ = value.inst_; + break; + case Type::type_pin: + pin_ = value.pin_; + break; + case Type::type_pins: + pins_ = value.pins_; + value.pins_ = nullptr; + break; + case Type::type_net: + net_ = value.net_; + break; + case Type::type_clk: + clk_ = value.clk_; + break; + case Type::type_clks: + clks_ = value.clks_; + // Steal the value. + value.clks_ = nullptr; + break; + case Type::type_path_refs: + path_refs_ = value.path_refs_; + // Steal the value. + value.clks_ = nullptr; + break; + case Type::type_pwr_activity: + pwr_activity_ = value.pwr_activity_; + break; + } +} + +PropertyValue::~PropertyValue() +{ + switch (type_) { + case Type::type_string: + stringDelete(string_); + break; + case Type::type_clks: + delete clks_; + break; + case Type::type_pins: + delete pins_; + break; + case Type::type_path_refs: + delete path_refs_; + break; + default: + break; + } +} + +PropertyValue & +PropertyValue::operator=(const PropertyValue &value) +{ + type_ = value.type_; + switch (type_) { + case Type::type_none: + break; + case Type::type_string: + string_ = stringCopy(value.string_); + break; + case Type::type_float: + float_ = value.float_; + break; + case Type::type_bool: + bool_ = value.bool_; + break; + case Type::type_library: + library_ = value.library_; + break; + case Type::type_cell: + cell_ = value.cell_; + break; + case Type::type_port: + port_ = value.port_; + break; + case Type::type_liberty_library: + liberty_library_ = value.liberty_library_; + break; + case Type::type_liberty_cell: + liberty_cell_ = value.liberty_cell_; + break; + case Type::type_liberty_port: + liberty_port_ = value.liberty_port_; + break; + case Type::type_instance: + inst_ = value.inst_; + break; + case Type::type_pin: + pin_ = value.pin_; + break; + case Type::type_pins: + pins_ = value.pins_ ? new PinSeq(*value.pins_) : nullptr; + break; + case Type::type_net: + net_ = value.net_; + break; + case Type::type_clk: + clk_ = value.clk_; + break; + case Type::type_clks: + clks_ = value.clks_ ? new ClockSeq(*value.clks_) : nullptr; + break; + case Type::type_path_refs: + path_refs_ = value.path_refs_ ? new PathRefSeq(*value.path_refs_) : nullptr; + break; + case Type::type_pwr_activity: + pwr_activity_ = value.pwr_activity_; + break; + } + return *this; +} + +PropertyValue & +PropertyValue::operator=(PropertyValue &&value) +{ + type_ = value.type_; + switch (type_) { + case Type::type_none: + break; + case Type::type_string: + string_ = value.string_; + value.string_ = nullptr; + break; + case Type::type_float: + float_ = value.float_; + break; + case Type::type_bool: + bool_ = value.bool_; + break; + case Type::type_library: + library_ = value.library_; + break; + case Type::type_cell: + cell_ = value.cell_; + break; + case Type::type_port: + port_ = value.port_; + break; + case Type::type_liberty_library: + liberty_library_ = value.liberty_library_; + break; + case Type::type_liberty_cell: + liberty_cell_ = value.liberty_cell_; + break; + case Type::type_liberty_port: + liberty_port_ = value.liberty_port_; + break; + case Type::type_instance: + inst_ = value.inst_; + break; + case Type::type_pin: + pin_ = value.pin_; + break; + case Type::type_pins: + pins_ = value.pins_; + value.pins_ = nullptr; + break; + case Type::type_net: + net_ = value.net_; + break; + case Type::type_clk: + clk_ = value.clk_; + break; + case Type::type_clks: + clks_ = value.clks_; + value.clks_ = nullptr; + break; + case Type::type_path_refs: + path_refs_ = value.path_refs_; + value.clks_ = nullptr; + break; + case Type::type_pwr_activity: + pwr_activity_ = value.pwr_activity_; + break; + } + return *this; +} + +//////////////////////////////////////////////////////////////// + +PropertyValue +getProperty(const Library *lib, + const char *property, + Sta *sta) +{ + auto network = sta->cmdNetwork(); + if (stringEqual(property, "name") + || stringEqual(property, "full_name")) + return PropertyValue(network->name(lib)); +#if 0 + else if (stringEqual(property, "filename")) + return PropertyValue(network->filename(lib)); +#endif + else + throw PropertyUnknown("library", property); +} + +PropertyValue +getProperty(const LibertyLibrary *lib, + const char *property, + Sta *) +{ + if (stringEqual(property, "name") + || stringEqual(property, "full_name")) + return PropertyValue(lib->name()); + else if (stringEqual(property, "filename")) + return PropertyValue(lib->filename()); + else + throw PropertyUnknown("liberty library", property); +} + +PropertyValue +getProperty(const LibertyCell *cell, + const char *property, + Sta *sta) +{ + if (stringEqual(property, "name") + || stringEqual(property, "base_name")) + return PropertyValue(cell->name()); + else if (stringEqual(property, "full_name")) { + auto network = sta->cmdNetwork(); + auto lib = cell->libertyLibrary(); + const char *lib_name = lib->name(); + const char *cell_name = cell->name(); + string full_name; + stringPrint(full_name, "%s%c%s", + lib_name, + network->pathDivider(), + cell_name); + return PropertyValue(full_name); + } + else if (stringEqual(property, "filename")) + return PropertyValue(cell->filename()); + else if (stringEqual(property, "library")) + return PropertyValue(cell->libertyLibrary()); + else if (stringEqual(property, "is_buffer")) + return PropertyValue(cell->isBuffer()); + else if (stringEqual(property, "dont_use")) + return PropertyValue(cell->dontUse()); + else + throw PropertyUnknown("liberty cell", property); +} + +PropertyValue +getProperty(const Cell *cell, + const char *property, + Sta *sta) +{ + auto network = sta->cmdNetwork(); + if (stringEqual(property, "name") + || stringEqual(property, "base_name")) + return PropertyValue(network->name(cell)); + else if (stringEqual(property, "full_name")) { + auto lib = network->library(cell); + const char *lib_name = network->name(lib); + const char *cell_name = network->name(cell); + string full_name; + stringPrint(full_name, "%s%c%s", + lib_name, + network->pathDivider(), + cell_name); + return PropertyValue(full_name); + } + else if (stringEqual(property, "library")) + return PropertyValue(network->library(cell)); + else if (stringEqual(property, "filename")) + return PropertyValue(network->filename(cell)); + else + throw PropertyUnknown("cell", property); +} + +//////////////////////////////////////////////////////////////// + +PropertyValue +getProperty(const Port *port, + const char *property, + Sta *sta) +{ + auto network = sta->cmdNetwork(); + if (stringEqual(property, "name") + || stringEqual(property, "full_name")) + return PropertyValue(network->name(port)); + else if (stringEqual(property, "direction")) + return PropertyValue(network->direction(port)->name()); + else if (stringEqual(property, "liberty_port")) + return PropertyValue(network->libertyPort(port)); + + else if (stringEqual(property, "activity")) { + const Instance *top_inst = network->topInstance(); + const Pin *pin = network->findPin(top_inst, port); + PwrActivity activity = sta->power()->findClkedActivity(pin); + return PropertyValue(&activity); + } + + else if (stringEqual(property, "actual_fall_transition_min")) + return portSlewProperty(port, TransRiseFall::fall(), MinMax::min(), sta); + else if (stringEqual(property, "actual_fall_transition_max")) + return portSlewProperty(port, TransRiseFall::fall(), MinMax::max(), sta); + else if (stringEqual(property, "actual_rise_transition_min")) + return portSlewProperty(port, TransRiseFall::rise(), MinMax::min(), sta); + else if (stringEqual(property, "actual_rise_transition_max")) + return portSlewProperty(port, TransRiseFall::rise(), MinMax::max(), sta); + + else if (stringEqual(property, "min_fall_slack")) + return portSlackProperty(port, TransRiseFall::fall(), MinMax::min(), sta); + else if (stringEqual(property, "max_fall_slack")) + return portSlackProperty(port, TransRiseFall::fall(), MinMax::max(), sta); + else if (stringEqual(property, "min_rise_slack")) + return portSlackProperty(port, TransRiseFall::rise(), MinMax::min(), sta); + else if (stringEqual(property, "max_rise_slack")) + return portSlackProperty(port, TransRiseFall::rise(), MinMax::max(), sta); + + else + throw PropertyUnknown("port", property); +} + +static PropertyValue +portSlewProperty(const Port *port, + const TransRiseFall *tr, + const MinMax *min_max, + Sta *sta) +{ + auto network = sta->cmdNetwork(); + Instance *top_inst = network->topInstance(); + Pin *pin = network->findPin(top_inst, port); + return pinSlewProperty(pin, tr, min_max, sta); +} + +static PropertyValue +portSlackProperty(const Port *port, + const TransRiseFall *tr, + const MinMax *min_max, + Sta *sta) +{ + auto network = sta->cmdNetwork(); + Instance *top_inst = network->topInstance(); + Pin *pin = network->findPin(top_inst, port); + return pinSlackProperty(pin, tr, min_max, sta); +} + +PropertyValue +getProperty(const LibertyPort *port, + const char *property, + Sta *sta) +{ + if (stringEqual(property, "name")) + return PropertyValue(port->name()); + else if (stringEqual(property, "full_name")) + return PropertyValue(port->name()); + else if (stringEqual(property, "direction")) + return PropertyValue(port->direction()->name()); + else if (stringEqual(property, "capacitance")) { + float cap = port->capacitance(TransRiseFall::rise(), MinMax::max()); + return PropertyValue(sta->units()->capacitanceUnit()->asString(cap, 6)); + } + else if (stringEqual(property, "drive_resistance_rise_min")) + return PropertyValue(port->driveResistance(TransRiseFall::rise(), + MinMax::min())); + else if (stringEqual(property, "drive_resistance_rise_max")) + return PropertyValue(port->driveResistance(TransRiseFall::rise(), + MinMax::max())); + else if (stringEqual(property, "drive_resistance_fall_min")) + return PropertyValue(port->driveResistance(TransRiseFall::fall(), + MinMax::min())); + else if (stringEqual(property, "drive_resistance_fall_max")) + return PropertyValue(port->driveResistance(TransRiseFall::fall(), + MinMax::max())); + else + throw PropertyUnknown("liberty port", property); +} + +//////////////////////////////////////////////////////////////// + +PropertyValue +getProperty(const Instance *inst, + const char *property, + Sta *sta) +{ + auto network = sta->cmdNetwork(); + if (stringEqual(property, "name")) + return PropertyValue(network->name(inst)); + else if (stringEqual(property, "full_name")) + return PropertyValue(network->pathName(inst)); + else if (stringEqual(property, "ref_name")) + return PropertyValue(network->name(network->cell(inst))); + else if (stringEqual(property, "liberty_cell")) + return PropertyValue(network->libertyCell(inst)); + else if (stringEqual(property, "cell")) + return PropertyValue(network->cell(inst)); + else + throw PropertyUnknown("instance", property); +} + +//////////////////////////////////////////////////////////////// + +PropertyValue +getProperty(const Pin *pin, + const char *property, + Sta *sta) +{ + auto network = sta->cmdNetwork(); + if (stringEqual(property, "direction")) + return PropertyValue(network->direction(pin)->name()); + else if (stringEqual(property, "name") + || stringEqual(property, "full_name")) + return PropertyValue(network->pathName(pin)); + else if (stringEqual(property, "lib_pin_name")) + return PropertyValue(network->portName(pin)); + else if (stringEqual(property, "clocks")) { + ClockSet clks; + sta->clocks(pin, clks); + return PropertyValue(&clks); + } + else if (stringEqual(property, "activity")) { + PwrActivity activity = sta->power()->findClkedActivity(pin); + return PropertyValue(&activity); + } + + else if (stringEqual(property, "max_fall_slack")) + return pinSlackProperty(pin, TransRiseFall::fall(), MinMax::max(), sta); + else if (stringEqual(property, "max_rise_slack")) + return pinSlackProperty(pin, TransRiseFall::rise(), MinMax::max(), sta); + else if (stringEqual(property, "min_fall_slack")) + return pinSlackProperty(pin, TransRiseFall::fall(), MinMax::min(), sta); + else if (stringEqual(property, "min_rise_slack")) + return pinSlackProperty(pin, TransRiseFall::rise(), MinMax::min(), sta); + + else if (stringEqual(property, "actual_fall_transition_max")) + return pinSlewProperty(pin, TransRiseFall::fall(), MinMax::max(), sta); + else if (stringEqual(property, "actual_rise_transition_max")) + return pinSlewProperty(pin, TransRiseFall::rise(), MinMax::max(), sta); + else if (stringEqual(property, "actual_rise_transition_min")) + return pinSlewProperty(pin, TransRiseFall::rise(), MinMax::min(), sta); + else if (stringEqual(property, "actual_fall_transition_min")) + return pinSlewProperty(pin, TransRiseFall::fall(), MinMax::min(), sta); + + else + throw PropertyUnknown("pin", property); +} + +static PropertyValue +pinSlackProperty(const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max, + Sta *sta) +{ + Slack slack = sta->pinSlack(pin, tr, min_max); + return PropertyValue(delayPropertyValue(slack, sta)); +} + +static PropertyValue +pinSlewProperty(const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max, + Sta *sta) +{ + auto 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, tr, min_max); + if (fuzzyGreater(vertex_slew, slew, min_max)) + slew = vertex_slew; + } + if (bidirect_drvr_vertex) { + Slew vertex_slew = sta->vertexSlew(bidirect_drvr_vertex, tr, min_max); + if (fuzzyGreater(vertex_slew, slew, min_max)) + slew = vertex_slew; + } + return PropertyValue(delayPropertyValue(slew, sta)); +} + +//////////////////////////////////////////////////////////////// + +PropertyValue +getProperty(const Net *net, + const char *property, + Sta *sta) +{ + auto network = sta->cmdNetwork(); + if (stringEqual(property, "name")) + return PropertyValue(network->name(net)); + else if (stringEqual(property, "full_name")) + return PropertyValue(network->pathName(net)); + else + throw PropertyUnknown("net", property); +} + +//////////////////////////////////////////////////////////////// + +PropertyValue +getProperty(Edge *edge, + const char *property, + Sta *sta) +{ + if (stringEqual(property, "full_name")) { + auto network = sta->cmdNetwork(); + auto graph = sta->graph(); + const char *from = edge->from(graph)->name(network); + const char *to = edge->to(graph)->name(network); + return stringPrintTmp("%s -> %s", from, to); + } + if (stringEqual(property, "delay_min_fall")) + return edgeDelayProperty(edge, TransRiseFall::fall(), MinMax::min(), sta); + else if (stringEqual(property, "delay_max_fall")) + return edgeDelayProperty(edge, TransRiseFall::fall(), MinMax::max(), sta); + else if (stringEqual(property, "delay_min_rise")) + return edgeDelayProperty(edge, TransRiseFall::rise(), MinMax::min(), sta); + else if (stringEqual(property, "delay_max_rise")) + return edgeDelayProperty(edge, TransRiseFall::rise(), MinMax::max(), sta); + else if (stringEqual(property, "sense")) + return PropertyValue(timingSenseString(edge->sense())); + else if (stringEqual(property, "from_pin")) + return PropertyValue(edge->from(sta->graph())->pin()); + else if (stringEqual(property, "to_pin")) + return PropertyValue(edge->to(sta->graph())->pin()); + else + throw PropertyUnknown("edge", property); +} + +static PropertyValue +edgeDelayProperty(Edge *edge, + const TransRiseFall *tr, + const MinMax *min_max, + Sta *sta) +{ + ArcDelay delay = 0.0; + bool delay_exists = false; + TimingArcSet *arc_set = edge->timingArcSet(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + TransRiseFall *to_tr = arc->toTrans()->asRiseFall(); + if (to_tr == tr) { + for (auto corner : *sta->corners()) { + DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); + ArcDelay arc_delay = sta->arcDelay(edge, arc, dcalc_ap); + if (!delay_exists + || ((min_max == MinMax::max() + && arc_delay > delay) + || (min_max == MinMax::min() + && arc_delay < delay))) + delay = arc_delay; + } + } + } + return PropertyValue(delayPropertyValue(delay, sta)); +} + +//////////////////////////////////////////////////////////////// + +PropertyValue +getProperty(TimingArcSet *arc_set, + const char *property, + Sta *) +{ + if (stringEqual(property, "name") + || stringEqual(property, "full_name")) { + auto from = arc_set->from()->name(); + auto to = arc_set->to()->name(); + auto cell_name = arc_set->libertyCell()->name(); + string name; + stringPrint(name, "%s %s -> %s", cell_name, from, to); + return PropertyValue(name); + } + else + throw PropertyUnknown("timing arc", property); +} + +//////////////////////////////////////////////////////////////// + +PropertyValue +getProperty(Clock *clk, + const char *property, + Sta *sta) +{ + if (stringEqual(property, "name") + || stringEqual(property, "full_name")) + return PropertyValue(clk->name()); + else if (stringEqual(property, "period")) + return PropertyValue(sta->units()->timeUnit()->asString(clk->period(), 6)); + else if (stringEqual(property, "sources")) + return PropertyValue(clk->pins()); + else if (stringEqual(property, "propagated")) + return PropertyValue(clk->isPropagated() ? "1" : "0"); + else + throw PropertyUnknown("clock", property); +} + +//////////////////////////////////////////////////////////////// + +PropertyValue +getProperty(PathEnd *end, + const char *property, + Sta *sta) +{ + if (stringEqual(property, "startpoint")) { + PathExpanded expanded(end->path(), sta); + return PropertyValue(expanded.startPath()->pin(sta)); + } + else if (stringEqual(property, "startpoint_clock")) + return PropertyValue(end->path()->clock(sta)); + else if (stringEqual(property, "endpoint")) + return PropertyValue(end->path()->pin(sta)); + else if (stringEqual(property, "endpoint_clock")) + return PropertyValue(end->targetClk(sta)); + else if (stringEqual(property, "endpoint_clock_pin")) + return PropertyValue(end->targetClkPath()->pin(sta)); + else if (stringEqual(property, "slack")) + return PropertyValue(delayPropertyValue(end->slack(sta), sta)); + else if (stringEqual(property, "points")) { + PathExpanded expanded(end->path(), sta); + PathRefSeq paths; + for (auto i = expanded.startIndex(); i < expanded.size(); i++) { + PathRef *path = expanded.path(i); + paths.push_back(*path); + } + return PropertyValue(&paths); + } + else + throw PropertyUnknown("path end", property); +} + +PropertyValue +getProperty(PathRef *path, + const char *property, + Sta *sta) +{ + if (stringEqual(property, "pin")) + return PropertyValue(path->pin(sta)); + else if (stringEqual(property, "arrival")) + return PropertyValue(delayPropertyValue(path->arrival(sta), sta)); + else if (stringEqual(property, "required")) + return PropertyValue(delayPropertyValue(path->required(sta), sta)); + else if (stringEqual(property, "slack")) + return PropertyValue(delayPropertyValue(path->slack(sta), sta)); + else + throw PropertyUnknown("path", property); +} + +static float +delayPropertyValue(Delay delay, + Sta *sta) +{ + return delayAsFloat(delay) / sta->units()->timeUnit()->scale(); +} + +} // namespace diff --git a/search/Property.hh b/search/Property.hh new file mode 100644 index 0000000..c90ec0e --- /dev/null +++ b/search/Property.hh @@ -0,0 +1,194 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PROPERTY_H +#define STA_PROPERTY_H + +#include +#include "LibertyClass.hh" +#include "NetworkClass.hh" +#include "SearchClass.hh" +#include "SdcClass.hh" + +namespace sta { + +using std::string; + +class Sta; +class PwrActivity; + +// Adding a new property type +// value union +// enum Type +// constructor +// copy constructor switch clause +// move constructor switch clause +// operator= & switch clause +// operator= && switch clause +// StaTcl.i swig %typemap(out) PropertyValue switch clause + +class PropertyValue +{ +public: + enum 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_path_refs, type_pwr_activity }; + PropertyValue(); + PropertyValue(const char *value); + PropertyValue(string &value); + PropertyValue(float value); + PropertyValue(bool value); + PropertyValue(Library *value); + PropertyValue(Cell *value); + PropertyValue(Port *value); + PropertyValue(LibertyLibrary *value); + PropertyValue(LibertyCell *value); + PropertyValue(LibertyPort *value); + PropertyValue(Instance *value); + PropertyValue(Pin *value); + PropertyValue(PinSeq *value); + PropertyValue(PinSet *value); + PropertyValue(Net *value); + PropertyValue(Clock *value); + PropertyValue(ClockSeq *value); + PropertyValue(ClockSet *value); + PropertyValue(PathRefSeq *value); + PropertyValue(PwrActivity *value); + // Copy constructor. + PropertyValue(const PropertyValue &props); + // Move constructor. + PropertyValue(PropertyValue &&props); + ~PropertyValue(); + Type type() const { return type_; } + const char *stringValue() const { return string_; } + float floatValue() const { return float_; } + bool boolValue() const { return bool_; } + LibertyLibrary *libertyLibrary() const { return liberty_library_; } + LibertyCell *libertyCell() const { return liberty_cell_; } + LibertyPort *libertyPort() const { return liberty_port_; } + Library *library() const { return library_; } + Cell *cell() const { return cell_; } + Port *port() const { return port_; } + Instance *instance() const { return inst_; } + Pin *pin() const { return pin_; } + PinSeq *pins() const { return pins_; } + Net *net() const { return net_; } + Clock *clock() const { return clk_; } + ClockSeq *clocks() const { return clks_; } + PathRefSeq *pathRefs() const { return path_refs_; } + PwrActivity pwrActivity() const { return pwr_activity_; } + // Copy assignment. + PropertyValue &operator=(const PropertyValue &); + // Move assignment. + PropertyValue &operator=(PropertyValue &&); + +private: + Type type_; + union { + const char *string_; + float float_; + bool bool_; + Library *library_; + Cell *cell_; + Port *port_; + LibertyLibrary *liberty_library_; + LibertyCell *liberty_cell_; + LibertyPort *liberty_port_; + Instance *inst_; + Pin *pin_; + PinSeq *pins_; + Net *net_; + Clock *clk_; + ClockSeq *clks_; + PathRefSeq *path_refs_; + PwrActivity pwr_activity_; + }; +}; + +PropertyValue +getProperty(const Instance *inst, + const char *property, + Sta *sta); + +PropertyValue +getProperty(const Pin *pin, + const char *property, + Sta *sta); + +PropertyValue +getProperty(const Net *net, + const char *property, + Sta *sta); + +PropertyValue +getProperty(const Port *port, + const char *property, + Sta *sta); + +PropertyValue +getProperty(const Cell *cell, + const char *property, + Sta *sta); + +PropertyValue +getProperty(const LibertyCell *cell, + const char *property, + Sta *sta); + +PropertyValue +getProperty(const LibertyPort *port, + const char *property, + Sta *); + +PropertyValue +getProperty(const LibertyLibrary *lib, + const char *property, + Sta *sta); + +PropertyValue +getProperty(const Library *lib, + const char *property, + Sta *sta); + +PropertyValue +getProperty(Edge *edge, + const char *property, + Sta *sta); + +PropertyValue +getProperty(Clock *clk, + const char *property, + Sta *sta); + +PropertyValue +getProperty(PathEnd *end, + const char *property, + Sta *sta); + +PropertyValue +getProperty(PathRef *end, + const char *property, + Sta *sta); + +PropertyValue +getProperty(TimingArcSet *arc_set, + const char *property, + Sta *sta); + +} // namespace +#endif diff --git a/search/ReportPath.cc b/search/ReportPath.cc new file mode 100644 index 0000000..2655589 --- /dev/null +++ b/search/ReportPath.cc @@ -0,0 +1,3347 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Report.hh" +#include "Error.hh" +#include "StringUtil.hh" +#include "Units.hh" +#include "Fuzzy.hh" +#include "TimingRole.hh" +#include "Transition.hh" +#include "PortDirection.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "PortDelay.hh" +#include "ExceptionPath.hh" +#include "InputDrive.hh" +#include "Sdc.hh" +#include "Graph.hh" +#include "ClkInfo.hh" +#include "Tag.hh" +#include "PathVertex.hh" +#include "DcalcAnalysisPt.hh" +#include "ArcDelayCalc.hh" +#include "GraphDelayCalc.hh" +#include "Parasitics.hh" +#include "PathAnalysisPt.hh" +#include "PathGroup.hh" +#include "CheckMinPulseWidths.hh" +#include "CheckMinPeriods.hh" +#include "CheckMaxSkews.hh" +#include "PathRef.hh" +#include "Search.hh" +#include "PathExpanded.hh" +#include "Latches.hh" +#include "Corner.hh" +#include "Genclks.hh" +#include "ReportPath.hh" + +namespace sta { + +ReportField::ReportField(const char *name, + const char *title, + int width, + bool left_justify, + Unit *unit, + bool enabled) : + name_(name), + title_(stringCopy(title)), + left_justify_(left_justify), + unit_(unit), + enabled_(enabled), + blank_(nullptr) +{ + setWidth(width); +} + +ReportField::~ReportField() +{ + stringDelete(title_); + stringDelete(blank_); +} + +void +ReportField::setProperties(const char *title, + int width, + bool left_justify) +{ + if (title_) + stringDelete(title_); + title_ = stringCopy(title); + left_justify_ = left_justify; + setWidth(width); +} + +void +ReportField::setWidth(int 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'; +} + +void +ReportField::setEnabled(bool enabled) +{ + enabled_ = enabled; +} + +//////////////////////////////////////////////////////////////// + +const float ReportPath::field_blank_ = -1.0; + +ReportPath::ReportPath(StaState *sta) : + StaState(sta), + format_(ReportPathFormat::full), + no_split_(false), + start_end_pt_width_(80), + plus_zero_(nullptr), + minus_zero_(nullptr) +{ + setDigits(2); + makeFields(); + setReportFields(false, false, false, false); +} + +ReportPath::~ReportPath() +{ + delete field_description_; + delete field_total_; + delete field_incr_; + delete field_capacitance_; + delete field_slew_; + delete field_fanout_; + delete field_edge_; + delete field_case_; + + stringDelete(plus_zero_); + stringDelete(minus_zero_); +} + +void +ReportPath::makeFields() +{ + field_fanout_ = makeField("fanout", "Fanout", 5, false, nullptr, true); + field_capacitance_ = makeField("capacitance", "Cap", 6, false, + units_->capacitanceUnit(), true); + field_slew_ = makeField("slew", "Slew", 6, false, units_->timeUnit(), + true); + field_incr_ = makeField("incr", "Delay", 6, false, units_->timeUnit(), + true); + field_total_ = makeField("total", "Time", 6, false, units_->timeUnit(), + 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); +} + +ReportField * +ReportPath::makeField(const char *name, + const char *title, + int width, + bool left_justify, + Unit *unit, + bool enabled) +{ + ReportField *field = new ReportField(name, title, width, left_justify, + unit, enabled); + fields_.push_back(field); + return field; +} + +ReportField * +ReportPath::findField(const char *name) +{ + ReportFieldSeq::Iterator field_iter(fields_); + while (field_iter.hasNext()) { + ReportField *field = field_iter.next(); + if (stringEq(name, field->name())) + return field; + } + return nullptr; +} + +void +ReportPath::setReportFieldOrder(StringSeq *field_names) +{ + // Disable all fields. + ReportFieldSeq::Iterator field_iter1(fields_); + while (field_iter1.hasNext()) { + ReportField *field = field_iter1.next(); + field->setEnabled(false); + } + + ReportFieldSeq next_fields; + StringSeq::Iterator name_iter(field_names); + while (name_iter.hasNext()) { + const char *field_name = name_iter.next(); + ReportField *field = findField(field_name); + next_fields.push_back(field); + field->setEnabled(true); + } + // Push remaining disabled fields on the end. + ReportFieldSeq::Iterator field_iter2(fields_); + while (field_iter2.hasNext()) { + ReportField *field = field_iter2.next(); + if (!field->enabled()) + next_fields.push_back(field); + } + + fields_.clear(); + ReportFieldSeq::Iterator field_iter3(next_fields); + while (field_iter3.hasNext()) { + ReportField *field = field_iter3.next(); + fields_.push_back(field); + } +} + +void +ReportPath::setReportFields(bool report_input_pin, + bool report_net, + bool report_cap, + bool report_slew) +{ + report_input_pin_ = report_input_pin; + report_net_ = report_net; + + field_fanout_->setEnabled(report_net_); + field_capacitance_->setEnabled(report_cap); + field_slew_->setEnabled(report_slew); + // for debug + field_case_->setEnabled(false); +} + +void +ReportPath::setPathFormat(ReportPathFormat format) +{ + format_ = format; +} + +void +ReportPath::setNoSplit(bool no_split) +{ + no_split_ = no_split; +} + +void +ReportPath::setDigits(int digits) +{ + digits_ = digits; + + if (plus_zero_) { + stringDelete(plus_zero_); + stringDelete(minus_zero_); + } + minus_zero_ = stringPrint("-%.*f", digits_, 0.0); + plus_zero_ = stringPrint("%.*f", digits_, 0.0); +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportPathEndHeader() +{ + string header; + switch (format_) { + case ReportPathFormat::full: + case ReportPathFormat::full_clock: + case ReportPathFormat::full_clock_expanded: + case ReportPathFormat::shorter: + case ReportPathFormat::endpoint: + break; + case ReportPathFormat::summary: + reportSummaryHeader(header); + report_->print(header); + break; + case ReportPathFormat::slack_only: + reportSlackOnlyHeader(header); + report_->print(header); + break; + default: + internalError("unsupported path type"); + break; + } +} + +void +ReportPath::reportPathEndFooter() +{ + string header; + switch (format_) { + case ReportPathFormat::full: + case ReportPathFormat::full_clock: + case ReportPathFormat::full_clock_expanded: + case ReportPathFormat::shorter: + break; + case ReportPathFormat::endpoint: + case ReportPathFormat::summary: + case ReportPathFormat::slack_only: + report_->print("\n"); + break; + default: + internalError("unsupported path type"); + break; + } +} + +void +ReportPath::reportPathEnd(PathEnd *end) +{ + reportPathEnd(end, nullptr); +} + +void +ReportPath::reportPathEnd(PathEnd *end, + PathEnd *prev_end) +{ + string result; + switch (format_) { + case ReportPathFormat::full: + case ReportPathFormat::full_clock: + case ReportPathFormat::full_clock_expanded: + end->reportFull(this, result); + report_->print(result); + report_->print("\n\n"); + break; + case ReportPathFormat::shorter: + end->reportShort(this, result); + report_->print(result); + report_->print("\n\n"); + break; + case ReportPathFormat::endpoint: + reportEndpointHeader(end, prev_end); + reportEndLine(end, result); + report_->print(result); + break; + case ReportPathFormat::summary: + reportSummaryLine(end, result); + report_->print(result); + break; + case ReportPathFormat::slack_only: + reportSlackOnly(end, result); + report_->print(result); + break; + default: + internalError("unsupported path type"); + break; + } +} + +void +ReportPath::reportPathEnds(PathEndSeq *ends) +{ + reportPathEndHeader(); + PathEndSeq::Iterator end_iter(ends); + PathEnd *prev_end = nullptr; + while (end_iter.hasNext()) { + PathEnd *end = end_iter.next(); + reportEndpointHeader(end, prev_end); + string result; + end->reportFull(this, result); + report_->print(result); + report_->print("\n\n"); + prev_end = end; + } + reportPathEndFooter(); +} + +void +ReportPath::reportEndpointHeader(PathEnd *end, + PathEnd *prev_end) +{ + PathGroup *prev_group = nullptr; + if (prev_end) + prev_group = search_->pathGroup(prev_end); + PathGroup *group = search_->pathGroup(end); + if (group != prev_group) { + if (prev_group) + report_->print("\n"); + const char *setup_hold = (end->minMax(this) == MinMax::min()) + ? "min_delay/hold" + : "max_delay/setup"; + report_->print("%s group %s\n\n", + setup_hold, + group->name()); + string header; + reportEndHeader(header); + report_->print(header); + } +} + +void +ReportPath::reportPath(const Path *path) +{ + string result; + reportPath(path, result); + report_->print(result); +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportShort(const PathEndUnconstrained *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + reportShort(end, expanded, result); +} + +void +ReportPath::reportShort(const PathEndUnconstrained *end, + PathExpanded &expanded, + string &result) +{ + reportStartpoint(end, expanded, result); + reportUnclockedEndpoint(end, "internal pin", result); + reportGroup(end, result); +} + +void +ReportPath::reportFull(const PathEndUnconstrained *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + reportShort(end, expanded, result); + reportEndOfLine(result); + + reportPath(end, expanded, result); + reportLine("data arrival time", end->dataArrivalTimeOffset(this), + end->pathEarlyLate(this), result); + reportDashLine(result); + result += "(Path is unconstrained)\n"; +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportShort(const PathEndCheck *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + reportShort(end, expanded, result); +} + +void +ReportPath::reportShort(const PathEndCheck *end, + PathExpanded &expanded, + string &result) +{ + reportStartpoint(end, expanded, result); + reportEndpoint(end, result); + reportGroup(end, result); +} + +void +ReportPath::reportFull(const PathEndCheck *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + reportShort(end, expanded, result); + reportSrcPathArrival(end, expanded, result); + reportTgtClk(end, result); + reportRequired(end, checkRoleString(end), result); + reportSlack(end, result); +} + +string +ReportPath::checkRoleString(const PathEnd *end) +{ + const char *check_role = end->checkRole(this)->asString(); + return stdstrPrint("library %s time", check_role); +} + +void +ReportPath::reportEndpoint(const PathEndCheck *end, + string &result) +{ + 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)); + const TimingRole *check_role = end->checkRole(this); + const TimingRole *check_generic_role = check_role->genericRole(); + if (check_role == TimingRole::recovery() + || check_role == TimingRole::removal()) { + const char *check_role_name = check_role->asString(); + auto reason = stdstrPrint("%s check against %s-edge clock %s", + check_role_name, + rise_fall, + clk_name.c_str()); + reportEndpoint(inst_name, reason, result); + } + else if (check_generic_role == TimingRole::setup() + || 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()); + reportEndpoint(inst_name, reason, result); + } + else { + const char *reg_desc = clkRegLatchDesc(end); + auto reason = stdstrPrint("%s clocked by %s", reg_desc, clk_name.c_str()); + reportEndpoint(inst_name, reason, result); + } + } +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportShort(const PathEndLatchCheck *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + reportShort(end, expanded, result); +} + +void +ReportPath::reportShort(const PathEndLatchCheck *end, + PathExpanded &expanded, + string &result) +{ + reportStartpoint(end, expanded, result); + reportEndpoint(end, result); + reportGroup(end, result); +} + +void +ReportPath::reportFull(const PathEndLatchCheck *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + const EarlyLate *early_late = end->pathEarlyLate(this); + reportShort(end, expanded, result); + PathDelay *path_delay = end->pathDelay(); + reportEndOfLine(result); + bool ignore_clk_latency = path_delay && path_delay->ignoreClkLatency(); + if (ignore_clk_latency) { + // Based on reportSrcPath. + reportPathHeader(result); + reportPath3(end->path(), expanded, false, false, 0.0, + end->sourceClkOffset(this), result); + } + else + reportSrcPath(end, expanded, result); + reportLine("data arrival time", end->dataArrivalTimeOffset(this), + early_late, result); + reportEndOfLine(result); + + 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); + // Adjust required to requiredTimeOffset. + req_time += end->sourceClkOffset(this); + if (path_delay) { + float delay = path_delay->delay(); + reportLine("max_delay", delay, delay, early_late, result); + if (!ignore_clk_latency) { + if (reportClkPath() + && isPropagated(end->targetClkPath())) + reportTgtClk(end, delay, result); + else { + Delay delay1(delay); + reportCommonClkPessimism(end, delay1, result); + } + } + } + else + reportTgtClk(end, result); + + if (borrow >= 0.0) + reportLine("time borrowed from endpoint", borrow, req_time, + early_late, result); + else + reportLine("time given to endpoint", borrow, req_time, + early_late, result); + reportLine("data required time", req_time, early_late, result); + reportDashLine(result); + reportSlack(end, result); + if (end->checkGenericRole(this) == TimingRole::setup() + && !ignore_clk_latency) { + reportEndOfLine(result); + reportBorrowing(end, borrow, time_given_to_startpoint, result); + } +} + +void +ReportPath::reportEndpoint(const PathEndLatchCheck *end, + string &result) +{ + 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()); + reportEndpoint(inst_name, reason, result); +} + +const char * +ReportPath::latchDesc(const PathEndLatchCheck *end) +{ + // Liberty latch descriptions can have timing checks to the + // wrong edge of the enable, so look up the EN->Q arcs and use + // them to characterize the latch as positive/negative. + TimingArc *check_arc = end->checkArc(); + TimingArcSet *check_set = check_arc->set(); + LibertyCell *cell = check_set->from()->libertyCell(); + TransRiseFall *enable_tr = cell->latchCheckEnableTrans(check_set); + return latchDesc(enable_tr); +} + +void +ReportPath::reportBorrowing(const PathEndLatchCheck *end, + Arrival &borrow, + Arrival &time_given_to_startpoint, + string &result) +{ + Delay open_latency, latency_diff, max_borrow; + float nom_pulse_width, open_uncertainty; + Crpr open_crpr, crpr_diff; + 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); + result += "Time Borrowing Information\n"; + reportDashLineTotal(result); + if (borrow_limit_exists) + reportLineTotal("user max time borrow", max_borrow, early_late, result); + else { + 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, result); + if (!fuzzyZero(latency_diff)) + reportLineTotalMinus("clock latency difference", latency_diff, + early_late, result); + } + else { + auto width_msg = stdstrPrint("%s pulse width", tgt_clk_name.c_str()); + reportLineTotal(width_msg.c_str(), tgt_clk_width, early_late, result); + } + ArcDelay margin = end->margin(this); + reportLineTotalMinus("library setup time", margin, early_late, result); + reportDashLineTotal(result); + if (!fuzzyZero(crpr_diff)) + reportLineTotalMinus("CRPR difference", crpr_diff, early_late, result); + reportLineTotal("max time borrow", max_borrow, early_late, result); + } + if (fuzzyGreater(borrow, delay_zero) + && (!fuzzyZero(open_uncertainty) + || !fuzzyZero(open_crpr))) { + reportDashLineTotal(result); + reportLineTotal("actual time borrow", borrow, early_late, result); + if (!fuzzyZero(open_uncertainty)) + reportLineTotal("open edge uncertainty", open_uncertainty, + early_late, result); + if (!fuzzyZero(open_crpr)) + reportLineTotal("open edge CRPR", open_crpr, early_late, result); + reportDashLineTotal(result); + reportLineTotal("time given to startpoint", time_given_to_startpoint, + early_late, result); + } + else + reportLineTotal("actual time borrow", borrow, early_late, result); + reportDashLineTotal(result); +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportShort(const PathEndPathDelay *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + reportShort(end, expanded, result); +} + +void +ReportPath::reportShort(const PathEndPathDelay *end, + PathExpanded &expanded, + string &result) +{ + reportStartpoint(end, expanded, result); + if (end->targetClk(this)) + reportEndpoint(end, result); + else + reportUnclockedEndpoint(end, "internal path endpoint", result); + reportGroup(end, result); +} + +void +ReportPath::reportEndpoint(const PathEndPathDelay *end, + string &result) +{ + 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, result); +} + +void +ReportPath::reportFull(const PathEndPathDelay *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + reportShort(end, expanded, result); + const EarlyLate *early_late = end->pathEarlyLate(this); + + // Based on reportSrcPathArrival. + reportEndOfLine(result); + PathDelay *path_delay = end->pathDelay(); + if (path_delay->ignoreClkLatency()) { + // Based on reportSrcPath. + reportPathHeader(result); + reportPath3(end->path(), expanded, false, false, 0.0, + end->sourceClkOffset(this), result); + } + else + reportSrcPath(end, expanded, result); + reportLine("data arrival time", end->dataArrivalTimeOffset(this), + early_late, result); + reportEndOfLine(result); + + ArcDelay margin = end->margin(this); + MinMax *min_max = path_delay->minMax()->asMinMax(); + if (min_max == MinMax::max()) + margin = -margin; + + const char *min_max_str = min_max->asString(); + auto delay_msg = stdstrPrint("%s_delay", min_max_str); + float delay = path_delay->delay(); + reportLine(delay_msg.c_str(), delay, delay, early_late, result); + if (!path_delay->ignoreClkLatency()) { + const Path *tgt_clk_path = end->targetClkPath(); + if (tgt_clk_path) { + float delay = 0.0; + if (path_delay) + delay = path_delay->delay(); + if (reportClkPath() + && isPropagated(tgt_clk_path)) + reportTgtClk(end, delay, result); + else { + Arrival tgt_clk_delay = end->targetClkDelay(this); + Arrival tgt_clk_arrival = delay + tgt_clk_delay; + if (!fuzzyZero(tgt_clk_delay)) + reportLine(clkNetworkDelayIdealProp(isPropagated(tgt_clk_path)), + tgt_clk_delay, tgt_clk_arrival, early_late, result); + reportClkUncertainty(end, tgt_clk_arrival, result); + reportCommonClkPessimism(end, tgt_clk_arrival, result); + } + } + } + if (end->pathDelayMarginIsExternal()) + reportRequired(end, "output external delay", result); + else + reportRequired(end, checkRoleString(end), result); + reportSlack(end, result); +} + +bool +ReportPath::isPropagated(const Path *clk_path) +{ + return clk_path->clkInfo(search_)->isPropagated(); +} + +bool +ReportPath::isPropagated(const Path *clk_path, + Clock *clk) +{ + if (clk_path) + return clk_path->clkInfo(search_)->isPropagated(); + else + return clk->isPropagated(); +} + +const char * +ReportPath::clkNetworkDelayIdealProp(bool is_prop) +{ + if (is_prop) + return "clock network delay (propagated)"; + else + return "clock network delay (ideal)"; +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportShort(const PathEndOutputDelay *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + reportShort(end, expanded, result); +} + +void +ReportPath::reportShort(const PathEndOutputDelay *end, + PathExpanded &expanded, + string &result) +{ + reportStartpoint(end, expanded, result); + reportEndpoint(end, result); + reportGroup(end, result); +} + +void +ReportPath::reportFull(const PathEndOutputDelay *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + reportShort(end, expanded, result); + reportSrcPathArrival(end, expanded, result); + reportTgtClk(end, result); + reportRequired(end, "output external delay", result); + reportSlack(end, result); +} + +void +ReportPath::reportEndpoint(const PathEndOutputDelay *end, + string &result) +{ + Vertex *vertex = end->vertex(this); + Pin *pin = vertex->pin(); + const char *pin_name = cmd_network_->pathName(pin); + 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()); + reportEndpoint(pin_name, reason, result); + } + else + reportEndpoint(pin_name, "output port", result); + } + else { + if (tgt_clk) { + string clk_name = tgtClkName(end); + auto reason = stdstrPrint("internal path endpoint clocked by %s", clk_name.c_str()); + reportEndpoint(pin_name, reason, result); + } + else + reportEndpoint(pin_name, "internal path endpoint", result); + } +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportShort(const PathEndGatedClock *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + reportShort(end, expanded, result); +} + +void +ReportPath::reportShort(const PathEndGatedClock *end, + PathExpanded &expanded, + string &result) +{ + reportStartpoint(end, expanded, result); + reportEndpoint(end, result); + reportGroup(end, result); +} + +void +ReportPath::reportFull(const PathEndGatedClock *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + reportShort(end, expanded, result); + reportSrcPathArrival(end, expanded, result); + reportTgtClk(end, result); + reportRequired(end, checkRoleReason(end), result); + reportSlack(end, result); +} + +void +ReportPath::reportEndpoint(const PathEndGatedClock *end, + string &result) +{ + Instance *inst = network_->instance(end->vertex(this)->pin()); + const char *inst_name = cmd_network_->pathName(inst); + string clk_name = tgtClkName(end); + const TransRiseFall *clk_end_tr = end->targetClkEndTrans(this); + const TransRiseFall *clk_tr = + (end->minMax(this) == MinMax::max()) ? clk_end_tr : clk_end_tr->opposite(); + const char *rise_fall = asRisingFalling(clk_tr); + // 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()); + reportEndpoint(inst_name, reason, result); +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportShort(const PathEndDataCheck *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + reportShort(end, expanded, result); +} + +void +ReportPath::reportShort(const PathEndDataCheck *end, + PathExpanded &expanded, + string &result) +{ + reportStartpoint(end, expanded, result); + reportEndpoint(end, result); + reportGroup(end, result); + +} + +void +ReportPath::reportFull(const PathEndDataCheck *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + reportShort(end, expanded, result); + reportSrcPathArrival(end, expanded, result); + + // Capture/target clock path reporting resembles both source (reportSrcPath) + // and target (reportTgtClk) clocks. + // It is like a source because it can be a non-clock path. + // It is like a target because crpr and uncertainty are reported. + // It is always propagated, even if the clock is ideal. + reportTgtClk(end, 0.0, true, result); + const PathVertex *data_clk_path = end->dataClkPath(); + if (!data_clk_path->isClock(this)) { + // Report the path from the clk network to the data check. + PathExpanded clk_expanded(data_clk_path, this); + float src_offset = end->sourceClkOffset(this); + Delay clk_delay = end->targetClkDelay(this); + Arrival clk_arrival = end->targetClkArrival(this); + float offset = delayAsFloat(clk_arrival - clk_delay + src_offset); + reportPath5(data_clk_path, clk_expanded, clk_expanded.startIndex(), + clk_expanded.size() - 1, + data_clk_path->clkInfo(search_)->isPropagated(), false, + // Delay to startpoint is already included. + clk_arrival + src_offset, offset, result); + } + reportRequired(end, checkRoleReason(end), result); + reportSlack(end, result); +} + +void +ReportPath::reportEndpoint(const PathEndDataCheck *end, + string &result) +{ + Instance *inst = network_->instance(end->vertex(this)->pin()); + const char *inst_name = cmd_network_->pathName(inst); + const char *tgt_clk_tr = 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_tr, + tgt_clk_name); + + reportEndpoint(inst_name, reason, result); +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportEndHeader(string &result) +{ + // Line one. + reportDescription("", result); + result += ' '; + reportField("Required", field_total_, result); + result += ' '; + reportField("Actual", field_total_, result); + reportEndOfLine(result); + + // Line two. + reportDescription("Endpoint", result); + result += ' '; + reportField("Delay", field_total_, result); + result += ' '; + reportField("Delay", field_total_, result); + result += ' '; + reportField("Slack", field_total_, result); + reportEndOfLine(result); + + reportDashLine(field_description_->width() + field_total_->width() * 3 + 3, + result); +} + +void +ReportPath::reportEndLine(PathEnd *end, + string &result) +{ + const EarlyLate *early_late = end->pathEarlyLate(this); + string endpoint = pathEndpoint(end); + reportDescription(endpoint.c_str(), result); + reportSpaceFieldDelay(end->requiredTimeOffset(this), early_late, result); + reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, result); + reportSpaceSlack(end, result); +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportSummaryHeader(string &result) +{ + reportDescription("Startpoint", result); + result += ' '; + reportDescription("Endpoint", result); + result += ' '; + reportField("Slack", field_total_, result); + reportEndOfLine(result); + reportDashLine(field_description_->width() * 2 + field_total_->width() + 1, + result); + reportEndOfLine(result); +} + +void +ReportPath::reportSummaryLine(PathEnd *end, + string &result) +{ + PathExpanded expanded(end->path(), this); + const EarlyLate *early_late = end->pathEarlyLate(this); + auto startpoint = pathStartpoint(end, expanded); + reportDescription(startpoint.c_str(), result); + result += ' '; + auto endpoint = pathEndpoint(end); + reportDescription(endpoint.c_str(), result); + if (end->isUnconstrained()) + reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, result); + else + reportSpaceFieldDelay(end->slack(this), early_late, result); + reportEndOfLine(result); +} + +string +ReportPath::pathStartpoint(PathEnd *end, + PathExpanded &expanded) +{ + PathRef *start = expanded.startPath(); + Pin *pin = start->pin(graph_); + const char *pin_name = cmd_network_->pathName(pin); + if (network_->isTopLevelPort(pin)) { + PortDirection *dir = network_->direction(pin); + return stdstrPrint("%s (%s)", 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); + } +} + +string +ReportPath::pathEndpoint(PathEnd *end) +{ + Pin *pin = end->vertex(this)->pin(); + const char *pin_name = cmd_network_->pathName(pin); + if (network_->isTopLevelPort(pin)) { + PortDirection *dir = network_->direction(pin); + return stdstrPrint("%s (%s)", 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); + } +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportSlackOnlyHeader(string &result) +{ + reportDescription("Group", result); + result += ' '; + reportField("Slack", field_total_, result); + reportEndOfLine(result); + reportDashLine(field_description_->width() + field_total_->width() + 1, + result); +} + +void +ReportPath::reportSlackOnly(PathEnd *end, + string &result) +{ + const EarlyLate *early_late = end->pathEarlyLate(this); + reportDescription(search_->pathGroup(end)->name(), result); + if (end->isUnconstrained()) + reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, result); + else + reportSpaceFieldDelay(end->slack(this), early_late, result); + reportEndOfLine(result); +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportMpwCheck(MinPulseWidthCheck *check, + bool verbose) +{ + if (verbose) { + string result; + reportVerbose(check, result); + report_->print(result); + report_->print("\n"); + } + else { + string header; + reportMpwHeaderShort(header); + report_->print(header); + string result; + reportShort(check, result); + report_->print(result); + } + report_->print("\n"); +} + +void +ReportPath::reportMpwChecks(MinPulseWidthCheckSeq *checks, + bool verbose) +{ + if (!checks->empty()) { + if (verbose) { + MinPulseWidthCheckSeq::Iterator check_iter(checks); + while (check_iter.hasNext()) { + MinPulseWidthCheck *check = check_iter.next(); + string result; + reportVerbose(check, result); + report_->print(result); + report_->print("\n"); + } + } + else { + string header; + reportMpwHeaderShort(header); + report_->print(header); + MinPulseWidthCheckSeq::Iterator check_iter(checks); + while (check_iter.hasNext()) { + MinPulseWidthCheck *check = check_iter.next(); + string result; + reportShort(check, result); + report_->print(result); + } + } + report_->print("\n"); + } +} + +void +ReportPath::reportMpwHeaderShort(string &result) +{ + reportDescription("", result); + result += ' '; + reportField("Required", field_total_, result); + result += ' '; + reportField("Actual", field_total_, result); + reportEndOfLine(result); + + reportDescription("Pin", result); + result += ' '; + reportField("Width", field_total_, result); + result += ' '; + reportField("Width", field_total_, result); + result += ' '; + reportField("Slack", field_total_, result); + reportEndOfLine(result); + reportDashLine(field_description_->width() + field_total_->width() * 3 + 3, + result); +} + +void +ReportPath::reportShort(MinPulseWidthCheck *check, + string &result) +{ + 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(), result); + reportSpaceFieldTime(check->minWidth(this), result); + reportSpaceFieldDelay(check->width(this), EarlyLate::late(), result); + reportSpaceSlack(check->slack(this), result); +} + +void +ReportPath::reportVerbose(MinPulseWidthCheck *check, + string &result) +{ + const char *pin_name = cmd_network_->pathName(check->pin(this)); + result += "Pin: "; + result += pin_name; + reportEndOfLine(result); + + result += "Check: sequential_clock_pulse_width\n"; + reportEndOfLine(result); + + reportPathHeader(result); + const EarlyLate *open_el = EarlyLate::late(); + ClockEdge *open_clk_edge = check->openClkEdge(this); + 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, result); + 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, result); + reportLine(pin_name, delay_zero, open_arrival, open_el, result); + reportLine("open edge arrival time", open_arrival, open_el, result); + reportEndOfLine(result); + + const EarlyLate *close_el = EarlyLate::late(); + ClockEdge *close_clk_edge = check->closeClkEdge(this); + 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_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, result); + Arrival close_arrival = check->closeArrival(this) + close_offset; + reportLine(clk_ideal_prop, check->closeDelay(this), close_arrival, + close_el, result); + reportLine(pin_name, delay_zero, close_arrival, close_el, result); + + if (sdc_->crprEnabled()) { + Crpr pessimism = check->commonClkPessimism(this); + close_arrival += pessimism; + reportLine("clock reconvergence pessimism", pessimism, close_arrival, + close_el, result); + } + reportLine("close edge arrival time", close_arrival, close_el, result); + + reportDashLine(result); + 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(), result); + reportLine("actual pulse width", check->width(this), EarlyLate::early(), result); + reportDashLine(result); + reportSlack(check->slack(this), result); +} + +const char * +ReportPath::mpwCheckHiLow(MinPulseWidthCheck *check) +{ + if (check->openTransition(this) == TransRiseFall::rise()) + return "high"; + else + return "low"; +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportCheck(MinPeriodCheck *check, + bool verbose) +{ + if (verbose) { + string result; + reportVerbose(check, result); + report_->print(result); + report_->print("\n"); + } + else { + string header; + reportPeriodHeaderShort(header); + report_->print(header); + string result; + reportShort(check, result); + report_->print(result); + } + report_->print("\n"); +} + +void +ReportPath::reportChecks(MinPeriodCheckSeq *checks, + bool verbose) +{ + if (!checks->empty()) { + if (verbose) { + MinPeriodCheckSeq::Iterator check_iter(checks); + while (check_iter.hasNext()) { + MinPeriodCheck *check = check_iter.next(); + string result; + reportVerbose(check, result); + report_->print(result); + report_->print("\n"); + } + } + else { + string header; + reportPeriodHeaderShort(header); + report_->print(header); + MinPeriodCheckSeq::Iterator check_iter(checks); + while (check_iter.hasNext()) { + MinPeriodCheck *check = check_iter.next(); + string result; + reportShort(check, result); + report_->print(result); + } + } + report_->print("\n"); + } +} + +void +ReportPath::reportPeriodHeaderShort(string &result) +{ + reportDescription("", result); + result += ' '; + reportField("", field_total_, result); + result += ' '; + reportField("Min", field_total_, result); + result += ' '; + reportField("", field_total_, result); + reportEndOfLine(result); + + reportDescription("Pin", result); + result += ' '; + reportField("Period", field_total_, result); + result += ' '; + reportField("Period", field_total_, result); + result += ' '; + reportField("Slack", field_total_, result); + reportEndOfLine(result); + reportDashLine(field_description_->width() + field_total_->width() * 3 + 3, + result); +} + +void +ReportPath::reportShort(MinPeriodCheck *check, + string &result) +{ + const char *pin_name = cmd_network_->pathName(check->pin()); + reportDescription(pin_name, result); + reportSpaceFieldDelay(check->period(), EarlyLate::early(), result); + reportSpaceFieldDelay(check->minPeriod(this), EarlyLate::early(), result); + reportSpaceSlack(check->slack(this), result); +} + +void +ReportPath::reportVerbose(MinPeriodCheck *check, string &result) +{ + const char *pin_name = cmd_network_->pathName(check->pin()); + result += "Pin: "; + result += pin_name; + reportEndOfLine(result); + + reportLine("Period", check->period(), EarlyLate::early(), result); + reportLine("min_period", -check->minPeriod(this), + EarlyLate::early(), result); + reportDashLine(result); + reportSlack(check->slack(this), result); +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportCheck(MaxSkewCheck *check, + bool verbose) +{ + if (verbose) { + string result; + reportVerbose(check, result); + report_->print(result); + report_->print("\n"); + } + else { + string header; + reportMaxSkewHeaderShort(header); + report_->print(header); + string result; + reportShort(check, result); + report_->print(result); + } + report_->print("\n"); +} + +void +ReportPath::reportChecks(MaxSkewCheckSeq *checks, + bool verbose) +{ + if (!checks->empty()) { + if (verbose) { + MaxSkewCheckSeq::Iterator check_iter(checks); + while (check_iter.hasNext()) { + MaxSkewCheck *check = check_iter.next(); + string result; + reportVerbose(check, result); + report_->print(result); + report_->print("\n"); + } + } + else { + string header; + reportMaxSkewHeaderShort(header); + report_->print(header); + MaxSkewCheckSeq::Iterator check_iter(checks); + while (check_iter.hasNext()) { + MaxSkewCheck *check = check_iter.next(); + string result; + reportShort(check, result); + report_->print(result); + } + } + report_->print("\n"); + } +} + +void +ReportPath::reportMaxSkewHeaderShort(string &result) +{ + reportDescription("", result); + result += ' '; + reportField("Required", field_total_, result); + result += ' '; + reportField("Actual", field_total_, result); + result += ' '; + reportField("", field_total_, result); + reportEndOfLine(result); + + reportDescription("Pin", result); + result += ' '; + reportField("Skew", field_total_, result); + result += ' '; + reportField("Skew", field_total_, result); + result += ' '; + reportField("Slack", field_total_, result); + reportEndOfLine(result); + reportDashLine(field_description_->width() + field_total_->width() * 3 + 3, + result); +} + +void +ReportPath::reportShort(MaxSkewCheck *check, + string &result) +{ + 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->fromTrans()->asString(), + check_arc->toTrans()->asString()); + reportDescription(what.c_str(), result); + const EarlyLate *early_late = EarlyLate::early(); + reportSpaceFieldDelay(check->maxSkew(this), early_late, result); + reportSpaceFieldDelay(check->skew(this), early_late, result); + reportSpaceSlack(check->slack(this), result); +} + +void +ReportPath::reportVerbose(MaxSkewCheck *check, + string &result) +{ + const char *clk_pin_name = cmd_network_->pathName(check->clkPin(this)); + result += "Constrained Pin: "; + result += clk_pin_name; + reportEndOfLine(result); + + const char *ref_pin_name = cmd_network_->pathName(check->refPin(this)); + result += "Reference Pin: "; + result += ref_pin_name; + reportEndOfLine(result); + + result += "Check: max_skew"; + reportEndOfLine(result); + reportEndOfLine(result); + + reportPathHeader(result); + reportSkewClkPath("reference pin arrival time", check->refPath(), result); + reportSkewClkPath("constrained pin arrival time", check->clkPath(), result); + + reportDashLine(result); + reportLine("allowable skew", check->maxSkew(this), + EarlyLate::early(), result); + reportLine("actual skew", check->skew(this), EarlyLate::late(), result); + reportDashLine(result); + reportSlack(check->slack(this), result); +} + +// Based on reportTgtClk. +void +ReportPath::reportSkewClkPath(const char *arrival_msg, + const PathVertex *clk_path, + string &result) +{ + ClockEdge *clk_edge = clk_path->clkEdge(this); + Clock *clk = clk_edge->clock(); + const EarlyLate *early_late = clk_path->minMax(this); + const TransRiseFall *clk_tr = clk_edge->transition(); + const TransRiseFall *clk_end_tr = clk_path->transition(this); + string clk_name = clkName(clk, clk_end_tr != clk_tr); + 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(); + Vertex *clk_vertex = clk_path->vertex(this); + reportClkLine(clk, clk_name.c_str(), clk_end_tr, clk_time, min_max, result); + + bool is_prop = isPropagated(clk_path); + if (is_prop && reportClkPath()) { + const EarlyLate *early_late = TimingRole::skew()->tgtClkEarlyLate(); + if (reportGenClkSrcPath(clk_path, clk, clk_tr, min_max, early_late)) + reportGenClkSrcAndPath(clk_path, clk, clk_tr, early_late, path_ap, + 0.0, 0.0, false, result); + else { + Arrival insertion, latency; + PathEnd::checkTgtClkDelay(clk_path, clk_edge, TimingRole::skew(), this, + insertion, latency); + reportClkSrcLatency(insertion, clk_time, early_late, result); + PathExpanded clk_expanded(clk_path, this); + reportPath2(clk_path, clk_expanded, false, 0.0, result); + } + } + else { + reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, clk_arrival, + early_late, result); + reportLine(descriptionField(clk_vertex).c_str(), clk_arrival, + early_late, clk_end_tr, result); + } + reportLine(arrival_msg, search_->clkPathArrival(clk_path), + early_late, result); + reportEndOfLine(result); +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportSlewLimitShortHeader() +{ + string result; + reportSlewLimitShortHeader(result); + report_->print(result); +} + +void +ReportPath::reportSlewLimitShortHeader(string &result) +{ + reportDescription("Pin", result); + result += ' '; + reportField("Limit", field_slew_, result); + result += ' '; + reportField("Trans", field_slew_, result); + result += ' '; + reportField("Slack", field_slew_, result); + reportEndOfLine(result); + reportDashLine(field_description_->width() + field_slew_->width() * 3 + 3, + result); +} + +void +ReportPath::reportSlewLimitShort(Pin *pin, + const TransRiseFall *tr, + Slew slew, + float limit, + float slack) +{ + string result; + reportSlewLimitShort(pin, tr, slew, limit, slack, result); + report_->print(result); +} + +void +ReportPath::reportSlewLimitShort(Pin *pin, + const TransRiseFall *, + Slew slew, + float limit, + float slack, + string &result) +{ + const char *pin_name = cmd_network_->pathName(pin); + reportDescription(pin_name, result); + reportSpaceFieldTime(limit, result); + reportSpaceFieldDelay(slew, EarlyLate::late(), result); + reportSpaceSlack(slack, result); +} + +void +ReportPath::reportSlewLimitVerbose(Pin *pin, + const Corner *corner, + const TransRiseFall *tr, + Slew slew, + float limit, + float slack, + const MinMax *min_max) +{ + string result; + reportSlewLimitVerbose(pin, corner, tr, slew, limit, slack, min_max, result); + report_->print(result); +} + +void +ReportPath::reportSlewLimitVerbose(Pin *pin, + const Corner *, + const TransRiseFall *tr, + Slew slew, + float limit, + float slack, + const MinMax *min_max, + string &result) +{ + result += "Pin "; + result += cmd_network_->pathName(pin); + result += ' '; + result += tr->shortName(); + reportEndOfLine(result); + + result += min_max->asString(); + result += "_transition "; + reportSpaceFieldTime(limit, result); + reportEndOfLine(result); + + result += "transition_time "; + reportField(delayAsFloat(slew), field_slew_, result); + reportEndOfLine(result); + + reportDashLine(strlen("transition_time") + field_slew_->width() + 1, + result); + + result += "Slack "; + reportSpaceSlack(slack, result); +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportStartpoint(const PathEnd *end, + PathExpanded &expanded, + string &result) +{ + const Path *path = end->path(); + PathRef *start = expanded.startPath(); + TimingArc *prev_arc = expanded.startPrevArc(); + Edge *prev_edge = start->prevEdge(prev_arc, this); + Pin *pin = start->pin(graph_); + ClockEdge *clk_edge = path->clkEdge(this); + Clock *clk = path->clock(search_); + const char *pin_name = cmd_network_->pathName(pin); + if (pathFromClkPin(path, pin)) { + const char *clk_name = clk->name(); + auto reason = stdstrPrint("clock source '%s'", clk_name); + reportStartpoint(pin_name, reason, result); + } + else if (network_->isTopLevelPort(pin)) { + if (clk + && clk != sdc_->defaultArrivalClock()) { + const char *clk_name = clk->name(); + // Pin direction is "input" even for bidirects. + auto reason = stdstrPrint("input port clocked by %s", clk_name); + reportStartpoint(pin_name, reason, result); + } + else + reportStartpoint(pin_name, "input port", result); + } + else if (network_->isLeaf(pin) && prev_arc) { + Instance *inst = network_->instance(pin); + const char *inst_name = cmd_network_->pathName(inst); + if (clk_edge) { + const TransRiseFall *clk_tr = clk_edge->transition(); + PathRef clk_path; + expanded.clkPath(clk_path); + bool clk_inverted = !clk_path.isNull() + && clk_tr != 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()); + reportStartpoint(inst_name, reason, result); + } + else { + const char *reg_desc = edgeRegLatchDesc(prev_edge, prev_arc); + reportStartpoint(inst_name, reg_desc, result); + } + } + 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, result); + } + else + reportStartpoint(pin_name, "internal path startpoint", result); + } + else + reportStartpoint(pin_name, "internal pin", result); + } + else + reportStartpoint(pin_name, "", result); +} + +bool +ReportPath::pathFromClkPin(PathExpanded &expanded) +{ + PathRef *start = expanded.startPath(); + PathRef *end = expanded.endPath(); + const Pin *start_pin = start->pin(graph_); + return pathFromClkPin(end, start_pin); +} + +bool +ReportPath::pathFromClkPin(const Path *path, + const Pin *start_pin) +{ + Clock *clk = path->clock(search_); + return clk + && clk->vertexPins() + && clk->vertexPins()->hasKey(const_cast(start_pin)); +} + +void +ReportPath::reportStartpoint(const char *start, + string reason, + string &result) +{ + reportStartEndPoint(start, reason, "Startpoint", result); +} + +void +ReportPath::reportUnclockedEndpoint(const PathEnd *end, + const char *default_reason, + string &result) +{ + Vertex *vertex = end->vertex(this); + Pin *pin = vertex->pin(); + if (network_->isTopLevelPort(pin)) { + // Pin direction is "output" even for bidirects. + reportEndpoint(cmd_network_->pathName(pin), "output port", result); + } + else if (network_->isLeaf(pin)) { + VertexInEdgeIterator edge_iter(vertex, graph_); + 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, result); + 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, result); + return; + } + } + } + } + reportEndpoint(cmd_network_->pathName(pin), default_reason, result); + } + else + reportEndpoint(cmd_network_->pathName(pin), "", result); +} + +void +ReportPath::reportEndpoint(const char *end, + string reason, + string &result) +{ + reportStartEndPoint(end, reason, "Endpoint", result); +} + +void +ReportPath::reportStartEndPoint(const char *pt, + string reason, + const char *key, + string &result) +{ + // Account for punctuation in the line. + int line_len = strlen(key) + 2 + strlen(pt) + 2 + reason.size() + 1; + if (!no_split_ + && line_len > start_end_pt_width_) { + result += key; + result += ": "; + result += pt; + reportEndOfLine(result); + + for (unsigned i = 0; i < strlen(key); i++) + result += ' '; + + result += " ("; + result += reason; + result += ")\n"; + } + else { + result += key; + result += ": "; + result += pt; + result += " ("; + result += reason; + result += ")\n"; + } +} + +void +ReportPath::reportGroup(const PathEnd *end, + string &result) +{ + result += "Path Group: "; + result += search_->pathGroup(end)->name(); + reportEndOfLine(result); + result += "Path Type: "; + result += end->minMax(this)->asString(); + reportEndOfLine(result); + if (corners_->multiCorner()) { + result += "Corner: "; + result += end->pathAnalysisPt(this)->corner()->name(); + reportEndOfLine(result); + } +} + +//////////////////////////////////////////////////////////////// + +string +ReportPath::checkRoleReason(const PathEnd *end) +{ + const char *setup_hold = end->checkRole(this)->asString(); + return stdstrPrint("%s time", setup_hold); +} + +string +ReportPath::tgtClkName(const PathEnd *end) +{ + ClockEdge *tgt_clk_edge = end->targetClkEdge(this); + const Clock *tgt_clk = tgt_clk_edge->clock(); + const TransRiseFall *clk_tr = tgt_clk_edge->transition(); + const TransRiseFall *clk_end_tr = end->targetClkEndTrans(this); + return clkName(tgt_clk, clk_end_tr != clk_tr); +} + +string +ReportPath::clkName(const Clock *clk, + bool inverted) +{ + string name = clk->name(); + if (inverted) + name += '\''; + return name; +} + +const char * +ReportPath::clkRegLatchDesc(const PathEnd *end) +{ + // Goofy libraries can have registers with both rising and falling + // clk->q timing arcs. Try and match the timing check transition. + const TransRiseFall *check_clk_tr=end->checkArc()->fromTrans()->asRiseFall(); + TimingArcSet *clk_set = nullptr; + TimingArcSet *clk_tr_set = nullptr; + Vertex *tgt_clk_vertex = end->targetClkPath()->vertex(this); + VertexOutEdgeIterator iter(tgt_clk_vertex, graph_); + while (iter.hasNext()) { + Edge *edge = iter.next(); + TimingArcSet *arc_set = edge->timingArcSet(); + TimingRole *role = arc_set->role(); + if (role == TimingRole::regClkToQ() + || role == TimingRole::latchEnToQ()) { + TransRiseFall *arc_tr = arc_set->isRisingFallingEdge(); + clk_set = arc_set; + if (arc_tr == check_clk_tr) + clk_tr_set = arc_set; + } + } + if (clk_tr_set) + return checkRegLatchDesc(clk_tr_set->role(), + clk_tr_set->isRisingFallingEdge()); + else if (clk_set) + return checkRegLatchDesc(clk_set->role(), + clk_set->isRisingFallingEdge()); + else + return checkRegLatchDesc(TimingRole::regClkToQ(), check_clk_tr); +} + +void +ReportPath::reportSrcPathArrival(const PathEnd *end, + PathExpanded &expanded, + string &result) +{ + reportEndOfLine(result); + reportSrcPath(end, expanded, result); + reportLine("data arrival time", end->dataArrivalTimeOffset(this), + end->pathEarlyLate(this), result); + reportEndOfLine(result); +} + +void +ReportPath::reportSrcPath(const PathEnd *end, + PathExpanded &expanded, + string &result) +{ + reportPathHeader(result); + float src_clk_offset = end->sourceClkOffset(this); + Arrival src_clk_insertion = end->sourceClkInsertionDelay(this); + 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(), result); +} + +void +ReportPath::reportSrcClkAndPath(const Path *path, + PathExpanded &expanded, + float time_offset, + Arrival clk_insertion, + Arrival clk_latency, + bool is_path_delay, + string &result) +{ + ClockEdge *clk_edge = path->clkEdge(this); + const MinMax *min_max = path->minMax(this); + if (clk_edge) { + Clock *clk = clk_edge->clock(); + TransRiseFall *clk_tr = clk_edge->transition(); + float clk_time = clk_edge->time() + time_offset; + 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, result); + reportLine(clkNetworkDelayIdealProp(false), 0.0, clk_end_time, + early_late, result); + } + reportPath1(path, expanded, false, time_offset, result); + } + else { + bool path_from_input = false; + bool input_has_ref_path = false; + Arrival clk_delay, clk_end_time; + PathRef clk_path; + expanded.clkPath(clk_path); + const TransRiseFall *clk_end_tr; + if (!clk_path.isNull()) { + clk_end_time = search_->clkPathArrival(&clk_path) + time_offset; + clk_delay = clk_end_time - clk_time; + clk_end_tr = clk_path.transition(this); + } + else { + // Path from input port or clk used as data. + clk_end_tr = clk_tr; + clk_delay = clk_insertion + clk_latency; + clk_end_time = clk_time + clk_delay; + + PathRef *first_path = expanded.startPath(); + InputDelay *input_delay = pathInputDelay(first_path); + if (input_delay) { + path_from_input = true; + Pin *ref_pin = input_delay->refPin(); + if (ref_pin && clk->isPropagated()) { + PathRef ref_path; + pathInputDelayRefPath(first_path, input_delay, ref_path); + if (!ref_path.isNull()) { + const Arrival &ref_end_time = ref_path.arrival(this); + clk_delay = ref_end_time - clk_time; + clk_end_time = ref_end_time + time_offset; + input_has_ref_path = true; + } + } + } + } + string clk_name = clkName(clk, clk_tr != clk_end_tr); + + bool clk_used_as_data = pathFromClkPin(expanded); + bool is_prop = isPropagated(path); + const EarlyLate *early_late = min_max; + if (reportGenClkSrcPath(clk_path.isNull() ? nullptr : &clk_path, + clk, clk_tr, min_max, early_late) + && !(path_from_input && !input_has_ref_path)) { + reportClkLine(clk, clk_name.c_str(), clk_end_tr, clk_time, + min_max, result); + const PathAnalysisPt *path_ap = path->pathAnalysisPt(this); + reportGenClkSrcAndPath(path, clk, clk_tr, early_late, path_ap, + time_offset, time_offset, clk_used_as_data, + result); + } + else if (clk_used_as_data + && pathFromGenPropClk(path, path->minMax(this))) { + reportClkLine(clk, clk_name.c_str(), clk_end_tr, clk_time, + min_max, result); + ClkInfo *clk_info = path->tag(search_)->clkInfo(); + if (clk_info->isPropagated()) + reportClkSrcLatency(clk_insertion, clk_time, early_late, result); + reportPath1(path, expanded, true, time_offset, result); + } + else if (is_prop + && reportClkPath() + && !(path_from_input && !input_has_ref_path)) { + reportClkLine(clk, clk_name.c_str(), clk_end_tr, clk_time, + early_late, result); + reportClkSrcLatency(clk_insertion, clk_time, early_late, result); + reportPath1(path, expanded, false, time_offset, result); + } + else if (clk_used_as_data) { + reportClkLine(clk, clk_name.c_str(), clk_end_tr, clk_time, + early_late, result); + if (clk_insertion > 0.0) + reportClkSrcLatency(clk_insertion, clk_time, early_late, result); + reportPath1(path, expanded, true, time_offset, result); + } + else { + if (is_path_delay) { + if (clk_delay > 0.0) + reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, + clk_end_time, early_late, result); + } + else { + reportClkLine(clk, clk_name.c_str(), clk_end_tr, clk_time, + min_max, result); + Arrival clk_arrival = clk_end_time; + reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, + clk_arrival, early_late, result); + } + reportPath1(path, expanded, false, time_offset, result); + } + } + } + else + reportPath1(path, expanded, false, time_offset, result); +} + +void +ReportPath::reportTgtClk(const PathEnd *end, + string &result) +{ + reportTgtClk(end, 0.0, result); +} + +void +ReportPath::reportTgtClk(const PathEnd *end, + float prev_time, + string &result) +{ + Clock *clk = end->targetClk(this); + const Path *clk_path = end->targetClkPath(); + reportTgtClk(end, prev_time, isPropagated(clk_path, clk), result); +} + +void +ReportPath::reportTgtClk(const PathEnd *end, + float prev_time, + bool is_prop, + string &result) +{ + float src_offset = end->sourceClkOffset(this); + const ClockEdge *clk_edge = end->targetClkEdge(this); + Clock *clk = clk_edge->clock(); + const TransRiseFall *clk_tr = clk_edge->transition(); + const TransRiseFall *clk_end_tr = end->targetClkEndTrans(this); + string clk_name = clkName(clk, clk_end_tr != clk_tr); + 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(); + const Path *clk_path = end->targetClkPath(); + reportClkLine(clk, clk_name.c_str(), clk_end_tr, prev_time, clk_time, + min_max, result); + 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_tr, min_max, early_late)) { + float insertion_offset = + clk_path ? tgtClkInsertionOffet(clk_path, early_late, path_ap) : 0.0; + reportGenClkSrcAndPath(clk_path, clk, clk_tr, early_late, path_ap, + time_offset, time_offset + insertion_offset, + false, result); + } + else { + Arrival insertion = end->targetClkInsertionDelay(this); + if (clk_path) { + reportClkSrcLatency(insertion, clk_time, early_late, result); + PathExpanded clk_expanded(clk_path, this); + float insertion_offset = tgtClkInsertionOffet(clk_path, early_late, + path_ap); + reportPath5(clk_path, clk_expanded, 0, clk_expanded.size() - 1, is_prop, + reportClkPath(), delay_zero, time_offset + insertion_offset, + result); + } + else { + // Output departure. + Arrival clk_arrival = clk_time + clk_delay; + reportLine(clkNetworkDelayIdealProp(clk->isPropagated()), + clk_delay, clk_arrival, min_max, result); + } + } + reportClkUncertainty(end, clk_arrival, result); + reportCommonClkPessimism(end, clk_arrival, result); + } + else { + reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, clk_arrival, + min_max, result); + reportClkUncertainty(end, clk_arrival, result); + reportCommonClkPessimism(end, clk_arrival, result); + 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_tr, result); + } + } +} + +float +ReportPath::tgtClkInsertionOffet(const Path *clk_path, + const EarlyLate *early_late, + PathAnalysisPt *path_ap) +{ + 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 TransRiseFall *clk_tr = clk_edge->transition(); + const MinMax *min_max = path_ap->pathMinMax(); + Arrival path_insertion = search_->clockInsertion(clk, src_pin, clk_tr, + min_max, min_max, + path_ap); + Arrival tgt_insertion = search_->clockInsertion(clk, src_pin, clk_tr, + min_max, early_late, + path_ap); + return delayAsFloat(tgt_insertion - path_insertion); +} + +bool +ReportPath::pathFromGenPropClk(const Path *clk_path, + const EarlyLate *early_late) +{ + ClkInfo *clk_info = clk_path->tag(search_)->clkInfo(); + const ClockEdge *clk_edge = clk_info->clkEdge(); + if (clk_edge) { + 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); + return !exists + && clk->isGeneratedWithPropagatedMaster(); + } + else + return false; +} + +bool +ReportPath::isGenPropClk(const Clock *clk, + const TransRiseFall *clk_tr, + const MinMax *min_max, + const EarlyLate *early_late) +{ + float insertion; + bool exists; + sdc_->clockInsertion(clk, clk->srcPin(), clk_tr, + min_max, early_late, + insertion, exists); + return !exists + && clk->isGeneratedWithPropagatedMaster(); +} + +void +ReportPath::reportClkLine(const Clock *clk, + const char *clk_name, + const TransRiseFall *clk_tr, + Arrival clk_time, + const MinMax *min_max, + string &result) +{ + reportClkLine(clk, clk_name, clk_tr, 0.0, clk_time, min_max, result); +} + +void +ReportPath::reportClkLine(const Clock *clk, + const char *clk_name, + const TransRiseFall *clk_tr, + Arrival prev_time, + Arrival clk_time, + const MinMax *min_max, + string &result) +{ + const char *rise_fall = asRiseFall(clk_tr); + auto clk_msg = stdstrPrint("clock %s (%s edge)", clk_name, rise_fall); + if (clk->isPropagated()) + reportLine(clk_msg.c_str(), clk_time - prev_time, clk_time, min_max, result); + else { + // Report ideal clock slew. + float clk_slew = clk->slew(clk_tr, min_max); + reportLine(clk_msg.c_str(), clk_slew, clk_time - prev_time, clk_time, + min_max, result); + } +} + +bool +ReportPath::reportGenClkSrcPath(const Path *clk_path, + Clock *clk, + const TransRiseFall *clk_tr, + const MinMax *min_max, + const EarlyLate *early_late) +{ + bool from_gen_prop_clk = clk_path + ? pathFromGenPropClk(clk_path, early_late) + : isGenPropClk(clk, clk_tr, min_max, early_late); + return from_gen_prop_clk + && format_ == ReportPathFormat::full_clock_expanded; +} + +void +ReportPath::reportGenClkSrcAndPath(const Path *path, + Clock *clk, + const TransRiseFall *clk_tr, + const EarlyLate *early_late, + const PathAnalysisPt *path_ap, + float time_offset, + float path_time_offset, + bool clk_used_as_data, + string &result) +{ + const Pin *clk_pin = path + ? path->clkInfo(search_)->clkSrc() + : clk->defaultPin(); + float gclk_time = clk->edge(clk_tr)->time() + time_offset; + bool skip_first_path = reportGenClkSrcPath1(clk, clk_pin, clk_tr, + early_late, path_ap, gclk_time, + time_offset, clk_used_as_data, + result); + if (path) { + PathExpanded expanded(path, this); + reportPath4(path, expanded, skip_first_path, false, clk_used_as_data, + path_time_offset, result); + } +} + +bool +ReportPath::reportGenClkSrcPath1(Clock *clk, + const Pin *clk_pin, + const TransRiseFall *clk_tr, + const EarlyLate *early_late, + const PathAnalysisPt *path_ap, + float gclk_time, + float time_offset, + bool clk_used_as_data, + string &result) +{ + PathAnalysisPt *insert_ap = path_ap->insertionAnalysisPt(early_late); + PathVertex src_path; + const MinMax *min_max = path_ap->pathMinMax(); + search_->genclks()->srcPath(clk, clk_pin, clk_tr, insert_ap, src_path); + if (!src_path.isNull()) { + ClkInfo *src_clk_info = src_path.clkInfo(search_); + ClockEdge *src_clk_edge = src_clk_info->clkEdge(); + Clock *src_clk = src_clk_info->clock(); + bool skip_first_path = false; + const TransRiseFall *src_clk_tr = src_clk_edge->transition(); + const Pin *src_clk_pin = src_clk_info->clkSrc(); + if (src_clk->isGeneratedWithPropagatedMaster() + && src_clk_info->isPropagated()) { + skip_first_path = reportGenClkSrcPath1(src_clk, src_clk_pin, + src_clk_tr, early_late, path_ap, + gclk_time, time_offset, + clk_used_as_data, result); + } + else { + const Arrival insertion = search_->clockInsertion(src_clk, src_clk_pin, + src_clk_tr, + path_ap->pathMinMax(), + early_late, path_ap); + reportClkSrcLatency(insertion, gclk_time, early_late, result); + } + PathExpanded src_expanded(&src_path, this); + if (clk->pllOut()) { + reportPath4(&src_path, src_expanded, skip_first_path, true, + clk_used_as_data, gclk_time, result); + PathAnalysisPt *pll_ap=path_ap->insertionAnalysisPt(min_max->opposite()); + Arrival pll_delay = search_->genclks()->pllDelay(clk, clk_tr, pll_ap); + size_t path_length = src_expanded.size(); + if (path_length < 2) + internalError("generated clock pll source path too short.\n"); + PathRef *path0 = src_expanded.path(path_length - 2); + Arrival time0 = path0->arrival(this) + gclk_time; + PathRef *path1 = src_expanded.path(path_length - 1); + reportPathLine(path1, -pll_delay, time0 - pll_delay, "pll_delay", result); + } + else + reportPath4(&src_path, src_expanded, skip_first_path, false, + clk_used_as_data, gclk_time, result); + if (!clk->isPropagated()) + reportLine("clock network delay (ideal)", 0.0, src_path.arrival(this), + min_max, result); + } + else { + if (clk->isPropagated()) + reportClkSrcLatency(0.0, gclk_time, early_late, result); + else if (!clk_used_as_data) + reportLine("clock network delay (ideal)", 0.0, gclk_time, + min_max, result); + } + return !src_path.isNull(); +} + +void +ReportPath::reportClkSrcLatency(Arrival insertion, + float clk_time, + const EarlyLate *early_late, + string &result) +{ + reportLine("clock source latency", insertion, clk_time + insertion, + early_late, result); +} + +void +ReportPath::reportPathLine(const Path *path, + Arrival incr, + Arrival time, + const char *line_case, + string &result) +{ + Vertex *vertex = path->vertex(this); + Pin *pin = vertex->pin(); + auto what = descriptionField(vertex); + const TransRiseFall *tr = 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, tr, ap_index); + float cap = field_blank_; + // Don't show capacitance field for input pins. + if (is_driver && field_capacitance_->enabled()) + cap = loadCap(pin, tr, dcalc_ap); + reportLine(what.c_str(), cap, slew, field_blank_, + incr, time, false, early_late, tr, line_case, result); +} + +void +ReportPath::reportRequired(const PathEnd *end, + string margin_msg, + string &result) +{ + Required req_time = end->requiredTimeOffset(this); + const EarlyLate *early_late = end->clkEarlyLate(this); + ArcDelay margin = end->margin(this); + if (end->minMax(this) == MinMax::max()) + margin = -margin; + reportLine(margin_msg.c_str(), margin, req_time, early_late, result); + reportLine("data required time", req_time, early_late, result); + reportDashLine(result); +} + +void +ReportPath::reportSlack(const PathEnd *end, + string &result) +{ + const EarlyLate *early_late = end->pathEarlyLate(this); + reportLine("data required time", end->requiredTimeOffset(this), + early_late->opposite(), result); + reportLineNegative("data arrival time", end->dataArrivalTimeOffset(this), + early_late, result); + reportDashLine(result); + reportSlack(end->slack(this), result); +} + +void +ReportPath::reportSlack(Slack slack, + string &result) +{ + const EarlyLate *early_late = EarlyLate::early(); + const char *msg = (delayAsFloat(slack, early_late, this) >= 0.0) + ? "slack (MET)" + : "slack (VIOLATED)"; + reportLine(msg, slack, early_late, result); +} + +void +ReportPath::reportSpaceSlack(PathEnd *end, + string &result) +{ + Slack slack = end->slack(this); + reportSpaceSlack(slack, result); +} + +void +ReportPath::reportSpaceSlack(Slack slack, + string &result) +{ + const EarlyLate *early_late = EarlyLate::early(); + reportSpaceFieldDelay(slack, early_late, result); + result += (delayAsFloat(slack, early_late, this) >= 0.0) + ? " (MET)" + : " (VIOLATED)"; + reportEndOfLine(result); +} + +void +ReportPath::reportCommonClkPessimism(const PathEnd *end, + Arrival &clk_arrival, + string &result) +{ + if (sdc_->crprEnabled()) { + Crpr pessimism = end->commonClkPessimism(this); + clk_arrival += pessimism; + reportLine("clock reconvergence pessimism", pessimism, clk_arrival, + end->clkEarlyLate(this), result); + } +} + +void +ReportPath::reportClkUncertainty(const PathEnd *end, + Arrival &clk_arrival, + string &result) +{ + const EarlyLate *early_late = end->clkEarlyLate(this); + float uncertainty = end->targetNonInterClkUncertainty(this); + clk_arrival += uncertainty; + if (uncertainty != 0.0) + reportLine("clock uncertainty", uncertainty, clk_arrival, + early_late, result); + float inter_uncertainty = end->interClkUncertainty(this); + clk_arrival += inter_uncertainty; + if (inter_uncertainty != 0.0) + reportLine("inter-clock uncertainty", inter_uncertainty, clk_arrival, + early_late, result); +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportPath(const PathEnd *end, + PathExpanded &expanded, + string &result) +{ + reportPathHeader(result); + // Source clk offset for path delays removes clock phase time. + float src_clk_offset = end->sourceClkOffset(this); + reportPath1(end->path(), expanded, pathFromClkPin(expanded), + src_clk_offset, result); +} + +void +ReportPath::reportPath(const Path *path, + string &result) +{ + reportPathHeader(result); + PathExpanded expanded(path, this); + reportSrcClkAndPath(path, expanded, 0.0, delay_zero, delay_zero, + false, result); +} + +void +ReportPath::reportPath1(const Path *path, + PathExpanded &expanded, + bool clk_used_as_data, + float time_offset, + string &result) +{ + PathRef *d_path, *q_path; + Edge *d_q_edge; + expanded.latchPaths(d_path, q_path, d_q_edge); + if (d_path) { + Arrival latch_time_given, latch_enable_time; + PathVertex latch_enable_path; + latches_->latchTimeGivenToStartpoint(d_path, q_path, d_q_edge, + latch_time_given, + latch_enable_path); + if (!latch_enable_path.isNull()) { + const EarlyLate *early_late = latch_enable_path.minMax(this); + latch_enable_time = search_->clkPathArrival(&latch_enable_path); + if (reportClkPath()) { + PathExpanded enable_expanded(&latch_enable_path, this); + // Report the path to the latch enable. + reportPath2(&latch_enable_path, enable_expanded, false, + time_offset, result); + } + Arrival time = latch_enable_time + latch_time_given; + Arrival incr = latch_time_given; + if (incr >= 0.0) + reportLine("time given to startpoint", incr, time, early_late, result); + else + reportLine("time borrowed from startpoint", incr, time, + early_late, result); + // Override latch D arrival with enable + given. + reportPathLine(expanded.path(0), delay_zero, time, "latch_D", result); + bool propagated_clk = path->clkInfo(search_)->isPropagated(); + bool report_clk_path = path->isClock(search_) || reportClkPath(); + reportPath5(path, expanded, 1, expanded.size() - 1, + propagated_clk, report_clk_path, + latch_enable_time + latch_time_given, time_offset, result); + } + } + else + reportPath2(path, expanded, clk_used_as_data, time_offset, result); +} + +void +ReportPath::reportPath2(const Path *path, + PathExpanded &expanded, + bool clk_used_as_data, + float time_offset, + string &result) +{ + // Report the clock path if the end is a clock or we wouldn't have + // anything to report. + bool report_clk_path = clk_used_as_data + || (reportClkPath() + && path->clkInfo(search_)->isPropagated()); + reportPath3(path, expanded, clk_used_as_data, report_clk_path, + delay_zero, time_offset, result); +} + +void +ReportPath::reportPath3(const Path *path, + PathExpanded &expanded, + bool clk_used_as_data, + bool report_clk_path, + Arrival prev_time, + float time_offset, + string &result) +{ + bool propagated_clk = clk_used_as_data + || path->clkInfo(search_)->isPropagated(); + size_t path_last_index = expanded.size() - 1; + reportPath5(path, expanded, 0, path_last_index, propagated_clk, + report_clk_path, prev_time, time_offset, result); +} + +void +ReportPath::reportPath4(const Path *path, + PathExpanded &expanded, + bool skip_first_path, + bool skip_last_path, + bool clk_used_as_data, + float time_offset, + string &result) +{ + size_t path_first_index = 0; + Arrival prev_time(0.0); + if (skip_first_path) { + path_first_index = 1; + PathRef *start = expanded.path(0); + prev_time = start->arrival(this) + time_offset; + } + size_t path_last_index = expanded.size() - 1; + if (skip_last_path + && path_last_index > 1) + path_last_index--; + bool propagated_clk = clk_used_as_data + || path->clkInfo(search_)->isPropagated(); + // Report the clock path if the end is a clock or we wouldn't have + // anything to report. + bool report_clk_path = path->isClock(search_) + || (reportClkPath() && propagated_clk); + reportPath5(path, expanded, path_first_index, path_last_index, + propagated_clk, report_clk_path, prev_time, time_offset, result); +} + +void +ReportPath::reportPath5(const Path *path, + PathExpanded &expanded, + size_t path_first_index, + size_t path_last_index, + bool propagated_clk, + bool report_clk_path, + Arrival prev_time, + float time_offset, + string &result) +{ + const MinMax *min_max = path->minMax(this); + DcalcAnalysisPt *dcalc_ap = path->pathAnalysisPt(this)->dcalcAnalysisPt(); + DcalcAPIndex ap_index = dcalc_ap->index(); + for (size_t i = path_first_index; i <= path_last_index; i++) { + PathRef *path1 = expanded.path(i); + TimingArc *prev_arc = expanded.prevArc(i); + Vertex *vertex = path1->vertex(this); + Pin *pin = vertex->pin(); + Arrival time = path1->arrival(this) + time_offset; + float incr = 0.0; + const char *line_case = nullptr; + bool is_clk_start = network_->isRegClkPin(pin); + bool is_clk = path1->isClock(search_); + // 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) { + const TransRiseFall *tr = path1->transition(this); + Slew slew = graph_->slew(vertex, tr, ap_index); + if (prev_arc == nullptr) { + // First path. + reportInputExternalDelay(path1, time_offset, result); + size_t next_index = i + 1; + const PathRef *next_path = expanded.path(next_index); + if (network_->isTopLevelPort(pin) + && next_path + && !nextArcAnnotated(next_path, next_index, expanded, ap_index) + && hasExtInputDriver(pin, tr, 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(this) + time_offset; + incr = delayAsFloat(next_time, min_max, this) + - delayAsFloat(time, min_max, this); + 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"; + } + } + 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. + ClockEdge *src_clk_edge = path->clkEdge(this); + time = search_->clkPathArrival(path1) + time_offset; + if (src_clk_edge) { + Clock *src_clk = src_clk_edge->clock(); + TransRiseFall *src_clk_tr = src_clk_edge->transition(); + slew = src_clk->slew(src_clk_tr, 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; + ClockEdge *src_clk_edge = path->clkEdge(this); + Clock *src_clk = src_clk_edge->clock(); + TransRiseFall *src_clk_tr = src_clk_edge->transition(); + slew = src_clk->slew(src_clk_tr, min_max); + line_case = "clk_ideal"; + } + else if (is_clk && !is_clk_start) { + incr = delayAsFloat(time, min_max, this) + - delayAsFloat(prev_time, min_max, this); + line_case = "clk_prop"; + } + else { + incr = delayAsFloat(time, min_max, this) + - delayAsFloat(prev_time, min_max, this); + line_case = "normal"; + } + if (report_input_pin_ + || (i == path_last_index) + || is_clk_start + || (prev_arc == nullptr) + // Filter wire edges from report unless reporting + // input pins. + || (prev_arc + && !prev_arc->role()->isWire())) { + bool is_driver = network_->isDriver(pin); + float cap = field_blank_; + // Don't show capacitance field for input pins. + if (is_driver && field_capacitance_->enabled()) + cap = loadCap(pin, tr, dcalc_ap); + auto what = descriptionField(vertex); + if (report_net_ && is_driver) { + // Capacitance field is reported on the net line. + reportLine(what.c_str(), field_blank_, slew, field_blank_, + incr, time, false, min_max, tr, line_case, result); + string what2; + if (network_->isTopLevelPort(pin)) { + const char *pin_name = cmd_network_->pathName(pin); + what2 = stdstrPrint("%s (net)", pin_name); + } + else { + Net *net = network_->net(pin); + if (net) { + Net *highest_net = network_->highestNetAbove(net); + const char *net_name = cmd_network_->pathName(highest_net); + what2 = stdstrPrint("%s (net)", net_name); + } + else + what2 = "(unconnected)"; + } + float fanout = drvrFanout(vertex, min_max); + reportLine(what2.c_str(), cap, field_blank_, fanout, + field_blank_, field_blank_, false, min_max, nullptr, + line_case, result); + } + else + reportLine(what.c_str(), cap, slew, field_blank_, + incr, time, false, min_max, tr, line_case, result); + prev_time = time; + } + } + else + prev_time = time; + } +} + +bool +ReportPath::nextArcAnnotated(const PathRef *next_path, + size_t next_index, + PathExpanded &expanded, + DcalcAPIndex ap_index) +{ + TimingArc *arc = expanded.prevArc(next_index); + Edge *edge = next_path->prevEdge(arc, this); + return graph_->arcDelayAnnotated(edge, arc, ap_index); +} + +string +ReportPath::descriptionField(Vertex *vertex) +{ + Pin *pin = vertex->pin(); + const char *pin_name = cmd_network_->pathName(pin); + const char *name2; + if (network_->isTopLevelPort(pin)) { + PortDirection *dir = network_->direction(pin); + // Translate port direction. Note that this is intentionally + // inconsistent with the direction reported for top level ports as + // startpoints. + if (dir->isInput()) + name2 = "in"; + else if (dir->isOutput() || dir->isTristate()) + name2 = "out"; + else if (dir->isBidirect()) + name2 = "inout"; + else + name2 = "?"; + } + else { + Instance *inst = network_->instance(pin); + name2 = network_->cellName(inst); + } + return stdstrPrint("%s (%s)", pin_name, name2); +} + +float +ReportPath::drvrFanout(Vertex *drvr, + const MinMax *min_max) +{ + float fanout = 0.0; + VertexOutEdgeIterator iter(drvr, graph_); + while (iter.hasNext()) { + Edge *edge = iter.next(); + Pin *pin = edge->to(graph_)->pin(); + if (network_->isTopLevelPort(pin)) { + // Output port counts as a fanout. + Port *port = network_->port(pin); + fanout += sdc_->portExtFanout(port, min_max) + 1; + } + else + fanout++; + } + return fanout; +} + +bool +ReportPath::hasExtInputDriver(const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max) +{ + Port *port = network_->port(pin); + InputDrive *drive = sdc_->findInputDrive(port); + return (drive + && (drive->hasDriveResistance(tr, min_max) + || drive->hasDriveCell(tr, min_max))); +} + +void +ReportPath::reportInputExternalDelay(const Path *first_path, + float time_offset, + string &result) +{ + const Pin *first_pin = first_path->pin(graph_); + if (!pathFromClkPin(first_path, first_pin)) { + const TransRiseFall *tr = first_path->transition(this); + Arrival time = first_path->arrival(this) + time_offset; + 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) { + PathRef ref_path; + pathInputDelayRefPath(first_path, input_delay, ref_path); + if (!ref_path.isNull() && reportClkPath()) { + PathExpanded ref_expanded(&ref_path, this); + float ref_offset = time_offset; + ClkInfo *ref_clk_info = ref_path.clkInfo(this); + // Ref clk does not include latency for non-ideal clocks. + // Remove it to compensate for reportPath5 adding it. + if (!ref_clk_info->isPropagated()) + ref_offset -= ref_clk_info->latency(); + reportPath3(&ref_path, ref_expanded, false, true, + delay_zero, 0.0, result); + } + } + float input_arrival = + input_delay->delays()->value(tr, first_path->minMax(this)); + reportLine("input external delay", input_arrival, time, + early_late, tr, result); + } + else if (network_->isTopLevelPort(first_pin)) + reportLine("input external delay", 0.0, time, early_late, tr, result); + } +} + +// Return the input delay at the start of a path. +InputDelay * +ReportPath::pathInputDelay(const Path *first_path) const +{ + return first_path->tag(this)->inputDelay(); +} + +void +ReportPath::pathInputDelayRefPath(const Path *path, + InputDelay *input_delay, + // Return value. + PathRef &ref_path) +{ + Pin *ref_pin = input_delay->refPin(); + TransRiseFall *ref_tr = input_delay->refTransition(); + Vertex *ref_vertex = graph_->pinDrvrVertex(ref_pin); + const PathAnalysisPt *path_ap = path->pathAnalysisPt(this); + const ClockEdge *clk_edge = path->clkEdge(this); + VertexPathIterator path_iter(ref_vertex, ref_tr, path_ap, this); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + if (path->isClock(this) + && path->clkEdge(this) == clk_edge) { + ref_path.init(path); + break; + } + } +} + +float +ReportPath::loadCap(Pin *drvr_pin, + const TransRiseFall *tr, + DcalcAnalysisPt *dcalc_ap) +{ + Parasitic *parasitic = nullptr; + if (arc_delay_calc_) + parasitic = arc_delay_calc_->findParasitic(drvr_pin, tr, dcalc_ap); + return graph_delay_calc_->loadCap(drvr_pin, parasitic, tr, dcalc_ap); +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportPathHeader(string &result) +{ + ReportFieldSeq::Iterator field_iter(fields_); + bool first_field = true; + while (field_iter.hasNext()) { + ReportField *field = field_iter.next(); + if (field->enabled()) { + if (!first_field) + result += ' '; + reportField(field->title(), field, result); + first_field = false; + } + } + trimRight(result); + reportEndOfLine(result); + + reportDashLine(result); +} + +// Report total. +void +ReportPath::reportLine(const char *what, + Delay total, + const EarlyLate *early_late, + string &result) +{ + reportLine(what, field_blank_, field_blank_, field_blank_, + field_blank_, total, false, early_late, nullptr, nullptr, result); +} + +// Report negative total. +void +ReportPath::reportLineNegative(const char *what, + Delay total, + const EarlyLate *early_late, + string &result) +{ + reportLine(what, field_blank_, field_blank_, field_blank_, + field_blank_, total, true, early_late, nullptr, nullptr, result); +} + +// Report total, and transition suffix. +void +ReportPath::reportLine(const char *what, + Delay total, + const EarlyLate *early_late, + const TransRiseFall *tr, + string &result) +{ + reportLine(what, field_blank_, field_blank_, field_blank_, + field_blank_, total, false, early_late, tr, nullptr, result); +} + +// Report increment, and total. +void +ReportPath::reportLine(const char *what, + Delay incr, + Delay total, + const EarlyLate *early_late, + string &result) +{ + reportLine(what, field_blank_, field_blank_, field_blank_, + incr, total, false, early_late, nullptr, nullptr, result); +} + +// Report increment, total, and transition suffix. +void +ReportPath::reportLine(const char *what, + Delay incr, + Delay total, + const EarlyLate *early_late, + const TransRiseFall *tr, + string &result) +{ + reportLine(what, field_blank_, field_blank_, field_blank_, + incr, total, false, early_late, tr, nullptr, result); +} + +// Report slew, increment, and total. +void +ReportPath::reportLine(const char *what, + Slew slew, + Delay incr, + Delay total, + const EarlyLate *early_late, + string &result) +{ + reportLine(what, field_blank_, slew, field_blank_, + incr, total, false, early_late, nullptr, nullptr, result); +} + +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 TransRiseFall *tr, + const char *line_case, + string &result) +{ + ReportFieldSeq::Iterator field_iter(fields_); + size_t field_index = 0; + bool first_field = true; + while (field_iter.hasNext()) { + ReportField *field = field_iter.next(); + bool last_field = field_index == (fields_.size() - 1); + + if (field->enabled()) { + if (!first_field) + result += ' '; + + if (field == field_description_) + reportDescription(what, first_field, last_field, result); + else if (field == field_fanout_) { + if (fanout == field_blank_) + reportFieldBlank(field, result); + else + result += stdstrPrint("%*d", + field_fanout_->width(), + static_cast(fanout)); + } + else if (field == field_capacitance_) + reportField(cap, field, result); + else if (field == field_slew_) + reportFieldDelay(slew, early_late, field, result); + else if (field == field_incr_) + reportFieldDelay(incr, early_late, field, result); + else if (field == field_total_) { + if (total_with_minus) + reportFieldDelayMinus(total, early_late, field, result); + else + reportFieldDelay(total, early_late, field, result); + } + else if (field == field_edge_) { + if (tr) + reportField(tr->shortName(), field, result); + // Compatibility kludge; suppress trailing spaces. + else if (field_iter.hasNext()) + reportFieldBlank(field, result); + } + else if (field == field_case_ && line_case) + result += line_case; + + first_field = false; + } + field_index++; + } + reportEndOfLine(result); +} + +//////////////////////////////////////////////////////////////// + +// Only the total field. +void +ReportPath::reportLineTotal(const char *what, + Delay incr, + const EarlyLate *early_late, + string &result) +{ + reportLineTotal1(what, incr, false, early_late, result); +} + +// Only the total field and always with leading minus sign. +void +ReportPath::reportLineTotalMinus(const char *what, + Delay decr, + const EarlyLate *early_late, + string &result) +{ + reportLineTotal1(what, decr, true, early_late, result); +} + +void +ReportPath::reportLineTotal1(const char *what, + Delay incr, + bool incr_with_minus, + const EarlyLate *early_late, + string &result) +{ + reportDescription(what, result); + result += ' '; + if (incr_with_minus) + reportFieldDelayMinus(incr, early_late, field_total_, result); + else + reportFieldDelay(incr, early_late, field_total_, result); + + reportEndOfLine(result); +} + +void +ReportPath::reportDashLineTotal(string &result) +{ + reportDashLine(field_description_->width() + field_total_->width() + 1, + result); +} + +//////////////////////////////////////////////////////////////// + +void +ReportPath::reportDescription(const char *what, + string &result) +{ + reportDescription(what, false, false, result); +} + +void +ReportPath::reportDescription(const char *what, + bool first_field, + bool last_field, + string &result) +{ + result += what; + int length = strlen(what); + if (!no_split_ + && first_field + && length > field_description_->width()) { + reportEndOfLine(result); + for (int i = 0; i < field_description_->width(); i++) + result += ' '; + } + else if (!last_field) { + for (int i = length; i < field_description_->width(); i++) + result += ' '; + } +} + +void +ReportPath::reportFieldTime(float value, + ReportField *field, + string &result) +{ + if (delayAsFloat(value) == field_blank_) + reportFieldBlank(field, result); + else { + const char *str = units_->timeUnit()->asString(value, digits_); + if (stringEq(str, minus_zero_)) + // Filter "-0.00" fields. + str = plus_zero_; + reportField(str, field, result); + } +} + +void +ReportPath::reportSpaceFieldTime(float value, + string &result) +{ + result += ' '; + reportFieldTime(value, field_total_, result); +} + +void +ReportPath::reportSpaceFieldDelay(Delay value, + const EarlyLate *early_late, + string &result) +{ + result += ' '; + reportTotalDelay(value, early_late, result); +} + +void +ReportPath::reportTotalDelay(Delay value, + const EarlyLate *early_late, + string &result) +{ + const char *str = delayAsString(value, early_late, this, digits_); + if (stringEq(str, minus_zero_)) + // Filter "-0.00" fields. + str = plus_zero_; + reportField(str, field_total_, result); +} + +// Total time always with leading minus sign. +void +ReportPath::reportFieldDelayMinus(Delay value, + const EarlyLate *early_late, + ReportField *field, + string &result) +{ + if (delayAsFloat(value) == field_blank_) + reportFieldBlank(field, result); + else { + float mean_sigma = delayAsFloat(value, early_late, this); + const char *str = units_->timeUnit()->asString(-mean_sigma, digits_); + if (stringEq(str, plus_zero_)) + // Force leading minus sign. + str = minus_zero_; + reportField(str, field, result); + } +} + +void +ReportPath::reportFieldDelay(Delay value, + const EarlyLate *early_late, + ReportField *field, + string &result) +{ + if (delayAsFloat(value) == field_blank_) + reportFieldBlank(field, result); + else { + const char *str = delayAsString(value, early_late, this, digits_); + if (stringEq(str, minus_zero_)) + // Filter "-0.00" fields. + str = plus_zero_; + reportField(str, field, result); + } +} + +void +ReportPath::reportField(float value, + ReportField *field, + string &result) +{ + if (value == field_blank_) + reportFieldBlank(field, result); + else { + const char *value_str = field->unit()->asString(value, digits_); + reportField(value_str, field, result); + } +} + +void +ReportPath::reportField(const char *value, + ReportField *field, + string &result) +{ + if (field->leftJustify()) + result += value; + for (int i = strlen(value); i < field->width(); i++) + result += ' '; + if (!field->leftJustify()) + result += value; +} + +void +ReportPath::reportFieldBlank(ReportField *field, + string &result) +{ + result += field->blank(); +} + +void +ReportPath::reportDashLine(string &result) +{ + ReportFieldSeq::Iterator field_iter(fields_); + while (field_iter.hasNext()) { + ReportField *field = field_iter.next(); + if (field->enabled()) { + for (int i = 0; i < field->width(); i++) + result += '-'; + } + } + result += "------"; + reportEndOfLine(result); +} + +void +ReportPath::reportDashLine(int line_width, + string &result) +{ + for (int i = 0; i < line_width; i++) + result += '-'; + reportEndOfLine(result); +} + +void +ReportPath::reportEndOfLine(string &result) +{ + result += '\n'; +} + +bool +ReportPath::reportClkPath() const +{ + return format_ == ReportPathFormat::full_clock + || format_ == ReportPathFormat::full_clock_expanded; +} + +//////////////////////////////////////////////////////////////// + +const char * +ReportPath::asRisingFalling(const TransRiseFall *tr) +{ + if (tr == TransRiseFall::rise()) + return "rising"; + else + return "falling"; +} + +const char * +ReportPath::asRiseFall(const TransRiseFall *tr) +{ + if (tr == TransRiseFall::rise()) + return "rise"; + else + return "fall"; +} + +// Find the startpoint type from the first path edge. +const char * +ReportPath::edgeRegLatchDesc(Edge *first_edge, + TimingArc *first_arc) +{ + TimingRole *role = first_arc->role(); + if (role == TimingRole::latchDtoQ()) { + Instance *inst = network_->instance(first_edge->to(graph_)->pin()); + LibertyCell *cell = network_->libertyCell(inst); + if (cell) { + LibertyPort *enable_port; + FuncExpr *enable_func; + TransRiseFall *enable_tr; + cell->latchEnable(first_edge->timingArcSet(), + enable_port, enable_func, enable_tr); + return latchDesc(enable_tr); + } + } + else if (role == TimingRole::regClkToQ()) + return regDesc(first_arc->fromTrans()->asRiseFall()); + else if (role == TimingRole::latchEnToQ()) + return latchDesc(first_arc->fromTrans()->asRiseFall()); + // Who knows... + return regDesc(first_arc->fromTrans()->asRiseFall()); +} + +const char * +ReportPath::checkRegLatchDesc(const TimingRole *role, + const TransRiseFall *clk_tr) const +{ + if (role == TimingRole::regClkToQ()) + return regDesc(clk_tr); + else if (role == TimingRole::latchEnToQ() + || role == TimingRole::latchDtoQ()) + return latchDesc(clk_tr); + else + // Default when we don't know better. + return "edge-triggered flip-flop"; +} + +const char * +ReportPath::regDesc(const TransRiseFall *clk_tr) const +{ + if (clk_tr == TransRiseFall::rise()) + return "rising edge-triggered flip-flop"; + else if (clk_tr == TransRiseFall::fall()) + return "falling edge-triggered flip-flop"; + else + return "edge-triggered flip-flop"; +} + +const char * +ReportPath::latchDesc(const TransRiseFall *clk_tr) const +{ + return (clk_tr == TransRiseFall::rise()) + ? "positive level-sensitive latch" + : "negative level-sensitive latch"; +} + +} // namespace diff --git a/search/ReportPath.hh b/search/ReportPath.hh new file mode 100644 index 0000000..6c5540b --- /dev/null +++ b/search/ReportPath.hh @@ -0,0 +1,578 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_REPORT_PATH_H +#define STA_REPORT_PATH_H + +#include +#include "DisallowCopyAssign.hh" +#include "StringSeq.hh" +#include "SearchClass.hh" +#include "PathEnd.hh" + +namespace sta { + +class Corner; +class DcalcAnalysisPt; +class PathExpanded; +class ReportField; + +using std::string; + +typedef Vector ReportFieldSeq; + +class ReportPath : public StaState +{ +public: + explicit ReportPath(StaState *sta); + virtual ~ReportPath(); + void setPathFormat(ReportPathFormat format); + void setReportFieldOrder(StringSeq *field_names); + void setReportFields(bool report_input_pin, + bool report_net, + bool report_cap, + bool report_slew); + int digits() const { return digits_; } + void setDigits(int digits); + void setNoSplit(bool no_split); + ReportField *findField(const char *name); + + // Header above reportPathEnd results. + void reportPathEndHeader(); + // Footer below reportPathEnd results. + void reportPathEndFooter(); + void reportPathEnd(PathEnd *end); + // Format report_path_endpoint only: + // Previous path end is used to detect path group changes + // so headers are reported by group. + void reportPathEnd(PathEnd *end, + PathEnd *prev_end); + void reportPathEnds(PathEndSeq *ends); + void reportPath(const Path *path); + + void reportShort(const PathEndUnconstrained *end, + string &result); + void reportShort(const PathEndCheck *end, + string &result); + void reportShort(const PathEndLatchCheck *end, + string &result); + void reportShort(const PathEndPathDelay *end, + string &result); + void reportShort(const PathEndOutputDelay *end, + string &result); + void reportShort(const PathEndGatedClock *end, + string &result); + void reportShort(const PathEndDataCheck *end, + string &result); + + void reportFull(const PathEndUnconstrained *end, + string &result); + void reportFull(const PathEndCheck *end, + string &result); + void reportFull(const PathEndLatchCheck *end, + string &result); + void reportFull(const PathEndPathDelay *end, + string &result); + void reportFull(const PathEndOutputDelay *end, + string &result); + void reportFull(const PathEndGatedClock *end, + string &result); + void reportFull(const PathEndDataCheck *end, + string &result); + + void reportEndHeader(string &result); + void reportEndLine(PathEnd *end, + string &result); + + void reportSummaryHeader(string &result); + void reportSummaryLine(PathEnd *end, + string &result); + + void reportSlackOnlyHeader(string &result); + void reportSlackOnly(PathEnd *end, + string &result); + + void reportMpwCheck(MinPulseWidthCheck *check, + bool verbose); + void reportMpwChecks(MinPulseWidthCheckSeq *checks, + bool verbose); + void reportMpwHeaderShort(string &result); + void reportShort(MinPulseWidthCheck *check, + string &result); + void reportVerbose(MinPulseWidthCheck *check, + string &result); + + void reportCheck(MinPeriodCheck *check, + bool verbose); + void reportChecks(MinPeriodCheckSeq *checks, + bool verbose); + void reportPeriodHeaderShort(string &result); + void reportShort(MinPeriodCheck *check, + string &result); + void reportVerbose(MinPeriodCheck *check, + string &result); + + void reportCheck(MaxSkewCheck *check, + bool verbose); + void reportChecks(MaxSkewCheckSeq *checks, + bool verbose); + void reportMaxSkewHeaderShort(string &result); + void reportShort(MaxSkewCheck *check, + string &result); + void reportVerbose(MaxSkewCheck *check, + string &result); + + void reportSlewLimitShortHeader(); + void reportSlewLimitShortHeader(string &result); + void reportSlewLimitShort(Pin *pin, + const TransRiseFall *tr, + Slew slew, + float limit, + float slack); + void reportSlewLimitShort(Pin *pin, const + TransRiseFall *tr, + Slew slew, + float limit, + float slack, + string &result); + void reportSlewLimitVerbose(Pin *pin, + const Corner *corner, + const TransRiseFall *tr, + Slew slew, + float limit, + float slack, + const MinMax *min_max); + void reportSlewLimitVerbose(Pin *pin, + const Corner *corner, + const TransRiseFall *tr, + Slew slew, + float limit, + float slack, + const MinMax *min_max, + string &result); + +protected: + void makeFields(); + ReportField *makeField(const char *name, + const char *title, + int width, + bool left_justify, + Unit *unit, + bool enabled); + void reportEndpointHeader(PathEnd *end, + PathEnd *prev_end); + void reportShort(const PathEndUnconstrained *end, + PathExpanded &expanded, + string &result); + void reportShort(const PathEndCheck *end, + PathExpanded &expanded, + string &result); + void reportShort(const PathEndLatchCheck *end, + PathExpanded &expanded, + string &result); + void reportShort(const PathEndPathDelay *end, + PathExpanded &expanded, + string &result); + void reportShort(const PathEndOutputDelay *end, + PathExpanded &expanded, + string &result); + void reportShort(const PathEndGatedClock *end, + PathExpanded &expanded, + string &result); + void reportShort(const PathEndDataCheck *end, + PathExpanded &expanded, + string &result); + void reportEndpoint(const PathEndOutputDelay *end, + string &result); + void reportEndpoint(const PathEndPathDelay *end, + string &result); + void reportEndpoint(const PathEndGatedClock *end, + string &result); + string pathEndpoint(PathEnd *end); + string pathStartpoint(PathEnd *end, + PathExpanded &expanded); + void reportBorrowing(const PathEndLatchCheck *end, + Arrival &borrow, + Arrival &time_given_to_startpoint, + string &result); + void reportEndpoint(const PathEndDataCheck *end, + string &result); + const char *clkNetworkDelayIdealProp(bool is_ideal); + + string checkRoleReason(const PathEnd *end); + string checkRoleString(const PathEnd *end); + virtual void reportGroup(const PathEnd *end, + string &result); + void reportStartpoint(const PathEnd *end, + PathExpanded &expanded, + string &result); + void reportUnclockedEndpoint(const PathEnd *end, + const char *default_reason, + string &result); + void reportEndpoint(const PathEndCheck *end, + string &result); + void reportEndpoint(const PathEndLatchCheck *end, + string &result); + const char *latchDesc(const PathEndLatchCheck *end); + void reportStartpoint(const char *start, + string reason, + string &result); + void reportEndpoint(const char *end, + string reason, + string &result); + void reportStartEndPoint(const char *pt, + string reason, + const char *key, + string &result); + string tgtClkName(const PathEnd *end); + const char *clkRegLatchDesc(const PathEnd *end); + void reportSrcPath(const PathEnd *end, + PathExpanded &expanded, + string &result); + void reportTgtClk(const PathEnd *end, + string &result); + void reportTgtClk(const PathEnd *end, + float prev_time, + string &result); + void reportTgtClk(const PathEnd *end, + float prev_time, + bool is_prop, + string &result); + bool pathFromGenPropClk(const Path *clk_path, + const EarlyLate *early_late); + bool isGenPropClk(const Clock *clk, + const TransRiseFall *clk_tr, + const MinMax *min_max, + const EarlyLate *early_late); + void reportSrcClkAndPath(const Path *path, + PathExpanded &expanded, + float time_offset, + Arrival clk_insertion, + Arrival clk_latency, + bool is_path_delay, + string &result); + bool reportGenClkSrcPath(const Path *clk_path, Clock *clk, + const TransRiseFall *clk_tr, + const MinMax *min_max, + const EarlyLate *early_late); + void reportGenClkSrcAndPath(const Path *path, Clock *clk, + const TransRiseFall *clk_tr, + const EarlyLate *early_late, + const PathAnalysisPt *path_ap, + float time_offset, + float path_time_offset, + bool clk_used_as_data, + string &result); + bool reportGenClkSrcPath1(Clock *clk, const Pin *clk_pin, + const TransRiseFall *clk_tr, + const EarlyLate *early_late, + const PathAnalysisPt *path_ap, + float gclk_time, + float time_offset, + bool clk_used_as_data, + string &result); + void reportClkSrcLatency(Arrival insertion, + float clk_time, + const EarlyLate *early_late, + string &result); + void reportPathLine(const Path *path, + Delay incr, + Arrival time, + const char *line_case, + string &result); + void reportCommonClkPessimism(const PathEnd *end, + Arrival &clk_arrival, + string &result); + void reportClkUncertainty(const PathEnd *end, + Arrival &clk_arrival, + string &result); + void reportClkLine(const Clock *clk, + const char *clk_name, + const TransRiseFall *clk_tr, + Arrival clk_time, + const MinMax *min_max, + string &result); + void reportClkLine(const Clock *clk, + const char *clk_name, + const TransRiseFall *clk_tr, + Arrival prev_time, + Arrival clk_time, + const MinMax *min_max, + string &result); + void reportRequired(const PathEnd *end, + string margin_msg, + string &result); + void reportSlack(const PathEnd *end, + string &result); + void reportSlack(Slack slack, + string &result); + void reportSpaceSlack(PathEnd *end, + string &result); + void reportSpaceSlack(Slack slack, + string &result); + void reportSrcPathArrival(const PathEnd *end, + PathExpanded &expanded, + string &result); + void reportPath(const PathEnd *end, + PathExpanded &expanded, + string &result); + void reportPath(const Path *path, + string &result); + void reportPathHeader(string &result); + void reportPath1(const Path *path, + PathExpanded &expanded, + bool clk_used_as_data, + float time_offset, + string &result); + void reportPath2(const Path *path, + PathExpanded &expanded, + bool clk_used_as_data, + float time_offset, + string &result); + void reportPath3(const Path *path, + PathExpanded &expanded, + bool clk_used_as_data, + bool report_clk_path, + Arrival prev_time, + float time_offset, + string &result); + void reportPath4(const Path *path, + PathExpanded &expanded, + bool clk_used_as_data, + bool skip_first_path, + bool skip_last_path, + float time_offset, + string &result); + void reportPath5(const Path *path, + PathExpanded &expanded, + size_t path_first_index, + size_t path_last_index, + bool propagated_clk, + bool report_clk_path, + Arrival prev_time, + float time_offset, + string &result); + void reportInputExternalDelay(const Path *path, + float time_offset, + string &result); + void reportLine(const char *what, + Delay total, + const EarlyLate *early_late, + string &result); + void reportLineNegative(const char *what, + Delay total, + const EarlyLate *early_late, + string &result); + void reportLine(const char *what, + Delay total, + const EarlyLate *early_late, + const TransRiseFall *tr, + string &result); + void reportLine(const char *what, + Delay incr, + Delay total, + const EarlyLate *early_late, + string &result); + void reportLine(const char *what, + Delay incr, + Delay total, + const EarlyLate *early_late, + const TransRiseFall *tr, + string &result); + void reportLine(const char *what, + Slew slew, + Delay incr, + Delay total, + const EarlyLate *early_late, + string &result); + void reportLine(const char *what, + float cap, + Slew slew, + float fanout, + Delay incr, + Delay total, + bool total_with_minus, + const EarlyLate *early_late, + const TransRiseFall *tr, + const char *line_case, + string &result); + void reportLineTotal(const char *what, + Delay incr, + const EarlyLate *early_late, + string &result); + void reportLineTotalMinus(const char *what, + Delay decr, + const EarlyLate *early_late, + string &result); + void reportLineTotal1(const char *what, + Delay incr, + bool incr_with_minus, + const EarlyLate *early_late, + string &result); + void reportDashLineTotal(string &result); + void reportDescription(const char *what, + string &result); + void reportDescription(const char *what, + bool first_field, + bool last_field, + string &result); + void reportFieldTime(float value, + ReportField *field, + string &result); + void reportSpaceFieldTime(float value, + string &result); + void reportSpaceFieldDelay(Delay value, + const EarlyLate *early_late, + string &result); + void reportFieldDelayMinus(Delay value, + const EarlyLate *early_late, + ReportField *field, + string &result); + void reportTotalDelay(Delay value, + const EarlyLate *early_late, + string &result); + void reportFieldDelay(Delay value, + const EarlyLate *early_late, + ReportField *field, + string &result); + void reportField(float value, + ReportField *field, + string &result); + void reportField(const char *value, + ReportField *field, + string &result); + void reportFieldBlank(ReportField *field, + string &result); + void reportDashLine(string &result); + void reportDashLine(int line_width, + string &result); + void reportEndOfLine(string &result); + string descriptionField(Vertex *vertex); + bool reportClkPath() const; + string clkName(const Clock *clk, + bool inverted); + bool hasExtInputDriver(const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max); + float loadCap(Pin *drvr_pin, + const TransRiseFall *tr, + DcalcAnalysisPt *dcalc_ap); + float drvrFanout(Vertex *drvr, + const MinMax *min_max); + const char *mpwCheckHiLow(MinPulseWidthCheck *check); + void reportSkewClkPath(const char *arrival_msg, + const PathVertex *clk_path, + string &result); + const char *edgeRegLatchDesc(Edge *edge, + TimingArc *arc); + const char *checkRegLatchDesc(const TimingRole *role, + const TransRiseFall *clk_tr) const; + const char *regDesc(const TransRiseFall *clk_tr) const; + const char *latchDesc(const TransRiseFall *clk_tr) const; + void pathClkPath(const Path *path, + PathRef &clk_path) const; + bool isPropagated(const Path *clk_path); + bool isPropagated(const Path *clk_path, + Clock *clk); + bool pathFromClkPin(PathExpanded &expanded); + bool pathFromClkPin(const Path *path, + const Pin *start_pin); + void latchPaths(const Path *path, + // Return values. + PathRef &d_path, + PathRef &q_path, + Edge *&d_q_edge) const; + bool nextArcAnnotated(const PathRef *next_path, + size_t next_index, + PathExpanded &expanded, + DcalcAPIndex ap_index); + float tgtClkInsertionOffet(const Path *clk_path, + const EarlyLate *early_late, + PathAnalysisPt *path_ap); + // InputDelay used to seed path root. + InputDelay *pathInputDelay(const Path *path) const; + void pathInputDelayRefPath(const Path *path, + InputDelay *input_delay, + // Return value. + PathRef &ref_path); + const char *asRisingFalling(const TransRiseFall *tr); + const char *asRiseFall(const TransRiseFall *tr); + + // Path options. + ReportPathFormat format_; + ReportFieldSeq fields_; + bool report_input_pin_; + bool report_net_; + bool no_split_; + int digits_; + + int start_end_pt_width_; + + ReportField *field_description_; + ReportField *field_total_; + ReportField *field_incr_; + ReportField *field_capacitance_; + ReportField *field_slew_; + ReportField *field_fanout_; + ReportField *field_edge_; + ReportField *field_case_; + + const char *plus_zero_; + const char *minus_zero_; + + static const float field_blank_; + static const float field_skip_; + +private: + DISALLOW_COPY_AND_ASSIGN(ReportPath); +}; + +class ReportField +{ +public: + ReportField(const char *name, + const char *title, + int 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); + bool leftJustify() const { return left_justify_; } + Unit *unit() const { return unit_; } + const char *blank() { return blank_; } + void setEnabled(bool enabled); + bool enabled() const { return enabled_; } + +protected: + const char *name_; + const char *title_; + int width_; + bool left_justify_; + Unit *unit_; + bool enabled_; + char *blank_; + ReportPath *report_path_; +}; + +} // namespace +#endif diff --git a/search/Search.cc b/search/Search.cc new file mode 100644 index 0000000..1afb22d --- /dev/null +++ b/search/Search.cc @@ -0,0 +1,4011 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include // abs +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "Mutex.hh" +#include "ThreadForEach.hh" +#include "Report.hh" +#include "Debug.hh" +#include "Error.hh" +#include "Stats.hh" +#include "Fuzzy.hh" +#include "TimingRole.hh" +#include "FuncExpr.hh" +#include "TimingArc.hh" +#include "Sequential.hh" +#include "Units.hh" +#include "PortDirection.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Graph.hh" +#include "GraphCmp.hh" +#include "Levelize.hh" +#include "PortDelay.hh" +#include "Clock.hh" +#include "CycleAccting.hh" +#include "ExceptionPath.hh" +#include "DataCheck.hh" +#include "Sdc.hh" +#include "SearchPred.hh" +#include "Bfs.hh" +#include "DcalcAnalysisPt.hh" +#include "Corner.hh" +#include "Sim.hh" +#include "PathVertex.hh" +#include "PathVertexRep.hh" +#include "PathRef.hh" +#include "ClkInfo.hh" +#include "Tag.hh" +#include "TagGroup.hh" +#include "PathEnd.hh" +#include "PathGroup.hh" +#include "PathAnalysisPt.hh" +#include "VisitPathEnds.hh" +#include "GatedClk.hh" +#include "WorstSlack.hh" +#include "Latches.hh" +#include "Crpr.hh" +#include "Genclks.hh" +#include "Search.hh" + +namespace sta { + +using std::min; +using std::max; +using std::abs; + +//////////////////////////////////////////////////////////////// + +EvalPred::EvalPred(const StaState *sta) : + SearchPred0(sta), + search_thru_latches_(true) +{ +} + +void +EvalPred::setSearchThruLatches(bool thru_latches) +{ + search_thru_latches_ = thru_latches; +} + +bool +EvalPred::searchThru(Edge *edge) +{ + const Sdc *sdc = sta_->sdc(); + TimingRole *role = edge->role(); + return SearchPred0::searchThru(edge) + && (sdc->dynamicLoopBreaking() + || !edge->isDisabledLoop()) + && !role->isTimingCheck() + && (search_thru_latches_ + || role != TimingRole::latchDtoQ() + || sta_->latches()->latchDtoQState(edge) == LatchEnableState::open); +} + +bool +EvalPred::searchTo(const Vertex *to_vertex) +{ + const Sdc *sdc = sta_->sdc(); + const Pin *pin = to_vertex->pin(); + return SearchPred0::searchTo(to_vertex) + && !(sdc->isVertexPinClock(pin) + && !sdc->isPathDelayInternalEndpoint(pin)); +} + +//////////////////////////////////////////////////////////////// + +DynLoopSrchPred::DynLoopSrchPred(TagGroupBldr *tag_bldr) : + tag_bldr_(tag_bldr) +{ +} + +bool +DynLoopSrchPred::loopEnabled(Edge *edge, + const Sdc *sdc, + const Graph *graph, + Search *search) +{ + return !edge->isDisabledLoop() + || (sdc->dynamicLoopBreaking() + && hasPendingLoopPaths(edge, graph, search)); +} + +bool +DynLoopSrchPred::hasPendingLoopPaths(Edge *edge, + const Graph *graph, + Search *search) +{ + if (tag_bldr_ + && tag_bldr_->hasLoopTag()) { + Corners *corners = search->corners(); + Vertex *from_vertex = edge->from(graph); + TagGroup *prev_tag_group = search->tagGroup(from_vertex); + ArrivalMap::Iterator arrival_iter(tag_bldr_->arrivalMap()); + while (arrival_iter.hasNext()) { + Tag *from_tag; + int arrival_index; + arrival_iter.next(from_tag, arrival_index); + if (from_tag->isLoop()) { + // Loop false path exceptions apply to rise/fall edges so to_tr + // 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, TransRiseFall::rise(), + path_ap->pathMinMax(), path_ap); + if (to_tag + && (prev_tag_group == nullptr + || !prev_tag_group->hasTag(from_tag))) + return true; + } + } + } + return false; +} + +// EvalPred unless +// latch D->Q edge +class SearchThru : public EvalPred, public DynLoopSrchPred +{ +public: + SearchThru(TagGroupBldr *tag_bldr, + const StaState *sta); + virtual bool searchThru(Edge *edge); + +private: + DISALLOW_COPY_AND_ASSIGN(SearchThru); +}; + +SearchThru::SearchThru(TagGroupBldr *tag_bldr, + const StaState *sta) : + EvalPred(sta), + DynLoopSrchPred(tag_bldr) +{ +} + +bool +SearchThru::searchThru(Edge *edge) +{ + const Graph *graph = sta_->graph(); + const Sdc *sdc = sta_->sdc(); + 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, sdc, graph, search); +} + +ClkArrivalSearchPred::ClkArrivalSearchPred(const StaState *sta) : + EvalPred(sta) +{ +} + +bool +ClkArrivalSearchPred::searchThru(Edge *edge) +{ + const TimingRole *role = edge->role(); + return (role->isWire() + || role == TimingRole::combinational()) + && EvalPred::searchThru(edge); +} + +//////////////////////////////////////////////////////////////// + +Search::Search(StaState *sta) : + StaState(sta) +{ + init(sta); +} + +void +Search::init(StaState *sta) +{ + 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; + 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_ = 127; + tag_set_ = new TagHashSet(tag_capacity_, false); + clk_info_set_ = new ClkInfoSet(ClkInfoLess(sta)); + tag_next_ = 0; + tags_ = new Tag*[tag_capacity_]; + tag_group_capacity_ = 127; + tag_groups_ = new TagGroup*[tag_group_capacity_]; + tag_group_next_ = 0; + tag_group_set_ = new TagGroupSet(tag_group_capacity_, false); + 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; + found_downstream_clk_pins_ = false; +} + +// Init "options". +void +Search::initVars() +{ + unconstrained_paths_ = false; + crpr_path_pruning_enabled_ = true; + crpr_approx_missing_requireds_ = true; +} + +Search::~Search() +{ + deletePaths(); + deleteTags(); + delete tag_set_; + delete clk_info_set_; + delete [] tags_; + delete [] tag_groups_; + delete tag_group_set_; + delete search_adj_; + delete eval_pred_; + delete arrival_visitor_; + delete arrival_iter_; + delete required_iter_; + delete endpoints_; + delete invalid_endpoints_; + delete visit_path_ends_; + delete gated_clk_; + delete worst_slacks_; + delete check_crpr_; + delete genclks_; + deleteFilter(); + deletePathGroups(); +} + +void +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(); + arrival_iter_->clear(); + invalid_requireds_.clear(); + invalid_tns_.clear(); + required_iter_->clear(); + endpointsInvalid(); + deletePathGroups(); + deletePaths(); + deleteTags(); + clearPendingLatchOutputs(); + deleteFilter(); + genclks_->clear(); + found_downstream_clk_pins_ = false; +} + +bool +Search::crprPathPruningEnabled() const +{ + return crpr_path_pruning_enabled_; +} + +void +Search::setCrprpathPruningEnabled(bool enabled) +{ + crpr_path_pruning_enabled_ = enabled; +} + +bool +Search::crprApproxMissingRequireds() const +{ + return crpr_approx_missing_requireds_; +} + +void +Search::setCrprApproxMissingRequireds(bool enabled) +{ + crpr_approx_missing_requireds_ = enabled; +} + +void +Search::deleteTags() +{ + for (TagGroupIndex i = 0; i < tag_group_next_; i++) { + TagGroup *group = tag_groups_[i]; + delete group; + } + tag_group_next_ = 0; + tag_group_set_->clear(); + tag_group_free_indices_.clear(); + + tag_next_ = 0; + tag_set_->deleteContentsClear(); + tag_free_indices_.clear(); + + clk_info_set_->deleteContentsClear(); +} + +void +Search::deleteFilter() +{ + if (filter_) { + sdc_->deleteException(filter_); + filter_ = nullptr; + filter_from_ = nullptr; + } + else { + // Filter owns filter_from_ if it exists. + delete filter_from_; + filter_from_ = nullptr; + } + delete filter_to_; + filter_to_ = nullptr; +} + +void +Search::copyState(const StaState *sta) +{ + StaState::copyState(sta); + // Notify sub-components. + arrival_iter_->copyState(sta); + required_iter_->copyState(sta); + visit_path_ends_->copyState(sta); + gated_clk_->copyState(sta); + check_crpr_->copyState(sta); + genclks_->copyState(sta); +} + +//////////////////////////////////////////////////////////////// + +void +Search::deletePaths() +{ + debugPrint0(debug_, "search", 1, "delete paths\n"); + if (arrivals_exist_) { + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + deletePaths1(vertex); + } + arrivals_exist_ = false; + } +} + +void +Search::deletePaths1(Vertex *vertex) +{ + Arrival *arrivals = vertex->arrivals(); + delete [] arrivals; + vertex->setArrivals(nullptr); + PathVertexRep *prev_paths = vertex->prevPaths(); + delete [] prev_paths; + vertex->setPrevPaths(nullptr); + vertex->setTagGroupIndex(tag_group_index_max); + vertex->setHasRequireds(false); + vertex->setCrprPathPruningDisabled(false); +} + +void +Search::deletePaths(Vertex *vertex) +{ + tnsNotifyBefore(vertex); + if (worst_slacks_) + worst_slacks_->worstSlackNotifyBefore(vertex); + deletePaths1(vertex); +} + +//////////////////////////////////////////////////////////////// + +// from/thrus/to are owned and deleted by Search. +// Returned sequence is owned by the caller. +// 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, + int group_count, + int endpoint_count, + bool unique_pins, + 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) +{ + unconstrained_paths_ = unconstrained; + // Delete results from last findPathEnds. + // Filtered arrivals are deleted by Sta::searchPreamble. + deletePathGroups(); + checkFromThrusTo(from, thrus, to); + filter_from_ = from; + filter_to_ = to; + if ((from + && (from->pins() + || from->instances())) + || thrus) { + filter_ = sdc_->makeFilterPath(from, thrus, nullptr); + findFilteredArrivals(); + } + else + // These cases do not require filtered arrivals. + // -from clocks + // -to + findAllArrivals(); + if (!sdc_->recoveryRemovalChecksEnabled()) + recovery = removal = false; + if (!sdc_->gatedClkChecksEnabled()) + clk_gating_setup = clk_gating_hold = false; + path_groups_ = makePathGroups(group_count, endpoint_count, unique_pins, + 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(); + return path_ends; +} + +// From/thrus/to are used to make a filter exception. If the last +// search used a filter arrival/required times were only found for a +// subset of the paths. Delete the paths that have a filter +// exception state. +void +Search::deleteFilteredArrivals() +{ + if (filter_) { + ExceptionFrom *from = filter_->from(); + ExceptionThruSeq *thrus = filter_->thrus(); + if ((from + && (from->pins() + || from->instances())) + || thrus) { + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + TagGroup *tag_group = tagGroup(vertex); + if (tag_group + && tag_group->hasFilterTag()) { + // Vertex's tag_group will be deleted. + deletePaths(vertex); + arrivalInvalid(vertex); + requiredInvalid(vertex); + } + } + deleteFilterTagGroups(); + deleteFilterClkInfos(); + deleteFilterTags(); + } + } + deleteFilter(); +} + +void +Search::deleteFilterTagGroups() +{ + for (TagGroupIndex i = 0; i < tag_group_next_; i++) { + TagGroup *group = tag_groups_[i]; + if (group + && group->hasFilterTag()) { + tag_group_set_->erase(group); + tag_groups_[group->index()] = nullptr; + tag_group_free_indices_.push_back(i); + delete group; + } + } +} + +void +Search::deleteFilterTags() +{ + for (TagIndex i = 0; i < tag_next_; i++) { + Tag *tag = tags_[i]; + if (tag + && tag->isFilter()) { + tags_[i] = nullptr; + tag_set_->erase(tag); + delete tag; + tag_free_indices_.push_back(i); + } + } +} + +void +Search::deleteFilterClkInfos() +{ + ClkInfoSet::Iterator clk_info_iter(clk_info_set_); + while (clk_info_iter.hasNext()) { + ClkInfo *clk_info = clk_info_iter.next(); + if (clk_info->refsFilter(this)) { + clk_info_set_->erase(clk_info); + delete clk_info; + } + } +} + +void +Search::findFilteredArrivals() +{ + findArrivals1(); + 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); + // Iterate until data arrivals at all latches stop changing. + for (int pass = 1; pass <= 2 || havePendingLatchOutputs() ; pass++) { + enqueuePendingLatchOutputs(); + debugPrint1(debug_, "search", 1, "find arrivals pass %d\n", pass); + int arrival_count = arrival_iter_->visitParallel(max_level, + arrival_visitor_); + debugPrint1(debug_, "search", 1, "found %d arrivals\n", arrival_count); + } + arrivals_exist_ = true; +} + +class SeedFaninsThruHierPin : public HierPinThruVisitor +{ +public: + SeedFaninsThruHierPin(Graph *graph, + Search *search); + +protected: + virtual void visit(Pin *drvr, + Pin *load); + + Graph *graph_; + Search *search_; + +private: + DISALLOW_COPY_AND_ASSIGN(SeedFaninsThruHierPin); +}; + +SeedFaninsThruHierPin::SeedFaninsThruHierPin(Graph *graph, + Search *search) : + HierPinThruVisitor(), + graph_(graph), + search_(search) +{ +} + +void +SeedFaninsThruHierPin::visit(Pin *drvr, + Pin *) +{ + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(drvr, vertex, bidirect_drvr_vertex); + search_->seedArrival(vertex); + if (bidirect_drvr_vertex) + search_->seedArrival(bidirect_drvr_vertex); +} + +void +Search::seedFilterStarts() +{ + ExceptionPt *first_pt = filter_->firstPt(); + PinSet first_pins; + first_pt->allPins(network_, &first_pins); + for (auto pin : first_pins) { + if (network_->isHierarchical(pin)) { + SeedFaninsThruHierPin visitor(graph_, this); + visitDrvrLoadsThruHierPin(pin, network_, &visitor); + } + else { + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + seedArrival(vertex); + if (bidirect_drvr_vertex) + seedArrival(bidirect_drvr_vertex); + } + } +} + +//////////////////////////////////////////////////////////////// + +void +Search::deleteVertexBefore(Vertex *vertex) +{ + if (arrivals_exist_) { + deletePaths(vertex); + arrival_iter_->deleteVertexBefore(vertex); + invalid_arrivals_.erase(vertex); + } + if (requireds_exist_) { + required_iter_->deleteVertexBefore(vertex); + invalid_requireds_.erase(vertex); + invalid_tns_.erase(vertex); + } + if (endpoints_) + endpoints_->erase(vertex); + if (invalid_endpoints_) + invalid_endpoints_->erase(vertex); +} + +void +Search::arrivalsInvalid() +{ + if (arrivals_exist_) { + debugPrint0(debug_, "search", 1, "arrivals invalid\n"); + // Delete paths to make sure no state is left over. + // For example, set_disable_timing strands a vertex, which means + // the search won't revisit it to clear the previous arrival. + deletePaths(); + deleteTags(); + genclks_->clear(); + deleteFilter(); + arrivals_at_endpoints_exist_ = false; + arrivals_seeded_ = false; + requireds_exist_ = false; + requireds_seeded_ = false; + clk_arrivals_valid_ = false; + arrival_iter_->clear(); + required_iter_->clear(); + // No need to keep track of incremental updates any more. + invalid_arrivals_.clear(); + invalid_requireds_.clear(); + tns_exists_ = false; + clearWorstSlack(); + invalid_tns_.clear(); + } +} + +void +Search::requiredsInvalid() +{ + debugPrint0(debug_, "search", 1, "requireds invalid\n"); + requireds_exist_ = false; + requireds_seeded_ = false; + invalid_requireds_.clear(); + tns_exists_ = false; + clearWorstSlack(); + invalid_tns_.clear(); +} + +void +Search::arrivalInvalid(Vertex *vertex) +{ + if (arrivals_exist_) { + debugPrint1(debug_, "search", 2, "arrival invalid %s\n", + vertex->name(sdc_network_)); + if (!arrival_iter_->inQueue(vertex)) { + // Lock for StaDelayCalcObserver called by delay calc threads. + UniqueLock lock(invalid_arrivals_lock_); + invalid_arrivals_.insert(vertex); + } + tnsInvalid(vertex); + } +} + +void +Search::arrivalInvalidDelete(Vertex *vertex) +{ + arrivalInvalid(vertex); + deletePaths1(vertex); +} + +void +Search::levelChangedBefore(Vertex *vertex) +{ + if (arrivals_exist_) { + arrival_iter_->remove(vertex); + required_iter_->remove(vertex); + search_->arrivalInvalid(vertex); + search_->requiredInvalid(vertex); + } +} + +void +Search::arrivalInvalid(const Pin *pin) +{ + if (graph_) { + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + arrivalInvalid(vertex); + if (bidirect_drvr_vertex) + arrivalInvalid(bidirect_drvr_vertex); + } +} + +void +Search::requiredInvalid(Instance *inst) +{ + if (graph_) { + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + requiredInvalid(pin); + } + delete pin_iter; + } +} + +void +Search::requiredInvalid(const Pin *pin) +{ + if (graph_) { + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + requiredInvalid(vertex); + if (bidirect_drvr_vertex) + requiredInvalid(bidirect_drvr_vertex); + } +} + +void +Search::requiredInvalid(Vertex *vertex) +{ + if (requireds_exist_) { + debugPrint1(debug_, "search", 2, "required invalid %s\n", + vertex->name(sdc_network_)); + if (!required_iter_->inQueue(vertex)) { + // Lock for StaDelayCalcObserver called by delay calc threads. + UniqueLock lock(invalid_arrivals_lock_); + invalid_requireds_.insert(vertex); + } + tnsInvalid(vertex); + } +} + +//////////////////////////////////////////////////////////////// + +void +Search::findClkArrivals() +{ + if (!clk_arrivals_valid_) { + genclks_->ensureInsertionDelays(); + Stats stats(debug_); + debugPrint0(debug_, "search", 1, "find clk arrivals\n"); + arrival_iter_->clear(); + seedClkVertexArrivals(); + ClkArrivalSearchPred search_clk(this); + arrival_visitor_->init(false, &search_clk); + arrival_iter_->visitParallel(levelize_->maxLevel(), arrival_visitor_); + arrivals_exist_ = true; + stats.report("Find clk arrivals"); + } + clk_arrivals_valid_ = true; +} + +void +Search::seedClkVertexArrivals() +{ + PinSet clk_pins; + findClkVertexPins(clk_pins); + for (auto pin : clk_pins) { + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + seedClkVertexArrivals(pin, vertex); + if (bidirect_drvr_vertex) + seedClkVertexArrivals(pin, 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 TransRiseFall *tr, + const MinMax *min_max, + const EarlyLate *early_late, + const PathAnalysisPt *path_ap) const +{ + float insert; + bool exists; + sdc_->clockInsertion(clk, pin, tr, min_max, early_late, insert, exists); + if (exists) + return insert; + else if (clk->isGeneratedWithPropagatedMaster()) + return genclks_->insertionDelay(clk, pin, tr, early_late, path_ap); + 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; + + InputDelayVertexPinsIterator arrival_iter(sdc_); + while (arrival_iter.hasNext()) { + const Pin *pin = arrival_iter.next(); + // Already hit these. + if (!network_->isTopLevelPort(pin)) { + Vertex *vertex = graph_->pinDrvrVertex(pin); + if (vertex) + visitor->visit(vertex); + } + } + + for (auto clk : sdc_->clks()) { + ClockVertexPinIterator pin_iter(clk); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + // Already hit these. + if (!network_->isTopLevelPort(pin)) { + Vertex *vertex = graph_->pinDrvrVertex(pin); + visitor->visit(vertex); + } + } + } + + // Register clk pins. + for (auto vertex : *graph_->regClkVertices()) + visitor->visit(vertex); + + auto startpoints = sdc_->pathDelayInternalStartpoints(); + if (startpoints) { + for (auto pin : *startpoints) { + Vertex *vertex = graph_->pinDrvrVertex(pin); + visitor->visit(vertex); + } + } +} + +void +Search::visitEndpoints(VertexVisitor *visitor) +{ + for (auto end : *endpoints()) { + Pin *pin = end->pin(); + // Filter register clock pins (fails on set_max_delay -from clk_src). + if (!network_->isRegClkPin(pin) + || sdc_->isPathDelayInternalEndpoint(pin)) + visitor->visit(end); + } +} + +//////////////////////////////////////////////////////////////// + +void +Search::findAllArrivals() +{ + arrival_visitor_->init(false); + findAllArrivals(arrival_visitor_); +} + +void +Search::findAllArrivals(VertexVisitor *arrival_visitor) +{ + // Iterate until data arrivals at all latches stop changing. + for (int pass = 1; pass == 1 || havePendingLatchOutputs(); pass++) { + enqueuePendingLatchOutputs(); + debugPrint1(debug_, "search", 1, "find arrivals pass %d\n", pass); + findArrivals(levelize_->maxLevel(), arrival_visitor); + } +} + +bool +Search::havePendingLatchOutputs() +{ + return pending_latch_outputs_.size() > 0; +} + +void +Search::clearPendingLatchOutputs() +{ + pending_latch_outputs_.clear(); +} + +void +Search::enqueuePendingLatchOutputs() +{ + for (auto latch_vertex : pending_latch_outputs_) + arrival_iter_->enqueue(latch_vertex); + clearPendingLatchOutputs(); +} + +void +Search::findArrivals() +{ + findArrivals(levelize_->maxLevel()); +} + +void +Search::findArrivals(Level level) +{ + arrival_visitor_->init(false); + findArrivals(level, arrival_visitor_); +} + +void +Search::findArrivals(Level level, + VertexVisitor *arrival_visitor) +{ + debugPrint1(debug_, "search", 1, "find arrivals to level %d\n", level); + findArrivals1(); + Stats stats(debug_); + int arrival_count = arrival_iter_->visitParallel(level, arrival_visitor); + stats.report("Find arrivals"); + if (arrival_iter_->empty() + && invalid_arrivals_.empty()) { + clk_arrivals_valid_ = true; + arrivals_at_endpoints_exist_ = true; + } + arrivals_exist_ = true; + debugPrint1(debug_, "search", 1, "found %u arrivals\n", arrival_count); +} + +void +Search::findArrivals1() +{ + if (!arrivals_seeded_) { + genclks_->ensureInsertionDelays(); + arrival_iter_->clear(); + required_iter_->clear(); + seedArrivals(); + arrivals_seeded_ = true; + } + else { + arrival_iter_->ensureSize(); + required_iter_->ensureSize(); + } + seedInvalidArrivals(); +} + +//////////////////////////////////////////////////////////////// + +ArrivalVisitor::ArrivalVisitor(const StaState *sta) : + PathVisitor(nullptr, sta) +{ + init0(); + init(true); +} + +// Copy constructor. +ArrivalVisitor::ArrivalVisitor(bool always_to_endpoints, + SearchPred *pred, + const StaState *sta) : + PathVisitor(pred, sta) +{ + init0(); + init(always_to_endpoints, pred); +} + +void +ArrivalVisitor::init0() +{ + tag_bldr_ = new TagGroupBldr(true, sta_); + tag_bldr_no_crpr_ = new TagGroupBldr(false, sta_); + adj_pred_ = new SearchThru(tag_bldr_, sta_); +} + +void +ArrivalVisitor::init(bool always_to_endpoints) +{ + Search *search = sta_->search(); + init(always_to_endpoints, search ? search->evalPred() : nullptr); +} + +void +ArrivalVisitor::init(bool always_to_endpoints, + SearchPred *pred) +{ + always_to_endpoints_ = always_to_endpoints; + pred_ = pred; + crpr_active_ = sta_->sdc()->crprActive(); +} + + +VertexVisitor * +ArrivalVisitor::copy() +{ + return new ArrivalVisitor(always_to_endpoints_, pred_, sta_); +} + +ArrivalVisitor::~ArrivalVisitor() +{ + delete tag_bldr_; + delete tag_bldr_no_crpr_; + delete adj_pred_; +} + +void +ArrivalVisitor::setAlwaysToEndpoints(bool to_endpoints) +{ + always_to_endpoints_ = to_endpoints; +} + +void +ArrivalVisitor::visit(Vertex *vertex) +{ + const Debug *debug = sta_->debug(); + const Network *network = sta_->network(); + const Network *sdc_network = sta_->sdcNetwork(); + const Graph *graph = sta_->graph(); + const Sdc *sdc = sta_->sdc(); + Search *search = sta_->search(); + debugPrint1(debug, "search", 2, "find arrivals %s\n", + vertex->name(sdc_network)); + Pin *pin = vertex->pin(); + // Don't clobber clock sources. + if (!sdc->isVertexPinClock(pin) + // Unless it is an internal path delay endpoint. + || sdc->isPathDelayInternalEndpoint(pin)) { + tag_bldr_->init(vertex); + has_fanin_one_ = graph->hasFaninOne(vertex); + if (crpr_active_ + && !has_fanin_one_) + tag_bldr_no_crpr_->init(vertex); + + visitFaninPaths(vertex); + if (crpr_active_ + && search->crprPathPruningEnabled() + && !vertex->crprPathPruningDisabled() + && !has_fanin_one_) + pruneCrprArrivals(); + + // Insert paths that originate here but + if (!network->isTopLevelPort(pin) + && sdc->hasInputDelay(pin)) + // set_input_delay on internal pin. + search->seedInputSegmentArrival(pin, vertex, tag_bldr_); + if (sdc->isPathDelayInternalStartpoint(pin)) + // set_min/max_delay on internal pin. + search->makeUnclkedPaths(vertex, true, tag_bldr_); + if (sdc->isPathDelayInternalEndpoint(pin) + && sdc->isVertexPinClock(pin)) + // set_min/max_delay on 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) { + debugPrint1(debug, "search", 2, "arrival seed unclked reg clk %s\n", + network->pathName(pin)); + search->makeUnclkedPaths(vertex, true, tag_bldr_); + } + + 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 (network->isLatchData(pin)) { + if (arrivals_changed + && network->isLatchData(pin)) + search->enqueueLatchDataOutputs(vertex); + } + if ((!search->arrivalsAtEndpointsExist() + || always_to_endpoints_ + || arrivals_changed) + && (network->isRegClkPin(pin) + || !sdc->isPathDelayInternalEndpoint(pin))) + search->arrivalIterator()->enqueueAdjacentVertices(vertex, adj_pred_); + if (arrivals_changed) { + debugPrint0(debug, "search", 4, "arrival changed\n"); + // Only update arrivals when delays change by more than + // fuzzyEqual can distinguish. + search->setVertexArrivals(vertex, tag_bldr_); + search->tnsInvalid(vertex); + constrainedRequiredsInvalid(vertex, is_clk); + } + enqueueRefPinInputDelays(pin); + } +} + +// When a clock arrival changes, the required time changes for any +// timing checks, data checks or gated clock enables constrained +// by the clock pin. +void +ArrivalVisitor::constrainedRequiredsInvalid(Vertex *vertex, + bool is_clk) +{ + Search *search = sta_->search(); + Pin *pin = vertex->pin(); + const Network *network = sta_->network(); + if (network->isLoad(pin) + && search->requiredsExist()) { + const Graph *graph = sta_->graph(); + const Sdc *sdc = sta_->sdc(); + 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); + } + } + } + // Data checks (vertex does not need to be a clk). + DataCheckSet *data_checks = sdc->dataChecksFrom(pin); + if (data_checks) { + for (auto data_check : *data_checks) { + Pin *to = data_check->to(); + search->requiredInvalid(to); + } + } + // Gated clocks. + if (is_clk && sdc->gatedClkChecksEnabled()) { + PinSet enable_pins; + search->gatedClk()->gatedClkEnables(vertex, enable_pins); + for (auto enable : enable_pins) + search->requiredInvalid(enable); + } + } +} + +bool +Search::arrivalsChanged(Vertex *vertex, + TagGroupBldr *tag_bldr) +{ + Arrival *arrivals1 = vertex->arrivals(); + if (arrivals1) { + TagGroup *tag_group = tagGroup(vertex); + if (tag_group->arrivalMap()->size() != tag_bldr->arrivalMap()->size()) + return true; + ArrivalMap::Iterator arrival_iter1(tag_group->arrivalMap()); + while (arrival_iter1.hasNext()) { + Tag *tag1; + int arrival_index1; + arrival_iter1.next(tag1, arrival_index1); + Arrival &arrival1 = arrivals1[arrival_index1]; + Arrival arrival2; + bool arrival_exists2; + tag_bldr->tagArrival(tag1, arrival2, arrival_exists2); + if (!arrival_exists2 + || !fuzzyEqual(arrival1, arrival2)) + return true; + } + return false; + } + else + return true; +} + +bool +ArrivalVisitor::visitFromToPath(const Pin *, + Vertex *from_vertex, + const TransRiseFall *from_tr, + Tag *from_tag, + PathVertex *from_path, + Edge *, + TimingArc *, + ArcDelay arc_delay, + Vertex *, + const TransRiseFall *to_tr, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max, + const PathAnalysisPt *) +{ + const Debug *debug = sta_->debug(); + const Network *sdc_network = sta_->sdcNetwork(); + debugPrint1(debug, "search", 3, " %s\n", + from_vertex->name(sdc_network)); + debugPrint3(debug, "search", 3, " %s -> %s %s\n", + from_tr->asString(), + to_tr->asString(), + min_max->asString()); + debugPrint1(debug, "search", 3, " from tag: %s\n", + from_tag->asString(sta_)); + debugPrint1(debug, "search", 3, " to tag : %s\n", + to_tag->asString(sta_)); + ClkInfo *to_clk_info = to_tag->clkInfo(); + bool to_is_clk = to_tag->isClock(); + Arrival arrival; + int arrival_index; + Tag *tag_match; + tag_bldr_->tagMatchArrival(to_tag, tag_match, arrival, arrival_index); + if (tag_match == nullptr + || fuzzyGreater(to_arrival, arrival, min_max)) { + debugPrint5(debug, "search", 3, " %s + %s = %s %s %s\n", + delayAsString(from_path->arrival(sta_), sta_), + delayAsString(arc_delay, sta_), + delayAsString(to_arrival, sta_), + min_max == MinMax::max() ? ">" : "<", + tag_match ? delayAsString(arrival, sta_) : "MIA"); + PathVertexRep prev_path; + if (to_tag->isClock() || to_tag->isGenClkSrcPath()) + prev_path.init(from_path, sta_); + tag_bldr_->setMatchArrival(to_tag, tag_match, + to_arrival, arrival_index, + &prev_path); + if (crpr_active_ + && !has_fanin_one_ + && to_clk_info->hasCrprClkPin() + && !to_is_clk) { + tag_bldr_no_crpr_->tagMatchArrival(to_tag, tag_match, + arrival, arrival_index); + if (tag_match == nullptr + || fuzzyGreater(to_arrival, arrival, min_max)) { + tag_bldr_no_crpr_->setMatchArrival(to_tag, tag_match, + to_arrival, arrival_index, + &prev_path); + } + } + } + return true; +} + +void +ArrivalVisitor::pruneCrprArrivals() +{ + const Debug *debug = sta_->debug(); + ArrivalMap::Iterator arrival_iter(tag_bldr_->arrivalMap()); + CheckCrpr *crpr = sta_->search()->checkCrpr(); + while (arrival_iter.hasNext()) { + Tag *tag; + int arrival_index; + arrival_iter.next(tag, arrival_index); + ClkInfo *clk_info = tag->clkInfo(); + if (!tag->isClock() + && clk_info->hasCrprClkPin()) { + PathAnalysisPt *path_ap = tag->pathAnalysisPt(sta_); + const MinMax *min_max = path_ap->pathMinMax(); + Tag *tag_no_crpr; + Arrival max_arrival; + int max_arrival_index; + tag_bldr_no_crpr_->tagMatchArrival(tag, tag_no_crpr, + max_arrival, max_arrival_index); + if (tag_no_crpr) { + ClkInfo *clk_info_no_crpr = tag_no_crpr->clkInfo(); + 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; + debugPrint4(debug, "search", 4, " cmp %s %s - %s = %s\n", + tag->asString(sta_), + delayAsString(max_arrival, sta_), + delayAsString(max_crpr, sta_), + delayAsString(max_arrival_max_crpr, sta_)); + Arrival arrival = tag_bldr_->arrival(arrival_index); + if (fuzzyGreater(max_arrival_max_crpr, arrival, min_max)) { + debugPrint1(debug, "search", 3, " pruned %s\n", + tag->asString(sta_)); + tag_bldr_->deleteArrival(tag); + } + } + } + } +} + +// Enqueue pins with input delays that use ref_pin as the clock +// 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) +{ + const Sdc *sdc = sta_->sdc(); + InputDelaySet *input_delays = sdc->refPinInputDelays(ref_pin); + if (input_delays) { + const Graph *graph = sta_->graph(); + for (auto 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); + if (bidirect_drvr_vertex) + seedInputDelayArrival(pin, bidirect_drvr_vertex, input_delay); + } + } +} + +void +ArrivalVisitor::seedInputDelayArrival(const Pin *pin, + Vertex *vertex, + InputDelay *input_delay) +{ + TagGroupBldr tag_bldr(true, sta_); + Search *search = sta_->search(); + Network *network = sta_->network(); + tag_bldr.init(vertex); + 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_); + UniqueLock lock(pending_latch_outputs_lock_); + pending_latch_outputs_.insert(out_vertex); + } + } +} + +void +Search::seedArrivals() +{ + VertexSet vertices; + findClockVertices(vertices); + findRootVertices(vertices); + findInputDrvrVertices(vertices); + + for (auto vertex : vertices) + seedArrival(vertex); +} + +void +Search::findClockVertices(VertexSet &vertices) +{ + for (auto clk : sdc_->clks()) { + ClockVertexPinIterator pin_iter(clk); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + vertices.insert(vertex); + if (bidirect_drvr_vertex) + vertices.insert(bidirect_drvr_vertex); + } + } +} + +void +Search::seedInvalidArrivals() +{ + for (auto vertex : invalid_arrivals_) + seedArrival(vertex); + invalid_arrivals_.clear(); +} + +void +Search::seedArrival(Vertex *vertex) +{ + const Pin *pin = vertex->pin(); + if (sdc_->isVertexPinClock(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); + 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))) { + debugPrint1(debug_, "search", 2, "arrival seed unclked root %s\n", + network_->pathName(pin)); + TagGroupBldr tag_bldr(true, this); + tag_bldr.init(vertex); + if (makeUnclkedPaths(vertex, is_reg_clk, &tag_bldr)) + // Only search downstream if there were no false paths from here. + arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); + setVertexArrivals(vertex, &tag_bldr); + } + else { + deletePaths(vertex); + if (search_adj_->searchFrom(vertex)) + arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); + } + } + else { + debugPrint1(debug_, "search", 2, "arrival enqueue %s\n", + network_->pathName(pin)); + arrival_iter_->enqueue(vertex); + } +} + +// Find all of the clock vertex pins. +void +Search::findClkVertexPins(PinSet &clk_pins) +{ + for (auto clk : sdc_->clks()) { + ClockVertexPinIterator pin_iter(clk); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + clk_pins.insert(pin); + } + } +} + +void +Search::seedClkArrivals(const Pin *pin, + Vertex *vertex, + TagGroupBldr *tag_bldr) +{ + for (auto clk : *sdc_->findVertexPinClocks(pin)) { + debugPrint2(debug_, "search", 2, "arrival seed clk %s pin %s\n", + clk->name(), network_->pathName(pin)); + for (auto path_ap : corners_->pathAnalysisPts()) { + const MinMax *min_max = path_ap->pathMinMax(); + for (auto tr : TransRiseFall::range()) { + ClockEdge *clk_edge = clk->edge(tr); + const EarlyLate *early_late = min_max; + if (clk->isGenerated() + && clk->masterClk() == nullptr) + seedClkDataArrival(pin, tr, clk, clk_edge, min_max, path_ap, + 0.0, tag_bldr); + else { + Arrival insertion = clockInsertion(clk, pin, tr, min_max, + early_late, path_ap); + seedClkArrival(pin, tr, clk, clk_edge, min_max, path_ap, + insertion, tag_bldr); + } + } + } + arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); + } +} + +void +Search::seedClkArrival(const Pin *pin, + const TransRiseFall *tr, + Clock *clk, + ClockEdge *clk_edge, + const MinMax *min_max, + const PathAnalysisPt *path_ap, + Arrival insertion, + TagGroupBldr *tag_bldr) +{ + bool is_propagated = false; + float latency = 0.0; + bool latency_exists; + // Check for clk pin latency. + sdc_->clockLatency(clk, pin, tr, min_max, + latency, latency_exists); + if (!latency_exists) { + // Check for clk latency (lower priority). + sdc_->clockLatency(clk, tr, 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; + } + } + else + is_propagated = sdc_->isPropagatedClock(pin) + || clk->isPropagated(); + } + + ClockUncertainties *uncertainties = sdc_->clockUncertainties(pin); + if (uncertainties == nullptr) + uncertainties = clk->uncertainties(); + // Propagate liberty "pulse_clock" transition to transitive fanout. + LibertyPort *port = network_->libertyPort(pin); + TransRiseFall *pulse_clk_sense = (port ? port->pulseClkSense() : nullptr); + ClkInfo *clk_info = findClkInfo(clk_edge, pin, is_propagated, nullptr, false, + pulse_clk_sense, insertion, latency, + uncertainties, path_ap, nullptr); + // Only false_paths -from apply to clock tree pins. + ExceptionStateSet *states = nullptr; + sdc_->exceptionFromClkStates(pin,tr,clk,tr,min_max,states); + Tag *tag = findTag(tr, path_ap, clk_info, true, nullptr, false, states, true); + Arrival arrival(clk_edge->time() + insertion); + tag_bldr->setArrival(tag, arrival, nullptr); +} + +void +Search::seedClkDataArrival(const Pin *pin, + const TransRiseFall *tr, + Clock *clk, + ClockEdge *clk_edge, + const MinMax *min_max, + const PathAnalysisPt *path_ap, + Arrival insertion, + TagGroupBldr *tag_bldr) +{ + Tag *tag = clkDataTag(pin, clk, tr, clk_edge, insertion, min_max, path_ap); + if (tag) { + // Data arrivals include insertion delay. + Arrival arrival(clk_edge->time() + insertion); + tag_bldr->setArrival(tag, arrival, nullptr); + } +} + +Tag * +Search::clkDataTag(const Pin *pin, + Clock *clk, + const TransRiseFall *tr, + ClockEdge *clk_edge, + Arrival insertion, + const MinMax *min_max, + const PathAnalysisPt *path_ap) +{ + ExceptionStateSet *states = nullptr; + if (sdc_->exceptionFromStates(pin, tr, clk, tr, min_max, states)) { + bool is_propagated = (clk->isPropagated() + || sdc_->isPropagatedClock(pin)); + ClkInfo *clk_info = findClkInfo(clk_edge, pin, is_propagated, + insertion, path_ap); + return findTag(tr, path_ap, clk_info, false, nullptr, false, states, true); + } + else + return nullptr; +} + +//////////////////////////////////////////////////////////////// + +bool +Search::makeUnclkedPaths(Vertex *vertex, + bool is_segment_start, + TagGroupBldr *tag_bldr) +{ + bool search_from = false; + const Pin *pin = vertex->pin(); + for (auto path_ap : corners_->pathAnalysisPts()) { + const MinMax *min_max = path_ap->pathMinMax(); + for (auto tr : TransRiseFall::range()) { + Tag *tag = fromUnclkedInputTag(pin, tr, min_max, path_ap, + is_segment_start); + if (tag) { + tag_bldr->setArrival(tag, delay_zero, nullptr); + search_from = true; + } + } + } + return search_from; +} + +// Find graph roots and input ports that do NOT have arrivals. +void +Search::findRootVertices(VertexSet &vertices) +{ + for (auto vertex : levelize_->roots()) { + const Pin *pin = vertex->pin(); + if (!sdc_->isVertexPinClock(pin) + && !sdc_->hasInputDelay(pin) + && !vertex->isConstant()) { + vertices.insert(vertex); + } + } +} + +void +Search::findInputDrvrVertices(VertexSet &vertices) +{ + 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()) + vertices.insert(graph_->pinDrvrVertex(pin)); + } + delete pin_iter; +} + +bool +Search::isSegmentStart(const Pin *pin) +{ + return (sdc_->isPathDelayInternalStartpoint(pin) + || sdc_->isInputDelayInternal(pin)) + && !sdc_->isVertexPinClock(pin); +} + +bool +Search::isInputArrivalSrchStart(Vertex *vertex) +{ + const Pin *pin = vertex->pin(); + 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. + InputDelayVertexPinsIterator arrival_iter(sdc_); + while (arrival_iter.hasNext()) { + const Pin *pin = arrival_iter.next(); + if (!sdc_->isVertexPinClock(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. + VertexPinInputDelayIterator arrival_iter(pin, sdc_); + TagGroupBldr tag_bldr(true, this); + tag_bldr.init(vertex); + while (arrival_iter.hasNext()) { + InputDelay *input_delay = arrival_iter.next(); + Clock *input_clk = input_delay->clock(); + ClockSet *pin_clks = sdc_->findVertexPinClocks(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); +} + +void +Search::seedInputArrival(const Pin *pin, + Vertex *vertex, + TagGroupBldr *tag_bldr) +{ + if (sdc_->hasInputDelay(pin)) + seedInputArrival1(pin, vertex, false, tag_bldr); + else if (!sdc_->isVertexPinClock(pin)) + // Seed inputs without set_input_delays. + seedInputDelayArrival(pin, vertex, nullptr, false, tag_bldr); +} + +void +Search::seedInputSegmentArrival(const Pin *pin, + Vertex *vertex, + TagGroupBldr *tag_bldr) +{ + seedInputArrival1(pin, vertex, true, tag_bldr); +} + +void +Search::seedInputArrival1(const Pin *pin, + Vertex *vertex, + bool is_segment_start, + TagGroupBldr *tag_bldr) +{ + // There can be multiple arrivals for a pin with wrt different clocks. + VertexPinInputDelayIterator arrival_iter(pin, sdc_); + while (arrival_iter.hasNext()) { + InputDelay *input_delay = arrival_iter.next(); + Clock *input_clk = input_delay->clock(); + ClockSet *pin_clks = sdc_->findVertexPinClocks(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); + } +} + +void +Search::seedInputDelayArrival(const Pin *pin, + Vertex *vertex, + InputDelay *input_delay, + bool is_segment_start, + TagGroupBldr *tag_bldr) +{ + debugPrint1(debug_, "search", 2, + input_delay + ? "arrival seed input arrival %s\n" + : "arrival seed input %s\n", + vertex->name(sdc_network_)); + ClockEdge *clk_edge = nullptr; + const Pin *ref_pin = nullptr; + if (input_delay) { + clk_edge = input_delay->clkEdge(); + if (clk_edge == nullptr + && sdc_->useDefaultArrivalClock()) + clk_edge = sdc_->defaultArrivalClockEdge(); + ref_pin = input_delay->refPin(); + } + else if (sdc_->useDefaultArrivalClock()) + clk_edge = sdc_->defaultArrivalClockEdge(); + if (ref_pin) { + Vertex *ref_vertex = graph_->pinLoadVertex(ref_pin); + for (auto path_ap : corners_->pathAnalysisPts()) { + const MinMax *min_max = path_ap->pathMinMax(); + TransRiseFall *ref_tr = input_delay->refTransition(); + const Clock *clk = input_delay->clock(); + VertexPathIterator ref_path_iter(ref_vertex, ref_tr, 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); + } + } + } + } + else { + for (auto path_ap : corners_->pathAnalysisPts()) { + const MinMax *min_max = path_ap->pathMinMax(); + 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); + } + } +} + +// Input delays with -reference_pin use the clock network latency +// from the clock source to the reference pin. +void +Search::inputDelayRefPinArrival(Path *ref_path, + ClockEdge *clk_edge, + const MinMax *min_max, + // Return values. + float &ref_arrival, + float &ref_insertion, + float &ref_latency) +{ + Clock *clk = clk_edge->clock(); + if (clk->isPropagated()) { + ClkInfo *clk_info = ref_path->clkInfo(this); + ref_arrival = delayAsFloat(ref_path->arrival(this)); + ref_insertion = delayAsFloat(clk_info->insertion()); + ref_latency = clk_info->latency(); + } + else { + const TransRiseFall *clk_tr = clk_edge->transition(); + 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_tr, min_max, early_late); + ref_arrival = clk_edge->time() + ref_insertion; + ref_latency = 0.0; + } +} + +void +Search::seedInputDelayArrival(const Pin *pin, + InputDelay *input_delay, + 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) +{ + for (auto tr : TransRiseFall::range()) { + if (input_delay) { + float delay; + bool exists; + input_delay->delays()->value(tr, min_max, delay, exists); + if (exists) + seedInputDelayArrival(pin, tr, clk_arrival + delay, + input_delay, clk_edge, + clk_insertion, clk_latency, is_segment_start, + min_max, path_ap, tag_bldr); + } + else + seedInputDelayArrival(pin, tr, 0.0, nullptr, clk_edge, + clk_insertion, clk_latency, is_segment_start, + min_max, path_ap, tag_bldr); + } +} + +void +Search::seedInputDelayArrival(const Pin *pin, + const TransRiseFall *tr, + float arrival, + InputDelay *input_delay, + ClockEdge *clk_edge, + float clk_insertion, + float clk_latency, + bool is_segment_start, + const MinMax *min_max, + PathAnalysisPt *path_ap, + TagGroupBldr *tag_bldr) +{ + Tag *tag = inputDelayTag(pin, tr, clk_edge, clk_insertion, clk_latency, + input_delay, is_segment_start, min_max, path_ap); + if (tag) + tag_bldr->setArrival(tag, arrival, nullptr); +} + +void +Search::inputDelayClkArrival(InputDelay *input_delay, + ClockEdge *clk_edge, + const MinMax *min_max, + const PathAnalysisPt *path_ap, + // Return values. + float &clk_arrival, float &clk_insertion, + float &clk_latency) +{ + clk_arrival = 0.0; + clk_insertion = 0.0; + clk_latency = 0.0; + if (input_delay && clk_edge) { + clk_arrival = clk_edge->time(); + Clock *clk = clk_edge->clock(); + TransRiseFall *clk_tr = clk_edge->transition(); + if (!input_delay->sourceLatencyIncluded()) { + const EarlyLate *early_late = min_max; + clk_insertion = delayAsFloat(clockInsertion(clk, clk->defaultPin(), + clk_tr, min_max, early_late, + path_ap)); + clk_arrival += clk_insertion; + } + if (!clk->isPropagated() + && !input_delay->networkLatencyIncluded()) { + clk_latency = sdc_->clockLatency(clk, clk_tr, min_max); + clk_arrival += clk_latency; + } + } +} + +Tag * +Search::inputDelayTag(const Pin *pin, + const TransRiseFall *tr, + ClockEdge *clk_edge, + float clk_insertion, + float clk_latency, + InputDelay *input_delay, + bool is_segment_start, + const MinMax *min_max, + const PathAnalysisPt *path_ap) +{ + Clock *clk = nullptr; + Pin *clk_pin = nullptr; + TransRiseFall *clk_tr = nullptr; + bool is_propagated = false; + ClockUncertainties *clk_uncertainties = nullptr; + if (clk_edge) { + clk = clk_edge->clock(); + clk_tr = clk_edge->transition(); + clk_pin = clk->defaultPin(); + is_propagated = clk->isPropagated(); + clk_uncertainties = clk->uncertainties(); + } + + ExceptionStateSet *states = nullptr; + Tag *tag = nullptr; + if (sdc_->exceptionFromStates(pin,tr,clk,clk_tr,min_max,states)) { + ClkInfo *clk_info = findClkInfo(clk_edge, clk_pin, is_propagated, nullptr, + false, nullptr, clk_insertion, clk_latency, + clk_uncertainties, path_ap, nullptr); + tag = findTag(tr, path_ap, clk_info, false, input_delay, is_segment_start, + states, true); + } + + if (tag) { + ClkInfo *clk_info = tag->clkInfo(); + // Check for state changes on existing tag exceptions (pending -thru pins). + tag = mutateTag(tag, pin, tr, false, clk_info, + pin, tr, false, false, is_segment_start, clk_info, + input_delay, min_max, path_ap); + } + return tag; +} + +//////////////////////////////////////////////////////////////// + +PathVisitor::PathVisitor(const StaState *sta) : + pred_(sta->search()->evalPred()), + sta_(sta) +{ +} + +PathVisitor::PathVisitor(SearchPred *pred, + const StaState *sta) : + pred_(pred), + sta_(sta) +{ +} + +void +PathVisitor::visitFaninPaths(Vertex *to_vertex) +{ + if (pred_->searchTo(to_vertex)) { + const Graph *graph = sta_->graph(); + VertexInEdgeIterator edge_iter(to_vertex, graph); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + 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; + } + } + } +} + +void +PathVisitor::visitFanoutPaths(Vertex *from_vertex) +{ + const Pin *from_pin = from_vertex->pin(); + if (pred_->searchFrom(from_vertex)) { + const Graph *graph = sta_->graph(); + 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)) { + debugPrint1(sta_->debug(), "search", 3, + " %s\n", to_vertex->name(sta_->network())); + 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) +{ + Search *search = sta_->search(); + TagGroup *from_tag_group = search->tagGroup(from_vertex); + if (from_tag_group) { + TimingArcSet *arc_set = edge->timingArcSet(); + VertexPathIterator from_iter(from_vertex, search); + while (from_iter.hasNext()) { + PathVertex *from_path = from_iter.next(); + Tag *from_tag = from_path->tag(sta_); + // Only propagate seeded paths from segment startpoint. + if (!search->isSegmentStart(from_pin) + || from_tag->isSegmentStart()) { + PathAnalysisPt *path_ap = from_path->pathAnalysisPt(sta_); + const MinMax *min_max = path_ap->pathMinMax(); + const TransRiseFall *from_tr = from_path->transition(sta_); + // Do not propagate paths from a clock source unless they are + // defined on the from pin. + if (!search->pathPropagatedToClkSrc(from_pin, from_path)) { + TimingArc *arc1, *arc2; + arc_set->arcsFrom(from_tr, arc1, arc2); + if (!visitArc(from_pin, from_vertex, from_tr, from_path, + edge, arc1, to_pin, to_vertex, + min_max, path_ap)) + return false; + if (!visitArc(from_pin, from_vertex, from_tr, from_path, + edge, arc2, to_pin, to_vertex, + min_max, path_ap)) + return false; + } + } + } + } + return true; +} + +bool +PathVisitor::visitArc(const Pin *from_pin, + Vertex *from_vertex, + const TransRiseFall *from_tr, + PathVertex *from_path, + Edge *edge, + TimingArc *arc, + const Pin *to_pin, + Vertex *to_vertex, + const MinMax *min_max, + PathAnalysisPt *path_ap) +{ + if (arc) { + TransRiseFall *to_tr = arc->toTrans()->asRiseFall(); + if (searchThru(from_vertex, from_tr, edge, to_vertex, to_tr)) + return visitFromPath(from_pin, from_vertex, from_tr, from_path, + edge, arc, to_pin, to_vertex, to_tr, + min_max, path_ap); + } + return true; +} + +bool +Search::pathPropagatedToClkSrc(const Pin *pin, + Path *path) +{ + const Tag *tag = path->tag(this); + if (!tag->isGenClkSrcPath() + // Clock source can have input arrivals from unrelated clock. + && tag->inputDelay() == nullptr + && sdc_->isPathDelayInternalEndpoint(pin)) { + ClockSet *clks = sdc_->findVertexPinClocks(pin); + return clks + && !clks->hasKey(tag->clock()); + } + else + return false; +} + +bool +PathVisitor::visitFromPath(const Pin *from_pin, + Vertex *from_vertex, + const TransRiseFall *from_tr, + PathVertex *from_path, + Edge *edge, + TimingArc *arc, + const Pin *to_pin, + Vertex *to_vertex, + const TransRiseFall *to_tr, + const MinMax *min_max, + const PathAnalysisPt *path_ap) +{ + Network *network = sta_->network(); + Sdc *sdc = sta_->sdc(); + Search *search = sta_->search(); + Latches *latches = sta_->latches(); + const TimingRole *role = edge->role(); + Tag *from_tag = from_path->tag(sta_); + ClkInfo *from_clk_info = from_tag->clkInfo(); + Tag *to_tag = nullptr; + ClockEdge *clk_edge = from_clk_info->clkEdge(); + Clock *clk = from_clk_info->clock(); + Arrival from_arrival = from_path->arrival(sta_); + ArcDelay arc_delay = 0.0; + Arrival to_arrival; + if (from_clk_info->isGenClkSrcPath()) { + if (!sdc->clkStopPropagation(clk,from_pin,from_tr,to_pin,to_tr) + && (sdc->clkThruTristateEnabled() + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable()))) { + Clock *gclk = from_tag->genClkSrcPathClk(sta_); + 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))) { + to_tag = search->thruClkTag(from_path, from_tag, true, edge, to_tr, + min_max, path_ap); + if (to_tag) { + arc_delay = search->deratedDelay(from_vertex, arc, edge, true, + path_ap); + to_arrival = from_arrival + arc_delay; + } + } + } + else { + // PLL out to feedback path. + to_tag = search->thruTag(from_tag, edge, to_tr, min_max, path_ap); + if (to_tag) { + arc_delay = search->deratedDelay(from_vertex, arc, edge, true, + path_ap); + to_arrival = from_arrival + arc_delay; + } + } + } + } + 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); + // 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 TransRiseFall *clk_tr = clk_edge ? clk_edge->transition() : nullptr; + ClkInfo *to_clk_info = from_clk_info; + if (network->direction(to_pin)->isInternal()) + to_clk_info = search->clkInfoWithCrprClkPath(from_clk_info, + from_path, path_ap); + to_tag = search->fromRegClkTag(from_pin, from_tr, clk, clk_tr, + to_clk_info, to_pin, to_tr, min_max, + path_ap); + if (to_tag) + to_tag = search->thruTag(to_tag, edge, to_tr, min_max, path_ap); + from_arrival = search->clkPathArrival(from_path, from_clk_info, + clk_edge, min_max, path_ap); + to_arrival = from_arrival + arc_delay; + } + else + to_tag = nullptr; + } + } + else if (edge->role() == TimingRole::latchDtoQ()) { + if (min_max == MinMax::max()) { + 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); + if (to_tag) + to_tag = search->thruTag(to_tag, edge, to_tr, min_max, path_ap); + } + } + else if (from_tag->isClock()) { + // 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))) { + // Propagate arrival as non-clock at the end of the clock tree. + bool to_propagates_clk = + !sdc->clkStopPropagation(clk,from_pin,from_tr,to_pin,to_tr) + && (sdc->clkThruTristateEnabled() + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())); + arc_delay = search->deratedDelay(from_vertex, arc, edge, + to_propagates_clk, path_ap); + to_tag = search->thruClkTag(from_path, from_tag, to_propagates_clk, + edge, to_tr, min_max, path_ap); + to_arrival = from_arrival + arc_delay; + } + } + else { + arc_delay = search->deratedDelay(from_vertex, arc, edge, false, path_ap); + if (!fuzzyEqual(arc_delay, min_max->initValue())) { + to_arrival = from_arrival + arc_delay; + to_tag = search->thruTag(from_tag, edge, to_tr, min_max, path_ap); + } + } + if (to_tag) + return visitFromToPath(from_pin, from_vertex, from_tr, from_tag, from_path, + edge, arc, arc_delay, + to_vertex, to_tr, to_tag, to_arrival, + min_max, path_ap); + else + return true; +} + +Arrival +Search::clkPathArrival(const Path *clk_path) const +{ + ClkInfo *clk_info = clk_path->clkInfo(this); + 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); +} + +Arrival +Search::clkPathArrival(const Path *clk_path, + ClkInfo *clk_info, + ClockEdge *clk_edge, + const MinMax *min_max, + const PathAnalysisPt *path_ap) const +{ + 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(); + } + else + return clk_path->arrival(this); +} + +Arrival +Search::pathClkPathArrival(const Path *path) const +{ + PathRef src_clk_path; + pathClkPathArrival1(path, src_clk_path); + if (!src_clk_path.isNull()) + return clkPathArrival(&src_clk_path); + else + return 0.0; +} + +// PathExpanded::expand() and PathExpanded::clkPath(). +void +Search::pathClkPathArrival1(const Path *path, + // Return value. + PathRef &clk_path) const +{ + PathRef p(path); + while (!p.isNull()) { + PathRef prev_path; + TimingArc *prev_arc; + p.prevPath(this, prev_path, prev_arc); + + if (p.isClock(this)) { + clk_path.init(p); + return; + } + if (prev_arc) { + TimingRole *prev_role = prev_arc->role(); + if (prev_role == TimingRole::regClkToQ() + || prev_role == TimingRole::latchEnToQ()) { + p.prevPath(this, prev_path, prev_arc); + clk_path.init(prev_path); + return; + } + else if (prev_role == TimingRole::latchDtoQ()) { + Edge *prev_edge = p.prevEdge(prev_arc, this); + PathVertex enable_path; + latches_->latchEnablePath(&p, prev_edge, enable_path); + clk_path.init(enable_path); + return; + } + } + p.init(prev_path); + } +} + +//////////////////////////////////////////////////////////////// + +// Find tag for a path starting with pin/clk_edge. +// Return nullptr if a false path starts at pin/clk_edge. +Tag * +Search::fromUnclkedInputTag(const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max, + const PathAnalysisPt *path_ap, + bool is_segment_start) +{ + ExceptionStateSet *states = nullptr; + if (sdc_->exceptionFromStates(pin, tr, nullptr, nullptr, min_max, states)) { + ClkInfo *clk_info = findClkInfo(nullptr, nullptr, false, 0.0, path_ap); + return findTag(tr, path_ap, clk_info, false, nullptr, + is_segment_start, states, true); + } + else + return nullptr; +} + +Tag * +Search::fromRegClkTag(const Pin *from_pin, + const TransRiseFall *from_tr, + Clock *clk, + const TransRiseFall *clk_tr, + ClkInfo *clk_info, + const Pin *to_pin, + const TransRiseFall *to_tr, + const MinMax *min_max, + const PathAnalysisPt *path_ap) +{ + ExceptionStateSet *states = nullptr; + if (sdc_->exceptionFromStates(from_pin, from_tr, clk, clk_tr, + min_max, states)) { + // Hack for filter -from reg/Q. + sdc_->filterRegQStates(to_pin, to_tr, min_max, states); + return findTag(to_tr, path_ap, clk_info, false, nullptr, false, states, true); + } + else + return nullptr; +} + +// Insert from_path as ClkInfo crpr_clk_path. +ClkInfo * +Search::clkInfoWithCrprClkPath(ClkInfo *from_clk_info, + PathVertex *from_path, + const PathAnalysisPt *path_ap) +{ + if (sdc_->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); + else + return from_clk_info; +} + +// Find tag for a path starting with from_tag going thru edge. +// Return nullptr if the result tag completes a false path. +Tag * +Search::thruTag(Tag *from_tag, + Edge *edge, + const TransRiseFall *to_tr, + const MinMax *min_max, + const PathAnalysisPt *path_ap) +{ + const Pin *from_pin = edge->from(graph_)->pin(); + Vertex *to_vertex = edge->to(graph_); + const Pin *to_pin = to_vertex->pin(); + const TransRiseFall *from_tr = from_tag->transition(); + ClkInfo *from_clk_info = from_tag->clkInfo(); + bool to_is_reg_clk = to_vertex->isRegClk(); + Tag *to_tag = mutateTag(from_tag, from_pin, from_tr, false, from_clk_info, + to_pin, to_tr, false, to_is_reg_clk, false, + // input delay is not propagated. + from_clk_info, nullptr, min_max, path_ap); + return to_tag; +} + +Tag * +Search::thruClkTag(PathVertex *from_path, + Tag *from_tag, + bool to_propagates_clk, + Edge *edge, + const TransRiseFall *to_tr, + const MinMax *min_max, + const PathAnalysisPt *path_ap) +{ + const Pin *from_pin = edge->from(graph_)->pin(); + Vertex *to_vertex = edge->to(graph_); + const Pin *to_pin = to_vertex->pin(); + const TransRiseFall *from_tr = from_tag->transition(); + ClkInfo *from_clk_info = from_tag->clkInfo(); + bool from_is_clk = from_tag->isClock(); + bool to_is_reg_clk = to_vertex->isRegClk(); + TimingRole *role = edge->role(); + bool to_is_clk = (from_is_clk + && to_propagates_clk + && (role->isWire() + || role == TimingRole::combinational())); + ClkInfo *to_clk_info = thruClkInfo(from_path, from_clk_info, + edge, to_vertex, to_pin, min_max, path_ap); + Tag *to_tag = mutateTag(from_tag,from_pin,from_tr,from_is_clk,from_clk_info, + to_pin, to_tr, to_is_clk, to_is_reg_clk, false, + to_clk_info, nullptr, min_max, path_ap); + return to_tag; +} + +// thruTag for clocks. +ClkInfo * +Search::thruClkInfo(PathVertex *from_path, + ClkInfo *from_clk_info, + Edge *edge, + Vertex *to_vertex, + const Pin *to_pin, + const MinMax *min_max, + const PathAnalysisPt *path_ap) +{ + ClkInfo *to_clk_info = from_clk_info; + bool changed = false; + ClockEdge *from_clk_edge = from_clk_info->clkEdge(); + const TransRiseFall *clk_tr = 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)) { + to_clk_prop = true; + changed = true; + } + + // Distinguish gen clk src path ClkInfo at generated clock roots, + // 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() + && sdc_->crprActive() + && sdc_->isClock(to_pin)) { + // Don't care that it could be a regular clock root. + gen_clk_src = to_pin; + changed = true; + } + + PathVertex *to_crpr_clk_path = nullptr; + if (sdc_->crprActive() + && to_vertex->isRegClk()) { + to_crpr_clk_path = from_path; + changed = true; + } + + // Propagate liberty "pulse_clock" transition to transitive fanout. + TransRiseFall *from_pulse_sense = from_clk_info->pulseClkSense(); + TransRiseFall *to_pulse_sense = from_pulse_sense; + LibertyPort *port = network_->libertyPort(to_pin); + if (port && port->pulseClkSense()) { + to_pulse_sense = port->pulseClkSense(); + changed = true; + } + else if (from_pulse_sense && + edge->timingArcSet()->sense() == TimingSense::negative_unate) { + to_pulse_sense = from_pulse_sense->opposite(); + changed = true; + } + + Clock *from_clk = from_clk_info->clock(); + Arrival to_insertion = from_clk_info->insertion(); + float to_latency = from_clk_info->latency(); + float latency; + bool exists; + sdc_->clockLatency(from_clk, to_pin, clk_tr, min_max, + latency, exists); + if (exists) { + // Latency on pin has precidence over fanin or hierarchical + // pin latency. + to_latency = latency; + to_clk_prop = false; + changed = true; + } + else { + // Check for hierarchical pin latency thru edge. + sdc_->clockLatency(edge, clk_tr, min_max, + latency, exists); + if (exists) { + to_latency = latency; + to_clk_prop = false; + changed = true; + } + } + + ClockUncertainties *to_uncertainties = from_clk_info->uncertainties(); + ClockUncertainties *uncertainties = sdc_->clockUncertainties(to_pin); + if (uncertainties) { + to_uncertainties = uncertainties; + changed = true; + } + + if (changed) + 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; +} + +// Find the tag for a path going from from_tag thru edge to to_pin. +Tag * +Search::mutateTag(Tag *from_tag, + const Pin *from_pin, + const TransRiseFall *from_tr, + bool from_is_clk, + ClkInfo *from_clk_info, + const Pin *to_pin, + const TransRiseFall *to_tr, + bool to_is_clk, + bool to_is_reg_clk, + bool to_is_segment_start, + ClkInfo *to_clk_info, + InputDelay *to_input_delay, + const MinMax *min_max, + const PathAnalysisPt *path_ap) +{ + ExceptionStateSet *new_states = nullptr; + ExceptionStateSet *from_states = from_tag->states(); + if (from_states) { + // Check for state changes in from_tag (but postpone copying state set). + bool state_change = false; + for (auto state : *from_states) { + ExceptionPath *exception = state->exception(); + if (state->isComplete() + && exception->isFalse() + && !from_is_clk) + // Don't propagate a completed false path -thru unless it is a + // clock (which ignores exceptions). + return nullptr; + if (state->matchesNextThru(from_pin,to_pin,to_tr,min_max,network_)) { + // Found a -thru that we've been waiting for. + if (state->nextState()->isComplete() + && exception->isLoop()) + // to_pin/edge completes a loop path. + return nullptr; + state_change = true; + break; + } + // Kill loop tags at register clock pins. + if (to_is_reg_clk && exception->isLoop()) { + state_change = true; + break; + } + } + // Get the set of -thru exceptions starting at to_pin/edge. + sdc_->exceptionThruStates(from_pin, to_pin, to_tr, 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; + for (auto state : *from_states) { + ExceptionPath *exception = state->exception(); + if (state->isComplete() + && exception->isFalse() + && !from_is_clk) { + // 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. + delete new_states; + return nullptr; + } + // One edge may traverse multiple hierarchical thru pins. + while (state->matchesNextThru(from_pin,to_pin,to_tr,min_max,network_)) + // Found a -thru that we've been waiting for. + state = state->nextState(); + + if (state->isComplete() + && exception->isLoop()) { + // to_pin/edge completes a loop path. + delete new_states; + return nullptr; + } + + // Kill loop tags at register clock pins. + if (!(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_tr, min_max, new_states); + + if (new_states) + return findTag(to_tr, path_ap, to_clk_info, to_is_clk, + from_tag->inputDelay(), to_is_segment_start, + new_states, true); + else { + // No state change. + if (to_clk_info == from_clk_info + && to_tr == from_tr + && to_is_clk == from_is_clk + && from_tag->isSegmentStart() == to_is_segment_start + && from_tag->inputDelay() == to_input_delay) + return from_tag; + else + return findTag(to_tr, path_ap, to_clk_info, to_is_clk, + to_input_delay, to_is_segment_start, + from_states, false); + } +} + +TagGroup * +Search::findTagGroup(TagGroupBldr *tag_bldr) +{ + TagGroup probe(tag_bldr); + TagGroup *tag_group = tag_group_set_->findKey(&probe); + if (tag_group == nullptr) { + // Recheck with lock. + UniqueLock lock(tag_group_lock_); + tag_group = tag_group_set_->findKey(&probe); + if (tag_group == nullptr) { + TagGroupIndex tag_group_index; + if (tag_group_free_indices_.empty()) + tag_group_index = tag_group_next_++; + else { + tag_group_index = tag_group_free_indices_.back(); + tag_group_free_indices_.pop_back(); + } + tag_group = tag_bldr->makeTagGroup(tag_group_index, this); + tag_groups_[tag_group_index] = tag_group; + tag_group_set_->insert(tag_group); + // If tag_groups_ needs to grow make the new array and copy the + // contents into it before updating tags_groups_ so that other threads + // can use Search::tagGroup(TagGroupIndex) without returning gubbish. + // std::vector doesn't seem to follow this protocol so multi-thread + // search fails occasionally if a vector is used for tag_groups_. + if (tag_group_next_ == tag_group_capacity_) { + TagGroupIndex new_capacity = nextMersenne(tag_group_capacity_); + TagGroup **new_tag_groups = new TagGroup*[new_capacity]; + memcpy(new_tag_groups, tag_groups_, + tag_group_capacity_ * sizeof(TagGroup*)); + TagGroup **old_tag_groups = tag_groups_; + tag_groups_ = new_tag_groups; + tag_group_capacity_ = new_capacity; + delete [] old_tag_groups; + tag_group_set_->resize(new_capacity); + } + if (tag_group_next_ > tag_group_index_max) + internalError("max tag group index exceeded"); + } + } + return tag_group; +} + +void +Search::setVertexArrivals(Vertex *vertex, + TagGroupBldr *tag_bldr) +{ + if (tag_bldr->empty()) + deletePaths(vertex); + else { + TagGroup *prev_tag_group = tagGroup(vertex); + Arrival *prev_arrivals = vertex->arrivals(); + PathVertexRep *prev_paths = vertex->prevPaths(); + + TagGroup *tag_group = findTagGroup(tag_bldr); + int arrival_count = tag_group->arrivalCount(); + bool has_requireds = vertex->hasRequireds(); + // Reuse arrival array if it is the same size. + if (prev_tag_group + && arrival_count == prev_tag_group->arrivalCount() + && (!has_requireds + // Requireds can only be reused if the tag group is unchanged. + || tag_group == prev_tag_group)) { + if (tag_bldr->hasClkTag() || tag_bldr->hasGenClkSrcTag()) { + if (prev_paths == nullptr) + prev_paths = new PathVertexRep[arrival_count]; + } + else { + // Prev paths not required, delete stale ones. + delete [] prev_paths; + prev_paths = nullptr; + vertex->setPrevPaths(nullptr); + } + tag_bldr->copyArrivals(tag_group, prev_arrivals, prev_paths); + vertex->setTagGroupIndex(tag_group->index()); + } + else { + delete [] prev_arrivals; + delete [] prev_paths; + + Arrival *arrivals = new Arrival[arrival_count]; + prev_paths = nullptr; + if (tag_bldr->hasClkTag() || tag_bldr->hasGenClkSrcTag()) + prev_paths = new PathVertexRep[arrival_count]; + tag_bldr->copyArrivals(tag_group, arrivals, prev_paths); + + vertex->setTagGroupIndex(tag_group->index()); + vertex->setArrivals(arrivals); + vertex->setPrevPaths(prev_paths); + + if (has_requireds) { + requiredInvalid(vertex); + vertex->setHasRequireds(false); + } + } + } +} + +void +Search::reportArrivals(Vertex *vertex) const +{ + report_->print("Vertex %s\n", vertex->name(sdc_network_)); + TagGroup *tag_group = tagGroup(vertex); + Arrival *arrivals = vertex->arrivals(); + if (tag_group) { + report_->print("Group %u\n", tag_group->index()); + ArrivalMap::Iterator arrival_iter(tag_group->arrivalMap()); + while (arrival_iter.hasNext()) { + Tag *tag; + int arrival_index; + arrival_iter.next(tag, arrival_index); + PathAnalysisPt *path_ap = tag->pathAnalysisPt(this); + const TransRiseFall *tr = tag->transition(); + report_->print(" %d %s %s %s", + arrival_index, + tr->asString(), + path_ap->pathMinMax()->asString(), + delayAsString(arrivals[arrival_index], this)); + if (vertex->hasRequireds()) { + int req_index; + bool exists; + tag_group->requiredIndex(tag, req_index, exists); + if (exists) + report_->print(" / %s", delayAsString(arrivals[req_index], this)); + } + report_->print(" %s", tag->asString(this)); + if (tag_group->hasClkTag()) { + PathVertex tmp; + PathVertex *prev = check_crpr_->clkPathPrev(vertex, arrival_index, tmp); + report_->print(" clk_prev=[%s]", + prev && !prev->isNull() ? prev->name(this) : "NULL"); + } + report_->print("\n"); + } + } + else + report_->print(" no arrivals\n"); +} + +TagGroup * +Search::tagGroup(TagGroupIndex index) const +{ + return tag_groups_[index]; +} + +TagGroup * +Search::tagGroup(const Vertex *vertex) const +{ + TagGroupIndex index = vertex->tagGroupIndex(); + if (index == tag_group_index_max) + return nullptr; + else + return tag_groups_[index]; +} + +TagGroupIndex +Search::tagGroupCount() const +{ + return tag_group_set_->size(); +} + +void +Search::reportTagGroups() const +{ + for (TagGroupIndex i = 0; i < tag_group_next_; i++) { + TagGroup *tag_group = tag_groups_[i]; + if (tag_group) { + report_->print("Group %4u hash = %4u (%4u)\n", + i, + tag_group->hash(), + tag_group->hash() % tag_group_set_->capacity()); + tag_group->reportArrivalMap(this); + } + } + size_t long_hash = tag_group_set_->longestBucketHash(); + report_->print("Longest hash bucket length %lu hash=%lu\n", + tag_group_set_->bucketLength(long_hash), + long_hash); +} + +void +Search::reportArrivalCountHistogram() const +{ + Vector vertex_counts(10); + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + TagGroup *tag_group = tagGroup(vertex); + if (tag_group) { + int arrival_count = tag_group->arrivalCount(); + if (arrival_count >= static_cast(vertex_counts.size())) + vertex_counts.resize(arrival_count * 2); + vertex_counts[arrival_count]++; + } + } + + for (int arrival_count = 0; + arrival_count < static_cast(vertex_counts.size()); + arrival_count++) { + int vertex_count = vertex_counts[arrival_count]; + if (vertex_count > 0) + report_->print("%6d %6d\n", arrival_count, vertex_count); + } +} + +//////////////////////////////////////////////////////////////// + +Tag * +Search::tag(TagIndex index) const +{ + Tag *tag = tags_[index]; + return tag; +} + +TagIndex +Search::tagCount() const +{ + return tag_set_->size(); +} + +Tag * +Search::findTag(const TransRiseFall *tr, + const PathAnalysisPt *path_ap, + ClkInfo *clk_info, + bool is_clk, + InputDelay *input_delay, + bool is_segment_start, + ExceptionStateSet *states, + bool own_states) +{ + Tag probe(0, tr->index(), path_ap->index(), clk_info, is_clk, input_delay, + is_segment_start, states, false, this); + Tag *tag = tag_set_->findKey(&probe); + if (tag == nullptr) { + // Recheck with lock. + UniqueLock lock(tag_lock_); + tag = tag_set_->findKey(&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(); + } + tag = new Tag(tag_index, tr->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. + // std::vector doesn't seem to follow this protocol so multi-thread + // search fails occasionally if a vector is used for tags_. + if (tag_next_ == tag_capacity_) { + TagIndex new_capacity = nextMersenne(tag_capacity_); + Tag **new_tags = new Tag*[new_capacity]; + memcpy(new_tags, tags_, tag_capacity_ * sizeof(Tag*)); + Tag **old_tags = tags_; + tags_ = new_tags; + delete [] old_tags; + tag_capacity_ = new_capacity; + tag_set_->resize(new_capacity); + } + if (tag_next_ > tag_index_max) + internalError("max tag index exceeded"); + } + } + if (own_states) + delete states; + return tag; +} + +void +Search::reportTags() const +{ + for (TagIndex i = 0; i < tag_next_; i++) { + Tag *tag = tags_[i]; + if (tag) + report_->print("Tag %4u %4u %s\n", + tag->index(), + tag->hash() % tag_set_->capacity(), + tag->asString(false, this)) ; + } + size_t long_hash = tag_set_->longestBucketHash(); + printf("Longest hash bucket length %d hash=%zu\n", + tag_set_->bucketLength(long_hash), + long_hash); +} + +void +Search::reportClkInfos() const +{ + Vector clk_infos; + // set -> vector for sorting. + for (auto clk_info : *clk_info_set_) + clk_infos.push_back(clk_info); + sort(clk_infos, ClkInfoLess(this)); + for (auto clk_info : clk_infos) + report_->print("ClkInfo %s\n", + clk_info->asString(this)); + printf("%lu clk infos\n", + clk_info_set_->size()); +} + +ClkInfo * +Search::findClkInfo(ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, + const Pin *gen_clk_src, + bool gen_clk_src_path, + const TransRiseFall *pulse_clk_sense, + Arrival insertion, + float latency, + ClockUncertainties *uncertainties, + const PathAnalysisPt *path_ap, + PathVertex *crpr_clk_path) +{ + PathVertexRep crpr_clk_path_rep(crpr_clk_path, this); + 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_rep, this); + UniqueLock lock(clk_info_lock_); + ClkInfo *clk_info = clk_info_set_->findKey(&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_rep, this); + clk_info_set_->insert(clk_info); + } + return clk_info; +} + +ClkInfo * +Search::findClkInfo(ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, + Arrival insertion, + const PathAnalysisPt *path_ap) +{ + return findClkInfo(clk_edge, clk_src, is_propagated, nullptr, false, nullptr, + insertion, 0.0, nullptr, path_ap, nullptr); +} + +int +Search::clkInfoCount() const +{ + return clk_info_set_->size(); +} + +ArcDelay +Search::deratedDelay(Vertex *from_vertex, + TimingArc *arc, + Edge *edge, + bool is_clk, + const PathAnalysisPt *path_ap) +{ + 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 +Search::timingDerate(Vertex *from_vertex, + TimingArc *arc, + Edge *edge, + bool is_clk, + const PathAnalysisPt *path_ap) +{ + PathClkOrData derate_clk_data = + is_clk ? PathClkOrData::clk : PathClkOrData::data; + TimingRole *role = edge->role(); + const Pin *pin = from_vertex->pin(); + if (role->isWire()) { + const TransRiseFall *tr = arc->toTrans()->asRiseFall(); + return sdc_->timingDerateNet(pin, derate_clk_data, tr, + path_ap->pathMinMax()); + } + else { + TimingDerateType derate_type; + const TransRiseFall *tr; + if (role->isTimingCheck()) { + derate_type = TimingDerateType::cell_check; + tr = arc->toTrans()->asRiseFall(); + } + else { + derate_type = TimingDerateType::cell_delay; + tr = arc->fromTrans()->asRiseFall(); + } + return sdc_->timingDerateInstance(pin, derate_type, derate_clk_data, tr, + path_ap->pathMinMax()); + } +} + +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(path->clock(this)); + } +} + +bool +Search::isClock(const Vertex *vertex) const +{ + TagGroup *tag_group = tagGroup(vertex); + if (tag_group) + return tag_group->hasClkTag(); + else + return false; +} + +bool +Search::isGenClkSrc(const Vertex *vertex) const +{ + TagGroup *tag_group = tagGroup(vertex); + if (tag_group) + return tag_group->hasGenClkSrcTag(); + else + return false; +} + +void +Search::clocks(const Pin *pin, + // Return value. + ClockSet &clks) const +{ + Vertex *vertex; + Vertex *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex) + clocks(vertex, clks); + if (bidirect_drvr_vertex) + clocks(bidirect_drvr_vertex, clks); +} + +//////////////////////////////////////////////////////////////// + +void +Search::findRequireds() +{ + findRequireds(0); +} + +void +Search::findRequireds(Level level) +{ + Stats stats(debug_); + debugPrint1(debug_, "search", 1, "find requireds to level %d\n", level); + RequiredVisitor req_visitor(this); + if (!requireds_seeded_) + seedRequireds(); + seedInvalidRequireds(); + int required_count = required_iter_->visitParallel(level, &req_visitor); + requireds_exist_ = true; + debugPrint1(debug_, "search", 1, "found %d requireds\n", required_count); + stats.report("Find requireds"); +} + +void +Search::seedRequireds() +{ + ensureDownstreamClkPins(); + for (auto vertex : *endpoints()) + seedRequired(vertex); + requireds_seeded_ = true; + requireds_exist_ = true; +} + +VertexSet * +Search::endpoints() +{ + if (endpoints_ == nullptr) { + endpoints_ = new VertexSet; + invalid_endpoints_ = new VertexSet; + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + if (isEndpoint(vertex)) { + debugPrint1(debug_, "endpoint", 2, "insert %s\n", + vertex->name(sdc_network_)); + endpoints_->insert(vertex); + } + } + } + if (invalid_endpoints_) { + for (auto vertex : *invalid_endpoints_) { + if (isEndpoint(vertex)) { + debugPrint1(debug_, "endpoint", 2, "insert %s\n", + vertex->name(sdc_network_)); + endpoints_->insert(vertex); + } + else { + if (debug_->check("endpoint", 2) + && endpoints_->hasKey(vertex)) + debug_->print("endpoint: remove %s\n", vertex->name(sdc_network_)); + endpoints_->erase(vertex); + } + } + invalid_endpoints_->clear(); + } + return endpoints_; +} + +void +Search::endpointInvalid(Vertex *vertex) +{ + if (invalid_endpoints_) { + debugPrint1(debug_, "endpoint", 2, "invalid %s\n", + vertex->name(sdc_network_)); + invalid_endpoints_->insert(vertex); + } +} + +bool +Search::isEndpoint(Vertex *vertex) const +{ + return isEndpoint(vertex, search_adj_); +} + +bool +Search::isEndpoint(Vertex *vertex, + SearchPred *pred) const +{ + Pin *pin = vertex->pin(); + return hasFanin(vertex, pred, graph_) + && ((vertex->hasChecks() + && hasEnabledChecks(vertex)) + || (sdc_->gatedClkChecksEnabled() + && gated_clk_->isGatedClkEnable(vertex)) + || vertex->isConstrained() + || sdc_->isPathDelayInternalEndpoint(pin) + || !hasFanout(vertex, pred, graph_) + // Unconstrained paths at register clk pins. + || (unconstrained_paths_ + && vertex->isRegClk())); +} + +bool +Search::hasEnabledChecks(Vertex *vertex) const +{ + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (visit_path_ends_->checkEdgeEnabled(edge)) + return true; + } + return false; +} + +void +Search::endpointsInvalid() +{ + delete endpoints_; + delete invalid_endpoints_; + endpoints_ = nullptr; + invalid_endpoints_ = nullptr; +} + +void +Search::seedInvalidRequireds() +{ + for (auto vertex : invalid_requireds_) + required_iter_->enqueue(vertex); + invalid_requireds_.clear(); +} + +//////////////////////////////////////////////////////////////// + +// Visitor used by visitPathEnds to seed end required time. +class FindEndRequiredVisitor : public PathEndVisitor +{ +public: + FindEndRequiredVisitor(RequiredCmp *required_cmp, + const StaState *sta); + FindEndRequiredVisitor(const StaState *sta); + virtual ~FindEndRequiredVisitor(); + virtual PathEndVisitor *copy(); + virtual void visit(PathEnd *path_end); + +protected: + const StaState *sta_; + RequiredCmp *required_cmp_; + bool own_required_cmp_; +}; + +FindEndRequiredVisitor::FindEndRequiredVisitor(RequiredCmp *required_cmp, + const StaState *sta) : + sta_(sta), + required_cmp_(required_cmp), + own_required_cmp_(false) +{ +} + +FindEndRequiredVisitor::FindEndRequiredVisitor(const StaState *sta) : + sta_(sta), + required_cmp_(new RequiredCmp), + own_required_cmp_(true) +{ +} + +FindEndRequiredVisitor::~FindEndRequiredVisitor() +{ + if (own_required_cmp_) + delete required_cmp_; +} + +PathEndVisitor * +FindEndRequiredVisitor::copy() +{ + return new FindEndRequiredVisitor(sta_); +} + +void +FindEndRequiredVisitor::visit(PathEnd *path_end) +{ + if (!path_end->isUnconstrained()) { + PathRef &path = path_end->pathRef(); + const MinMax *req_min = path.minMax(sta_)->opposite(); + int arrival_index; + bool arrival_exists; + path.arrivalIndex(arrival_index, arrival_exists); + Required required = path_end->requiredTime(sta_); + required_cmp_->requiredSet(arrival_index, required, req_min); + } +} + +void +Search::seedRequired(Vertex *vertex) +{ + debugPrint1(debug_, "search", 2, "required seed %s\n", + vertex->name(sdc_network_)); + RequiredCmp required_cmp; + FindEndRequiredVisitor seeder(&required_cmp, this); + required_cmp.requiredsInit(vertex, this); + visit_path_ends_->visitPathEnds(vertex, &seeder); + // Enqueue fanin vertices for back-propagating required times. + if (required_cmp.requiredsSave(vertex, this)) + required_iter_->enqueueAdjacentVertices(vertex); +} + +void +Search::seedRequiredEnqueueFanin(Vertex *vertex) +{ + RequiredCmp required_cmp; + FindEndRequiredVisitor seeder(&required_cmp, this); + required_cmp.requiredsInit(vertex, this); + visit_path_ends_->visitPathEnds(vertex, &seeder); + // Enqueue fanin vertices for back-propagating required times. + required_cmp.requiredsSave(vertex, this); + required_iter_->enqueueAdjacentVertices(vertex); +} + +//////////////////////////////////////////////////////////////// + +RequiredCmp::RequiredCmp() : + have_requireds_(false) +{ + requireds_.reserve(10); +} + +void +RequiredCmp::requiredsInit(Vertex *vertex, + const StaState *sta) +{ + Search *search = sta->search(); + TagGroup *tag_group = search->tagGroup(vertex); + if (tag_group) { + requireds_.resize(tag_group->arrivalCount()); + ArrivalMap *arrival_entries = tag_group->arrivalMap(); + ArrivalMap::Iterator arrival_iter(arrival_entries); + while (arrival_iter.hasNext()) { + Tag *tag; + int arrival_index; + arrival_iter.next(tag, arrival_index); + PathAnalysisPt *path_ap = tag->pathAnalysisPt(sta); + const MinMax *min_max = path_ap->pathMinMax(); + requireds_[arrival_index] = delayInitValue(min_max->opposite()); + } + } + else + requireds_.resize(0); + have_requireds_ = false; +} + +void +RequiredCmp::requiredSet(int arrival_index, + Required required, + const MinMax *min_max) +{ + if (fuzzyGreater(required, requireds_[arrival_index], min_max)) { + requireds_[arrival_index] = required; + have_requireds_ = true; + } +} + +bool +RequiredCmp::requiredsSave(Vertex *vertex, + const StaState *sta) +{ + bool requireds_changed = false; + bool prev_reqs = vertex->hasRequireds(); + if (have_requireds_) { + if (!prev_reqs) + requireds_changed = true; + Debug *debug = sta->debug(); + VertexPathIterator path_iter(vertex, sta); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + int arrival_index; + bool arrival_exists; + path->arrivalIndex(arrival_index, arrival_exists); + Required req = requireds_[arrival_index]; + if (prev_reqs) { + Required prev_req = path->required(sta); + if (!fuzzyEqual(prev_req, req)) { + debugPrint2(debug, "search", 3, "required save %s -> %s\n", + delayAsString(prev_req, sta), + delayAsString(req, sta)); + path->setRequired(req, sta); + requireds_changed = true; + } + } + else { + debugPrint1(debug, "search", 3, "required save MIA -> %s\n", + delayAsString(req, sta)); + path->setRequired(req, sta); + } + } + } + else if (prev_reqs) { + PathVertex::deleteRequireds(vertex, sta); + requireds_changed = true; + } + return requireds_changed; +} + +Required +RequiredCmp::required(int arrival_index) +{ + return requireds_[arrival_index]; +} + +//////////////////////////////////////////////////////////////// + +RequiredVisitor::RequiredVisitor(const StaState *sta) : + PathVisitor(sta), + required_cmp_(new RequiredCmp), + visit_path_ends_(new VisitPathEnds(sta)) +{ +} + +RequiredVisitor::~RequiredVisitor() +{ + delete required_cmp_; + delete visit_path_ends_; +} + +VertexVisitor * +RequiredVisitor::copy() +{ + return new RequiredVisitor(sta_); +} + +void +RequiredVisitor::visit(Vertex *vertex) +{ + Search *search = sta_->search(); + const Debug *debug = sta_->debug(); + debugPrint1(debug, "search", 2, "find required %s\n", + vertex->name(sta_->network())); + required_cmp_->requiredsInit(vertex, sta_); + vertex->setRequiredsPruned(false); + // Back propagate requireds from fanout. + visitFanoutPaths(vertex); + // Check for constraints at endpoints that set required times. + if (search->isEndpoint(vertex)) { + FindEndRequiredVisitor seeder(required_cmp_, sta_); + visit_path_ends_->visitPathEnds(vertex, &seeder); + } + bool changed = required_cmp_->requiredsSave(vertex, sta_); + search->tnsInvalid(vertex); + + if (changed) + search->requiredIterator()->enqueueAdjacentVertices(vertex); +} + +bool +RequiredVisitor::visitFromToPath(const Pin *, + Vertex *from_vertex, + const TransRiseFall *from_tr, + Tag *from_tag, + PathVertex *from_path, + Edge *edge, + TimingArc *, + ArcDelay arc_delay, + Vertex *to_vertex, + const TransRiseFall *to_tr, + Tag *to_tag, + Arrival &, + const MinMax *min_max, + const PathAnalysisPt *path_ap) +{ + // Don't propagate required times through latch D->Q edges. + if (edge->role() != TimingRole::latchDtoQ()) { + const Debug *debug = sta_->debug(); + debugPrint3(debug, "search", 3, " %s -> %s %s\n", + from_tr->asString(), + to_tr->asString(), + min_max->asString()); + debugPrint2(debug, "search", 3, " from tag %2u: %s\n", + from_tag->index(), + from_tag->asString(sta_)); + int arrival_index; + bool arrival_exists; + from_path->arrivalIndex(arrival_index, arrival_exists); + const MinMax *req_min = min_max->opposite(); + TagGroup *to_tag_group = sta_->search()->tagGroup(to_vertex); + // Check to see if to_tag was pruned. + if (to_tag_group->hasTag(to_tag)) { + PathVertex to_path(to_vertex, to_tag, sta_); + Required to_required = to_path.required(sta_); + Required from_required = to_required - arc_delay; + debugPrint2(debug, "search", 3, " to tag %2u: %s\n", + to_tag->index(), + to_tag->asString(sta_)); + debugPrint5(debug, "search", 3, " %s - %s = %s %s %s\n", + delayAsString(to_required, sta_), + delayAsString(arc_delay, sta_), + delayAsString(from_required, sta_), + min_max == MinMax::max() ? "<" : ">", + delayAsString(required_cmp_->required(arrival_index), sta_)); + required_cmp_->requiredSet(arrival_index, from_required, req_min); + } + else { + if (sta_->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_tr, path_ap, sta_); + while (to_iter.hasNext()) { + PathVertex *to_path = to_iter.next(); + Tag *to_path_tag = to_path->tag(sta_); + if (tagMatchNoCrpr(to_path_tag, to_tag)) { + Required to_required = to_path->required(sta_); + Required from_required = to_required - arc_delay; + debugPrint2(debug, "search", 3, " to tag %2u: %s\n", + to_path_tag->index(), + to_path_tag->asString(sta_)); + debugPrint5(debug, "search", 3, " %s - %s = %s %s %s\n", + delayAsString(to_required, sta_), + delayAsString(arc_delay, sta_), + delayAsString(from_required, sta_), + min_max == MinMax::max() ? "<" : ">", + delayAsString(required_cmp_->required(arrival_index), + sta_)); + required_cmp_->requiredSet(arrival_index, from_required, req_min); + break; + } + } + } + from_vertex->setRequiredsPruned(true); + } + // Propagate requireds pruned flag backwards. + if (to_vertex->requiredsPruned()) + from_vertex->setRequiredsPruned(true); + } + return true; +} + +//////////////////////////////////////////////////////////////// + +void +Search::ensureDownstreamClkPins() +{ + if (!found_downstream_clk_pins_) { + // Use backward BFS from register clk pins to mark upsteam pins + // as having downstream clk pins. + ClkTreeSearchPred pred(this); + BfsBkwdIterator iter(BfsIndex::other, &pred, this); + for (auto vertex : *graph_->regClkVertices()) + iter.enqueue(vertex); + + // Enqueue PLL feedback pins. + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + Pin *pin = vertex->pin(); + const LibertyPort *port = network_->libertyPort(pin); + if (port && port->isPllFeedbackPin()) + iter.enqueue(vertex); + } + while (iter.hasNext()) { + Vertex *vertex = iter.next(); + vertex->setHasDownstreamClkPin(true); + iter.enqueueAdjacentVertices(vertex); + } + } + found_downstream_clk_pins_ = true; +} + +//////////////////////////////////////////////////////////////// + +bool +Search::matchesFilter(Path *path, + const ClockEdge *to_clk_edge) +{ + if (filter_ == nullptr + && filter_from_ == nullptr + && filter_to_ == nullptr) + return true; + else if (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; + } + } + return false; + } + else if (filter_from_ + && filter_from_->pins() == nullptr + && filter_from_->instances() == nullptr + && filter_from_->clks()) { + // -from clks + ClockEdge *path_clk_edge = path->clkEdge(this); + Clock *path_clk = path_clk_edge ? path_clk_edge->clock() : nullptr; + TransRiseFall *path_clk_tr = + path_clk_edge ? path_clk_edge->transition() : nullptr; + return filter_from_->clks()->hasKey(path_clk) + && filter_from_->transition()->matches(path_clk_tr) + && matchesFilterTo(path, to_clk_edge); + } + else if (filter_from_ == nullptr + && filter_to_) + // -to + return matchesFilterTo(path, to_clk_edge); + else + internalError("unexpected filter path"); +} + +// Similar to Constraints::exceptionMatchesTo. +bool +Search::matchesFilterTo(Path *path, + const ClockEdge *to_clk_edge) const +{ + return (filter_to_ == nullptr + || filter_to_->matchesFilter(path->pin(graph_), to_clk_edge, + path->transition(this), network_)); +} + +//////////////////////////////////////////////////////////////// + +// Find the exception that has the highest priority for an end path, +// including exceptions that start at the end pin or target clock. +ExceptionPath * +Search::exceptionTo(ExceptionPathType type, + const Path *path, + const Pin *pin, + const TransRiseFall *tr, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + bool require_to_pin) const +{ + // Find the highest priority exception carried by the path's tag. + int hi_priority = -1; + ExceptionPath *hi_priority_exception = nullptr; + const ExceptionStateSet *states = path->tag(this)->states(); + if (states) { + 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, tr, 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, tr, clk_edge, min_max, + match_min_max_exactly, + hi_priority_exception, hi_priority); + return hi_priority_exception; +} + +//////////////////////////////////////////////////////////////// + +Slack +Search::totalNegativeSlack(const MinMax *min_max) +{ + tnsPreamble(); + Slack tns = 0.0; + for (auto corner : *corners_) { + PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); + Slack tns1 = tns_[path_ap_index]; + if (tns1 < tns) + tns = tns1; + } + return tns; +} + +Slack +Search::totalNegativeSlack(const Corner *corner, + const MinMax *min_max) +{ + tnsPreamble(); + PathAPIndex path_ap_index = corner->findPathAnalysisPt(min_max)->index(); + return 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); + if (tns_exists_) + updateInvalidTns(); + else + findTotalNegativeSlacks(); +} + +void +Search::tnsInvalid(Vertex *vertex) +{ + if ((tns_exists_ || worst_slacks_) + && isEndpoint(vertex)) { + debugPrint1(debug_, "tns", 2, "tns invalid %s\n", + vertex->name(sdc_network_)); + UniqueLock lock(tns_lock_); + invalid_tns_.insert(vertex); + } +} + +void +Search::updateInvalidTns() +{ + PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); + for (auto vertex : invalid_tns_) { + // Network edits can change endpointedness since tnsInvalid was called. + if (isEndpoint(vertex)) { + debugPrint1(debug_, "tns", 2, "update tns %s\n", + vertex->name(sdc_network_)); + SlackSeq slacks(path_ap_count); + wnsSlacks(vertex, slacks); + + if (tns_exists_) + updateTns(vertex, slacks); + if (worst_slacks_) + worst_slacks_->updateWorstSlacks(vertex, slacks); + } + } + invalid_tns_.clear(); +} + +void +Search::findTotalNegativeSlacks() +{ + PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); + for (PathAPIndex i = 0; i < path_ap_count; i++) { + tns_[i] = 0.0; + tns_slacks_[i].clear(); + } + for (auto vertex : *endpoints()) { + // No locking required. + SlackSeq slacks(path_ap_count); + wnsSlacks(vertex, slacks); + for (PathAPIndex i = 0; i < path_ap_count; i++) + tnsIncr(vertex, slacks[i], i); + } + tns_exists_ = true; +} + +void +Search::updateTns(Vertex *vertex, + SlackSeq &slacks) +{ + PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); + for (PathAPIndex i = 0; i < path_ap_count; i++) { + tnsDecr(vertex, i); + tnsIncr(vertex, slacks[i], i); + } +} + +void +Search::tnsIncr(Vertex *vertex, + Slack slack, + PathAPIndex path_ap_index) +{ + if (fuzzyLess(slack, 0.0)) { + debugPrint2(debug_, "tns", 3, "tns+ %s %s\n", + delayAsString(slack, this), + vertex->name(sdc_network_)); + tns_[path_ap_index] += slack; + if (tns_slacks_[path_ap_index].hasKey(vertex)) + internalError("tns incr existing vertex"); + tns_slacks_[path_ap_index][vertex] = slack; + } +} + +void +Search::tnsDecr(Vertex *vertex, + PathAPIndex path_ap_index) +{ + Slack slack; + bool found; + tns_slacks_[path_ap_index].findKey(vertex, slack, found); + if (found + && fuzzyLess(slack, 0.0)) { + debugPrint2(debug_, "tns", 3, "tns- %s %s\n", + delayAsString(slack, this), + vertex->name(sdc_network_)); + tns_[path_ap_index] -= slack; + tns_slacks_[path_ap_index].erase(vertex); + } +} + +// Notify tns before updating/deleting slack (arrival/required). +void +Search::tnsNotifyBefore(Vertex *vertex) +{ + if (tns_exists_ + && isEndpoint(vertex)) { + int ap_count = corners_->pathAnalysisPtCount(); + for (int i = 0; i < ap_count; i++) { + tnsDecr(vertex, i); + } + } +} + +//////////////////////////////////////////////////////////////// + +void +Search::worstSlack(const MinMax *min_max, + // 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) +{ + worstSlackPreamble(); + worst_slacks_->worstSlack(corner, min_max, worst_slack, worst_vertex); +} + +void +Search::worstSlackPreamble() +{ + wnsTnsPreamble(); + if (worst_slacks_) + updateInvalidTns(); + else + worst_slacks_ = new WorstSlacks(this); +} + +void +Search::wnsTnsPreamble() +{ + findAllArrivals(); + // Required times are only needed at endpoints. + if (requireds_seeded_) { + for (Vertex *vertex : invalid_requireds_) { + debugPrint1(debug_, "search", 2, "tns update required %s\n", + vertex->name(sdc_network_)); + 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); + } + } + invalid_requireds_.clear(); + } + else + seedRequireds(); +} + +void +Search::clearWorstSlack() +{ + if (worst_slacks_) { + // Don't maintain incremental worst slacks until there is a request. + delete worst_slacks_; + worst_slacks_ = nullptr; + } +} + +//////////////////////////////////////////////////////////////// + +class FindEndSlackVisitor : public PathEndVisitor +{ +public: + FindEndSlackVisitor(SlackSeq &slacks, + const StaState *sta); + virtual PathEndVisitor *copy(); + virtual void visit(PathEnd *path_end); + +protected: + SlackSeq &slacks_; + const StaState *sta_; +}; + +FindEndSlackVisitor::FindEndSlackVisitor(SlackSeq &slacks, + const StaState *sta) : + slacks_(slacks), + sta_(sta) +{ +} + +PathEndVisitor * +FindEndSlackVisitor::copy() +{ + + return new FindEndSlackVisitor(slacks_, sta_); +} + +void +FindEndSlackVisitor::visit(PathEnd *path_end) +{ + if (!path_end->isUnconstrained()) { + PathRef &path = path_end->pathRef(); + PathAPIndex path_ap_index = path.pathAnalysisPtIndex(sta_); + Slack slack = path_end->slack(sta_); + if (fuzzyLess(slack, slacks_[path_ap_index])) + slacks_[path_ap_index] = slack; + } +} + +void +Search::wnsSlacks(Vertex *vertex, + // 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++) + slacks[i] = slack_init; + if (hasFanout(vertex, search_adj_, graph_)) { + // If the vertex has fanout the path slacks include downstream + // PathEnd slacks so find the endpoint slack directly. + FindEndSlackVisitor end_visitor(slacks, this); + visit_path_ends_->visitPathEnds(vertex, &end_visitor); + } + else { + VertexPathIterator path_iter(vertex, this); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + PathAPIndex path_ap_index = path->pathAnalysisPtIndex(this); + const Slack path_slack = path->slack(this); + if (!path->tag(this)->isFilter() + && fuzzyLess(path_slack, slacks[path_ap_index])) + slacks[path_ap_index] = path_slack; + } + } +} + +Slack +Search::wnsSlack(Vertex *vertex, + PathAPIndex path_ap_index) +{ + PathAPIndex path_ap_count = corners_->pathAnalysisPtCount(); + SlackSeq slacks(path_ap_count); + wnsSlacks(vertex, slacks); + return slacks[path_ap_index]; +} + +//////////////////////////////////////////////////////////////// + +PathGroups * +Search::makePathGroups(int group_count, + int endpoint_count, + bool unique_pins, + 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) +{ + return new PathGroups(group_count, endpoint_count, unique_pins, + slack_min, slack_max, + group_names, + setup, hold, + recovery, removal, + clk_gating_setup, clk_gating_hold, + unconstrained_paths_, + this); +} + +void +Search::deletePathGroups() +{ + delete path_groups_; + path_groups_ = nullptr; +} + +PathGroup * +Search::pathGroup(const PathEnd *path_end) const +{ + return path_groups_->pathGroup(path_end); +} + +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; +} + +} // namespace diff --git a/search/Search.hh b/search/Search.hh new file mode 100644 index 0000000..daa0693 --- /dev/null +++ b/search/Search.hh @@ -0,0 +1,822 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SEARCH_H +#define STA_SEARCH_H + +#include +#include "MinMax.hh" +#include "StaState.hh" +#include "HashSet.hh" +#include "Transition.hh" +#include "LibertyClass.hh" +#include "NetworkClass.hh" +#include "GraphClass.hh" +#include "Delay.hh" +#include "SdcClass.hh" +#include "SearchClass.hh" +#include "SearchPred.hh" +#include "VertexVisitor.hh" + +namespace sta { + +class BfsFwdIterator; +class BfsBkwdIterator; +class SearchPred; +class SearchThru; +class ClkInfoLess; +class PathEndVisitor; +class ArrivalVisitor; +class RequiredVisitor; +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 HashSet TagHashSet; +typedef HashSet TagGroupSet; +typedef Map VertexSlackMap; +typedef Vector VertexSlackMapSeq; +typedef Vector WorstSlacksSeq; + +class Search : public StaState +{ +public: + explicit Search(StaState *sta); + virtual ~Search(); + virtual void copyState(const StaState *sta); + // Reset to virgin state. + void clear(); + // When enabled, non-critical path arrivals are pruned to improve + // run time and reduce memory. + 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; + void setCrprApproxMissingRequireds(bool enabled); + + bool unconstrainedPaths() const { return unconstrained_paths_; } + // from/thrus/to are owned and deleted by Search. + // Use corner nullptr to report timing for all corners. + // Returned sequence is owned by the caller. + // PathEnds are owned by Search PathGroups and deleted on next call. + PathEndSeq *findPathEnds(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool unconstrained, + const Corner *corner, + const MinMaxAll *min_max, + int group_count, + int endpoint_count, + bool unique_pins, + 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); + // Invalidate all arrival and required times. + void arrivalsInvalid(); + // Invalidate vertex arrival time. + void arrivalInvalid(Vertex *vertex); + void arrivalInvalidDelete(Vertex *vertex); + void arrivalInvalid(const Pin *pin); + // Invalidate all required times. + void requiredsInvalid(); + // Invalidate vertex required time. + void requiredInvalid(Vertex *vertex); + void requiredInvalid(Instance *inst); + void requiredInvalid(const Pin *pin); + // Vertex will be deleted. + void deleteVertexBefore(Vertex *vertex); + // Find all arrival times (propatating thru latches). + void findAllArrivals(); + // Find all arrivals (without latch propagation). + void findArrivals(); + virtual void findAllArrivals(VertexVisitor *visitor); + // Find arrival times up thru level. + void findArrivals(Level level); + void findRequireds(); + // Find required times down thru level. + void findRequireds(Level level); + bool requiredsSeeded() const { return requireds_seeded_; } + 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); + // 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); + // Clock arrival respecting ideal clock insertion delay and latency. + Arrival clkPathArrival(const Path *clk_path) const; + Arrival clkPathArrival(const Path *clk_path, + ClkInfo *clk_info, + ClockEdge *clk_edge, + const MinMax *min_max, + const PathAnalysisPt *path_ap) const; + // Clock arrival at the path source/launch point. + Arrival pathClkPathArrival(const Path *path) const; + + PathGroup *pathGroup(const PathEnd *path_end) const; + void deletePathGroups(); + virtual ExceptionPath *exceptionTo(ExceptionPathType type, + const Path *path, + const Pin *pin, + const TransRiseFall *tr, + const ClockEdge *clk_edge, + const MinMax *min_max, + bool match_min_max_exactly, + bool require_to_pin) const; + FilterPath *filter() const { return filter_; } + void deleteFilter(); + void deleteFilteredArrivals(); + + // Endpoints are discovered during arrival search, so are only + // defined after findArrivals. + 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. + void clocks(const Vertex *vertex, + // Return value. + ClockSet &clks) const; + void clocks(const Pin *pin, + // Return value. + ClockSet &clks) 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; + + //////////////////////////////////////////////////////////////// + // + // Somewhat protected functions. + // + //////////////////////////////////////////////////////////////// + + // Find arrivals for the clock tree. + void findClkArrivals(); + void seedArrival(Vertex *vertex); + EvalPred *evalPred() const { return eval_pred_; } + SearchPred *searchAdj() const { return search_adj_; } + Tag *tag(TagIndex index) const; + TagIndex tagCount() const; + TagGroupIndex tagGroupCount() const; + void reportTagGroups() const; + void reportArrivalCountHistogram() const; + virtual int clkInfoCount() const; + virtual bool isEndpoint(Vertex *vertex) const; + virtual bool isEndpoint(Vertex *vertex, + SearchPred *pred) const; + void endpointInvalid(Vertex *vertex); + Tag *fromUnclkedInputTag(const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max, + const PathAnalysisPt *path_ap, + bool is_segment_start); + Tag *fromRegClkTag(const Pin *from_pin, + const TransRiseFall *from_tr, + Clock *clk, + const TransRiseFall *clk_tr, + ClkInfo *clk_info, + const Pin *to_pin, + const TransRiseFall *to_tr, + const MinMax *min_max, + const PathAnalysisPt *path_ap); + virtual Tag *thruTag(Tag *from_tag, + Edge *edge, + const TransRiseFall *to_tr, + const MinMax *min_max, + const PathAnalysisPt *path_ap); + virtual Tag *thruClkTag(PathVertex *from_path, + Tag *from_tag, + bool to_propagates_clk, + Edge *edge, + const TransRiseFall *to_tr, + const MinMax *min_max, + const PathAnalysisPt *path_ap); + ClkInfo *thruClkInfo(PathVertex *from_path, + ClkInfo *from_tag_clk, + Edge *edge, + Vertex *to_vertex, + const Pin *to_pin, + const MinMax *min_max, + const PathAnalysisPt *path_ap); + ClkInfo *clkInfoWithCrprClkPath(ClkInfo *from_clk_info, + PathVertex *from_path, + const PathAnalysisPt *path_ap); + void seedClkArrivals(const Pin *pin, + Vertex *vertex, + TagGroupBldr *tag_bldr); + void setVertexArrivals(Vertex *vertex, + TagGroupBldr *group_bldr); + void tnsInvalid(Vertex *vertex); + 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_;} + bool makeUnclkedPaths(Vertex *vertex, + bool is_segment_start, + TagGroupBldr *tag_bldr); + bool isSegmentStart(const Pin *pin); + bool isInputArrivalSrchStart(Vertex *vertex); + void seedInputSegmentArrival(const Pin *pin, + Vertex *vertex, + TagGroupBldr *tag_bldr); + void enqueueLatchDataOutputs(Vertex *vertex); + virtual void seedRequired(Vertex *vertex); + virtual void seedRequiredEnqueueFanin(Vertex *vertex); + void seedInputDelayArrival(const Pin *pin, + Vertex *vertex, + InputDelay *input_delay); + void seedInputDelayArrival(const Pin *pin, + Vertex *vertex, + InputDelay *input_delay, + bool is_segment_start, + TagGroupBldr *tag_bldr); + // Insertion delay for regular or generated clock. + Arrival clockInsertion(const Clock *clk, + const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max, + const EarlyLate *early_late, + const PathAnalysisPt *path_ap) const; + bool propagateClkSense(const Pin *from_pin, + Path *from_path, + const TransRiseFall *to_tr); + + Tag *findTag(const TransRiseFall *tr, + const PathAnalysisPt *path_ap, + ClkInfo *tag_clk, + bool is_clk, + InputDelay *input_delay, + bool is_segment_start, + ExceptionStateSet *states, + bool own_states); + void reportTags() const; + void reportClkInfos() const; + virtual ClkInfo *findClkInfo(ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, + const Pin *gen_clk_src, + bool gen_clk_src_path, + const TransRiseFall *pulse_clk_sense, + Arrival insertion, + float latency, + ClockUncertainties *uncertainties, + const PathAnalysisPt *path_ap, + PathVertex *crpr_clk_path); + ClkInfo *findClkInfo(ClockEdge *clk_edge, + const Pin *clk_src, + bool is_propagated, + Arrival insertion, + const PathAnalysisPt *path_ap); + // Timing derated arc delay for a path analysis point. + ArcDelay deratedDelay(Vertex *from_vertex, + TimingArc *arc, + Edge *edge, + bool is_clk, + const PathAnalysisPt *path_ap); + + TagGroup *tagGroup(const Vertex *vertex) const; + TagGroup *tagGroup(TagGroupIndex index) const; + void reportArrivals(Vertex *vertex) const; + Slack wnsSlack(Vertex *vertex, + PathAPIndex path_ap_index); + void levelChangedBefore(Vertex *vertex); + void seedInputArrival(const Pin *pin, + Vertex *vertex, + TagGroupBldr *tag_bldr); + void ensureDownstreamClkPins(); + bool pathPropagatedToClkSrc(const Pin *pin, + Path *path); + // Check paths from inputs from the default arrival clock + // (missing set_input_delay). + virtual bool checkDefaultArrivalPaths() { return true; } + 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); + +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); + virtual void deleteTags(); + void seedInvalidArrivals(); + void seedArrivals(); + void findClockVertices(VertexSet &vertices); + void seedClkDataArrival(const Pin *pin, + const TransRiseFall *tr, + Clock *clk, + ClockEdge *clk_edge, + const MinMax *min_max, + const PathAnalysisPt *path_ap, + Arrival insertion, + TagGroupBldr *tag_bldr); + void seedClkArrival(const Pin *pin, + const TransRiseFall *tr, + Clock *clk, + ClockEdge *clk_edge, + const MinMax *min_max, + const PathAnalysisPt *path_ap, + Arrival insertion, + TagGroupBldr *tag_bldr); + Tag *clkDataTag(const Pin *pin, + Clock *clk, + const TransRiseFall *tr, + ClockEdge *clk_edge, + Arrival insertion, + const MinMax *min_max, + const PathAnalysisPt *path_ap); + 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); + void seedInputArrival(const Pin *pin, + Vertex *vertex, + ClockSet *wrt_clks); + void seedInputDelayArrival(const Pin *pin, + InputDelay *input_delay, + 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); + void seedInputDelayArrival(const Pin *pin, + const TransRiseFall *tr, + float arrival, + InputDelay *input_delay, + ClockEdge *clk_edge, + float clk_insertion, + float clk_latency, + bool is_segment_start, + const MinMax *min_max, + PathAnalysisPt *path_ap, + TagGroupBldr *tag_bldr); + void inputDelayClkArrival(InputDelay *input_delay, + ClockEdge *clk_edge, + const MinMax *min_max, + const PathAnalysisPt *path_ap, + // Return values. + float &clk_arrival, + float &clk_insertion, + float &clk_latency); + void inputDelayRefPinArrival(Path *ref_path, + ClockEdge *clk_edge, + const MinMax *min_max, + // Return values. + float &ref_arrival, + float &ref_insertion, + float &ref_latency); + Tag *inputDelayTag(const Pin *pin, + const TransRiseFall *tr, + ClockEdge *clk_edge, + float clk_insertion, + float clk_latency, + InputDelay *input_delay, + bool is_segment_start, + const MinMax *min_max, + const PathAnalysisPt *path_ap); + void seedClkVertexArrivals(); + void seedClkVertexArrivals(const Pin *pin, + Vertex *vertex); + void findClkArrivals1(); + + virtual void findArrivals(Level level, + VertexVisitor *arrival_visitor); + Tag *mutateTag(Tag *from_tag, + const Pin *from_pin, + const TransRiseFall *from_tr, + bool from_is_clk, + ClkInfo *from_clk_info, + const Pin *to_pin, + const TransRiseFall *to_tr, + bool to_is_clk, + bool to_is_reg_clk, + bool to_is_segment_start, + ClkInfo *to_clk_info, + InputDelay *to_input_delay, + const MinMax *min_max, + const PathAnalysisPt *path_ap); + ExceptionPath *exceptionTo(const Path *path, + const Pin *pin, + const TransRiseFall *tr, + const ClockEdge *clk_edge, + const MinMax *min_max) const; + void seedRequireds(); + void seedInvalidRequireds(); + bool havePendingLatchOutputs(); + void clearPendingLatchOutputs(); + void enqueuePendingLatchOutputs(); + void findFilteredArrivals(); + void findArrivals1(); + void seedFilterStarts(); + bool hasEnabledChecks(Vertex *vertex) const; + virtual float timingDerate(Vertex *from_vertex, + TimingArc *arc, + Edge *edge, + bool is_clk, + const PathAnalysisPt *path_ap); + void deletePaths(); + void deletePaths(Vertex *vertex); + void deletePaths1(Vertex *vertex); + TagGroup *findTagGroup(TagGroupBldr *group_bldr); + void deleteFilterTags(); + void deleteFilterTagGroups(); + void deleteFilterClkInfos(); + + void tnsPreamble(); + void findTotalNegativeSlacks(); + void updateInvalidTns(); + void clearWorstSlack(); + void wnsSlacks(Vertex *vertex, + // Return values. + SlackSeq &slacks); + void wnsTnsPreamble(); + void worstSlackPreamble(); + void deleteWorstSlacks(); + void updateWorstSlacks(Vertex *vertex, + Slack slacks); + void updateTns(Vertex *vertex, + SlackSeq &slacks); + void tnsIncr(Vertex *vertex, + Slack slack, + PathAPIndex path_ap_index); + void tnsDecr(Vertex *vertex, + PathAPIndex path_ap_index); + void tnsNotifyBefore(Vertex *vertex); + PathGroups *makePathGroups(int group_count, + int endpoint_count, + bool unique_pins, + 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); + bool matchesFilterTo(Path *path, + const ClockEdge *to_clk_edge) const; + void pathClkPathArrival1(const Path *path, + // Return value. + PathRef &clk_path) const; + + //////////////////////////////////////////////////////////////// + + // findPathEnds arg. + bool unconstrained_paths_; + bool crpr_path_pruning_enabled_; + bool crpr_approx_missing_requireds_; + // Search predicates. + SearchPred *search_adj_; + SearchPred *search_clk_; + EvalPred *eval_pred_; + ArrivalVisitor *arrival_visitor_; + // Clock arrivals are known. + bool clk_arrivals_valid_; + // Some arrivals exist. + bool arrivals_exist_; + // Arrivals at end points exist (but may be invalid). + bool arrivals_at_endpoints_exist_; + // 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_; + // Vertices with invalid arrival times to update and search from. + VertexSet invalid_arrivals_; + std::mutex invalid_arrivals_lock_; + BfsFwdIterator *arrival_iter_; + // Vertices with invalid required times to update and search from. + VertexSet invalid_requireds_; + BfsBkwdIterator *required_iter_; + bool tns_exists_; + // Endpoint vertices with slacks that have changed since tns was found. + VertexSet invalid_tns_; + // Indexed by path_ap->index(). + SlackSeq tns_; + // Indexed by path_ap->index(). + VertexSlackMapSeq tns_slacks_; + std::mutex tns_lock_; + // Indexed by path_ap->index(). + WorstSlacks *worst_slacks_; + // 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. + TagHashSet *tag_set_; + // Entries in tags_ may be missing where previous filter tags were deleted. + TagIndex tag_capacity_; + Tag **tags_; + TagIndex tag_next_; + // Holes in tags_ left by deleting filter tags. + std::vector tag_free_indices_; + std::mutex tag_lock_; + TagGroupSet *tag_group_set_; + TagGroup **tag_groups_; + TagGroupIndex tag_group_next_; + // 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_; + 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_; + bool found_downstream_clk_pins_; + PathGroups *path_groups_; + 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); + void setSearchThruLatches(bool thru_latches); + virtual bool searchTo(const Vertex *to_vertex); + +protected: + bool search_thru_latches_; +}; + +class ClkArrivalSearchPred : public EvalPred +{ +public: + ClkArrivalSearchPred(const StaState *sta); + virtual bool searchThru(Edge *edge); + +private: + DISALLOW_COPY_AND_ASSIGN(ClkArrivalSearchPred); +}; + +// 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: + // Uses search->evalPred() for search predicate. + explicit PathVisitor(const StaState *sta); + PathVisitor(SearchPred *pred, + const StaState *sta); + virtual void visitFaninPaths(Vertex *to_vertex); + virtual void visitFanoutPaths(Vertex *from_vertex); + +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); + // Return false to stop visiting. + bool visitArc(const Pin *from_pin, + Vertex *from_vertex, + const TransRiseFall *from_tr, + PathVertex *from_path, + Edge *edge, + TimingArc *arc, + const Pin *to_pin, + Vertex *to_vertex, + const MinMax *min_max, + PathAnalysisPt *path_ap); + // 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 TransRiseFall *from_tr, + PathVertex *from_path, + Edge *edge, + TimingArc *arc, + const Pin *to_pin, + Vertex *to_vertex, + const TransRiseFall *to_tr, + const MinMax *min_max, + const PathAnalysisPt *path_ap); + // Return false to stop visiting. + virtual bool visitFromToPath(const Pin *from_pin, + Vertex *from_vertex, + const TransRiseFall *from_tr, + Tag *from_tag, + PathVertex *from_path, + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex *to_vertex, + const TransRiseFall *to_tr, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max, + const PathAnalysisPt *path_ap) = 0; + SearchPred *pred_; + const StaState *sta_; +}; + +// Visitor called during forward search to record an +// arrival at an path. +class ArrivalVisitor : public PathVisitor +{ +public: + explicit ArrivalVisitor(const StaState *sta); + virtual ~ArrivalVisitor(); + // 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(); + // Return false to stop visiting. + virtual bool visitFromToPath(const Pin *from_pin, + Vertex *from_vertex, + const TransRiseFall *from_tr, + Tag *from_tag, + PathVertex *from_path, + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex *to_vertex, + const TransRiseFall *to_tr, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max, + const PathAnalysisPt *path_ap); + void setAlwaysToEndpoints(bool to_endpoints); + TagGroupBldr *tagBldr() const { return tag_bldr_; } + +protected: + ArrivalVisitor(bool always_to_endpoints, + SearchPred *pred, + const StaState *sta); + void init0(); + void enqueueRefPinInputDelays(const Pin *ref_pin); + void seedInputDelayArrival(const Pin *pin, + Vertex *vertex, + InputDelay *input_delay); + void pruneCrprArrivals(); + void constrainedRequiredsInvalid(Vertex *vertex, + bool is_clk); + bool always_to_endpoints_; + TagGroupBldr *tag_bldr_; + TagGroupBldr *tag_bldr_no_crpr_; + SearchPred *adj_pred_; + bool crpr_active_; + bool has_fanin_one_; +}; + +class RequiredCmp +{ +public: + RequiredCmp(); + void requiredsInit(Vertex *vertex, + const StaState *sta); + void requiredSet(int arrival_index, + Required required, + const MinMax *min_max); + // Return true if the requireds changed. + bool requiredsSave(Vertex *vertex, + const StaState *sta); + Required required(int arrival_index); + +protected: + ArrivalSeq requireds_; + bool have_requireds_; +}; + +// Visitor called during backward search to record a +// required time at an path. +class RequiredVisitor : public PathVisitor +{ +public: + explicit RequiredVisitor(const StaState *sta); + virtual ~RequiredVisitor(); + virtual VertexVisitor *copy(); + virtual void visit(Vertex *vertex); + +protected: + // Return false to stop visiting. + virtual bool visitFromToPath(const Pin *from_pin, + Vertex *from_vertex, + const TransRiseFall *from_tr, + Tag *from_tag, + PathVertex *from_path, + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex *to_vertex, + const TransRiseFall *to_tr, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max, + const PathAnalysisPt *path_ap); + + 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: + explicit DynLoopSrchPred(TagGroupBldr *tag_bldr); + +protected: + bool loopEnabled(Edge *edge, + const Sdc *sdc, + const Graph *graph, + Search *search); + bool hasPendingLoopPaths(Edge *edge, + const Graph *graph, + Search *search); + + TagGroupBldr *tag_bldr_; + +private: + DISALLOW_COPY_AND_ASSIGN(DynLoopSrchPred); +}; + +} // namespace +#endif diff --git a/search/SearchClass.hh b/search/SearchClass.hh new file mode 100644 index 0000000..dbeb380 --- /dev/null +++ b/search/SearchClass.hh @@ -0,0 +1,131 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SEARCH_CLASS_H +#define STA_SEARCH_CLASS_H + +#include "Vector.hh" +#include "Set.hh" +#include "Map.hh" +#include "UnorderedMap.hh" +#include "StringSet.hh" +#include "Delay.hh" +#include "NetworkClass.hh" +#include "GraphClass.hh" + +namespace sta { + +class Search; +class Path; +class PathRep; +class PathVertex; +class PathVertexRep; +class PathRef; +class PathEnumed; +class PathEnd; +class PathGroup; +class Tag; +class TagIndexLess; +class TagMatchLess; +class TagLess; +class TagHash; +class TagEqual; +class TagGroup; +class TagGroupHash; +class TagGroupEqual; +class ClkInfo; +class ClkInfoHash; +class ClkInfoEqual; +class VertexPathIterator; +class PathAnalysisPt; +class PathAnalysisPtIterator; +class MinPulseWidthCheck; +class MinPeriodCheck; +class MaxSkewCheck; +class CharPtrLess; + +// Tag compare using tag matching (tagMatch) critera. +class TagMatchLess +{ +public: + explicit TagMatchLess(bool match_crpr_clk_pin, + const StaState *sta); + bool operator()(const Tag *tag1, + const Tag *tag2) const; + +protected: + bool match_crpr_clk_pin_; + const StaState *sta_; +}; + +class TagMatchHash +{ +public: + TagMatchHash(bool match_crpr_clk_pin, + const StaState *sta); + size_t operator()(const Tag *tag) const; + +protected: + bool match_crpr_clk_pin_; + const StaState *sta_; +}; + +class TagMatchEqual +{ +public: + TagMatchEqual(bool match_crpr_clk_pin, + const StaState *sta); + bool operator()(const Tag *tag1, + const Tag *tag2) const; + +protected: + bool match_crpr_clk_pin_; + const StaState *sta_; +}; + +typedef int PathAPIndex; +typedef int TagIndex; +typedef int TagGroupTagIndex; +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 UnorderedMap ArrivalMap; +typedef Vector PathVertexSeq; +typedef Vector SlackSeq; +typedef Delay Crpr; +typedef Vector PathRefSeq; + +enum class ReportPathFormat { full, + full_clock, + full_clock_expanded, + shorter, + endpoint, + summary, + slack_only +}; + +static const int tag_index_bits = 24; +static const int tag_index_max = (1 << tag_index_bits) - 1; +static const int tag_index_null = tag_index_max; +static const int path_ap_index_bit_count = 4; + +} // namespace +#endif diff --git a/search/SearchPred.cc b/search/SearchPred.cc new file mode 100644 index 0000000..151dcf7 --- /dev/null +++ b/search/SearchPred.cc @@ -0,0 +1,271 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "TimingArc.hh" +#include "TimingRole.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Sdc.hh" +#include "Levelize.hh" +#include "Search.hh" +#include "Latches.hh" +#include "SearchPred.hh" + +namespace sta { + +static bool +searchThruSimEdge(const Vertex *vertex, const TransRiseFall *tr); +static bool +searchThruTimingSense(const Edge *edge, const TransRiseFall *from_tr, + const TransRiseFall *to_tr); + +SearchPred0::SearchPred0(const StaState *sta) : + sta_(sta) +{ +} + +bool +SearchPred0::searchFrom(const Vertex *from_vertex) +{ + return !(from_vertex->isDisabledConstraint() + || from_vertex->isConstant()); +} + +bool +SearchPred0::searchThru(Edge *edge) +{ + const TimingRole *role = edge->role(); + const Sdc *sdc = sta_->sdc(); + 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() + && !sdc->presetClrArcsEnabled()) + // Constants on other pins disable this edge (ie, a mux select). + || edge->simTimingSense() == TimingSense::none + || (edge->isBidirectInstPath() + && !sdc->bidirectInstPathsEnabled()) + || (edge->isBidirectNetPath() + && !sdc->bidirectNetPathsEnabled()) + || (role == TimingRole::latchDtoQ() + && sta_->latches()->latchDtoQState(edge) + == LatchEnableState::closed)); +} + +bool +SearchPred0::searchTo(const Vertex *to_vertex) +{ + return !to_vertex->isConstant(); +} + +//////////////////////////////////////////////////////////////// + +SearchPred1::SearchPred1(const StaState *sta) : + SearchPred0(sta) +{ +} + +bool +SearchPred1::searchThru(Edge *edge) +{ + return SearchPred0::searchThru(edge) + && !edge->isDisabledLoop(); +} + +//////////////////////////////////////////////////////////////// + +SearchPred2::SearchPred2(const StaState *sta) : + SearchPred1(sta) +{ +} + +bool +SearchPred2::searchThru(Edge *edge) +{ + return SearchPred1::searchThru(edge) + && !edge->role()->isTimingCheck(); +} + +//////////////////////////////////////////////////////////////// + +SearchPredNonLatch2::SearchPredNonLatch2(const StaState *sta) : + SearchPred2(sta) +{ +} + +bool +SearchPredNonLatch2::searchThru(Edge *edge) +{ + return SearchPred2::searchThru(edge) + && !sta_->latches()->isLatchDtoQ(edge); +} + +//////////////////////////////////////////////////////////////// + +SearchPredNonReg2::SearchPredNonReg2(const StaState *sta) : + SearchPred2(sta) +{ +} + +bool +SearchPredNonReg2::searchThru(Edge *edge) +{ + 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(); +} + +//////////////////////////////////////////////////////////////// + +ClkTreeSearchPred::ClkTreeSearchPred(const StaState *sta) : + SearchPred1(sta) +{ +} + +bool +ClkTreeSearchPred::searchThru(Edge *edge) +{ + const Sdc *sdc = sta_->sdc(); + // Propagate clocks through constants. + const TimingRole *role = edge->role(); + return (role->isWire() + || role == TimingRole::combinational()) + && (sdc->clkThruTristateEnabled() + || !(role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable())) + && SearchPred1::searchThru(edge); +} + +bool +isClkEnd(Vertex *vertex, + Graph *graph) +{ + ClkTreeSearchPred pred(graph); + VertexOutEdgeIterator edge_iter(vertex, graph); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (pred.searchThru(edge)) + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////// + +bool +searchThru(const Edge *edge, + const TimingArc *arc, + const Graph *graph) +{ + TransRiseFall *from_tr = arc->fromTrans()->asRiseFall(); + TransRiseFall *to_tr = arc->toTrans()->asRiseFall(); + // Ignore transitions other than rise/fall. + return from_tr && to_tr + && searchThru(edge->from(graph), from_tr, edge, edge->to(graph), to_tr); +} + +bool +searchThru(Vertex *from_vertex, + const TransRiseFall *from_tr, + const Edge *edge, + Vertex *to_vertex, + const TransRiseFall *to_tr) +{ + return searchThruTimingSense(edge, from_tr, to_tr) + && searchThruSimEdge(from_vertex, from_tr) + && searchThruSimEdge(to_vertex, to_tr); +} + +// set_case_analysis rising/falling filters rise/fall edges during search. +static bool +searchThruSimEdge(const Vertex *vertex, const TransRiseFall *tr) +{ + LogicValue sim_value = vertex->simValue(); + switch (sim_value) { + case LogicValue::rise: + return tr == TransRiseFall::rise(); + case LogicValue::fall: + return tr == TransRiseFall::fall(); + default: + return true; + }; +} + +static bool +searchThruTimingSense(const Edge *edge, const TransRiseFall *from_tr, + const TransRiseFall *to_tr) +{ + switch (edge->simTimingSense()) { + case TimingSense::unknown: + return true; + case TimingSense::positive_unate: + return from_tr == to_tr; + case TimingSense::negative_unate: + return from_tr != to_tr; + case TimingSense::non_unate: + return true; + case TimingSense::none: + return false; + default: + return true; + } +} + +//////////////////////////////////////////////////////////////// + +bool +hasFanin(Vertex *vertex, + SearchPred *pred, + const Graph *graph) +{ + if (pred->searchTo(vertex)) { + 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; + } + } + return false; +} + +bool +hasFanout(Vertex *vertex, + SearchPred *pred, + const Graph *graph) +{ + if (pred->searchFrom(vertex)) { + 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; + } + } + return false; +} + +} // namespace diff --git a/search/SearchPred.hh b/search/SearchPred.hh new file mode 100644 index 0000000..6d12203 --- /dev/null +++ b/search/SearchPred.hh @@ -0,0 +1,178 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SEARCH_PRED_H +#define STA_SEARCH_PRED_H + +#include "DisallowCopyAssign.hh" +#include "StaState.hh" +#include "NetworkClass.hh" +#include "GraphClass.hh" + +namespace sta { + +// Class hierarchy: +// SearchPred +// SearchPred0 (unless disabled or constant) +// EvalPred (unless timing check) +// SearchThru (unless latch D->Q, outside vertex subset) +// 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) + +// Virtual base class for search predicates. +class SearchPred +{ +public: + SearchPred() {} + virtual ~SearchPred() {} + // Search is allowed from from_vertex. + virtual bool searchFrom(const Vertex *from_vertex) = 0; + // 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; + // Search is allowed to to_pin. + virtual bool searchTo(const Vertex *to_vertex) = 0; + +private: + DISALLOW_COPY_AND_ASSIGN(SearchPred); +}; + +class SearchPred0 : public SearchPred +{ +public: + explicit SearchPred0(const StaState *sta); + // Search from a vertex unless + // disabled by constraint + // constant logic zero/one + virtual bool searchFrom(const Vertex *from_vertex); + // Search thru an edge unless + // traverses disabled from/to pin pair + // disabled by condition expression + // wire that traverses a disabled hierarchical pin + // register set/reset edge (and search thru them is disabled) + // 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); + // Search to a vertex unless + // constant logic zero/one + virtual bool searchTo(const Vertex *to_vertex); + +protected: + const StaState *sta_; + +private: + DISALLOW_COPY_AND_ASSIGN(SearchPred0); +}; + +// SearchPred0 unless +// disabled to break combinational loop +class SearchPred1 : public SearchPred0 +{ +public: + explicit SearchPred1(const StaState *sta); + virtual bool searchThru(Edge *edge); + +private: + DISALLOW_COPY_AND_ASSIGN(SearchPred1); +}; + +// SearchPred1 unless +// timing check edge +class SearchPred2 : public SearchPred1 +{ +public: + explicit SearchPred2(const StaState *sta); + virtual bool searchThru(Edge *edge); + +private: + DISALLOW_COPY_AND_ASSIGN(SearchPred2); +}; + +// SearchPred2 unless +// latch D->Q edge +class SearchPredNonLatch2 : public SearchPred2 +{ +public: + explicit SearchPredNonLatch2(const StaState *sta); + virtual bool searchThru(Edge *edge); + +private: + DISALLOW_COPY_AND_ASSIGN(SearchPredNonLatch2); +}; + +// SearchPred2 unless +// register/latch CLK->Q edges. +class SearchPredNonReg2 : public SearchPred2 +{ +public: + explicit SearchPredNonReg2(const StaState *sta); + virtual bool searchThru(Edge *edge); + +private: + DISALLOW_COPY_AND_ASSIGN(SearchPredNonReg2); +}; + +// Predicate for BFS search to stop at the end of the clock tree. +// Search only thru combinational gates and wires. +class ClkTreeSearchPred : public SearchPred1 +{ +public: + explicit ClkTreeSearchPred(const StaState *sta); + virtual bool searchThru(Edge *edge); + +private: + DISALLOW_COPY_AND_ASSIGN(ClkTreeSearchPred); +}; + +bool +isClkEnd(Vertex *vertex, Graph *graph); + +// 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); +bool +searchThru(Vertex *from_vertex, + const TransRiseFall *from_tr, + const Edge *edge, + Vertex *to_vertex, + const TransRiseFall *to_tr); + + +//////////////////////////////////////////////////////////////// + +bool +hasFanin(Vertex *vertex, + SearchPred *pred, + const Graph *graph); + +// Vertices with no fanout have at no enabled (non-disabled) edges +// leaving them. +bool +hasFanout(Vertex *vertex, + SearchPred *pred, + const Graph *graph); + +} // namespace +#endif diff --git a/search/Sim.cc b/search/Sim.cc new file mode 100644 index 0000000..624a3f4 --- /dev/null +++ b/search/Sim.cc @@ -0,0 +1,1308 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "StaConfig.hh" // CUDD +#include "Error.hh" +#include "Mutex.hh" +#include "Debug.hh" +#include "Report.hh" +#include "Stats.hh" +#include "PortDirection.hh" +#include "FuncExpr.hh" +#include "TimingRole.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "Sdc.hh" +#include "Graph.hh" +#include "Sim.hh" + +#if CUDD +#include "cudd.h" +#endif // CUDD + +namespace sta { + +static Pin * +findDrvrPin(const Pin *pin, + Network *network); + +#if CUDD + +Sim::Sim(StaState *sta) : + StaState(sta), + observer_(nullptr), + valid_(false), + incremental_(false), + const_func_pins_valid_(false), + // cacheSize = 2^15 + cudd_manager_(Cudd_Init(0, 0, CUDD_UNIQUE_SLOTS, 32768, 0)) +{ +} + +Sim::~Sim() +{ + delete observer_; + if (Cudd_CheckZeroRef(cudd_manager_) > 0) + internalErrorNoThrow("non-zero cudd reference counts"); + Cudd_Quit(cudd_manager_); +} + +TimingSense +Sim::functionSense(const FuncExpr *expr, + const Pin *input_pin, + const Instance *inst) +{ + debugPrint2(debug_, "sim", 4, "find sense pin %s %s\n", + network_->pathName(input_pin), + expr->asString()); + bool increasing, decreasing; + { + UniqueLock lock(cudd_lock_); + DdNode *bdd = funcBdd(expr, inst); + LibertyPort *input_port = network_->libertyPort(input_pin); + DdNode *input_node = ensureNode(input_port); + unsigned int input_index = Cudd_NodeReadIndex(input_node); + increasing = (Cudd_Increasing(cudd_manager_, bdd, input_index) + == Cudd_ReadOne(cudd_manager_)); + decreasing = (Cudd_Decreasing(cudd_manager_, bdd, input_index) + == Cudd_ReadOne(cudd_manager_)); + Cudd_RecursiveDeref(cudd_manager_, bdd); + clearSymtab(); + } + TimingSense sense; + if (increasing && decreasing) + sense = TimingSense::none; + else if (increasing) + sense = TimingSense::positive_unate; + else if (decreasing) + sense = TimingSense::negative_unate; + else + sense = TimingSense::non_unate; + debugPrint1(debug_, "sim", 4, " %s\n", timingSenseString(sense)); + return sense; +} + +void +Sim::clearSymtab() const +{ + BddSymbolTable::Iterator sym_iter(symtab_); + while (sym_iter.hasNext()) { + DdNode *sym_node = sym_iter.next(); + Cudd_RecursiveDeref(cudd_manager_, sym_node); + } + symtab_.clear(); +} + +LogicValue +Sim::evalExpr(const FuncExpr *expr, + const Instance *inst) const +{ + UniqueLock lock(cudd_lock_); + DdNode *bdd = funcBdd(expr, inst); + LogicValue value = LogicValue::unknown; + if (bdd == Cudd_ReadLogicZero(cudd_manager_)) + value = LogicValue::zero; + else if (bdd == Cudd_ReadOne(cudd_manager_)) + value = LogicValue::one; + if (bdd) { + Cudd_RecursiveDeref(cudd_manager_, bdd); + clearSymtab(); + } + return value; +} + +// Returns nullptr if the expression simply references an internal port. +DdNode * +Sim::funcBdd(const FuncExpr *expr, + const Instance *inst) const +{ + DdNode *left = nullptr; + DdNode *right = nullptr; + DdNode *result = nullptr; + switch (expr->op()) { + case FuncExpr::op_port: { + LibertyPort *port = expr->port(); + Pin *pin = network_->findPin(inst, port); + // Internal ports don't have instance pins. + if (pin) { + LogicValue value = logicValue(pin); + switch (value) { + case LogicValue::zero: + result = Cudd_ReadLogicZero(cudd_manager_); + break; + case LogicValue::one: + result = Cudd_ReadOne(cudd_manager_); + break; + default: + result = ensureNode(port); + break; + } + } + break; + } + case FuncExpr::op_not: + left = funcBdd(expr->left(), inst); + if (left) + result = Cudd_Not(left); + break; + case FuncExpr::op_or: + left = funcBdd(expr->left(), inst); + right = funcBdd(expr->right(), inst); + if (left && right) + result = Cudd_bddOr(cudd_manager_, left, right); + else if (left) + result = left; + else if (right) + result = right; + break; + case FuncExpr::op_and: + left = funcBdd(expr->left(), inst); + right = funcBdd(expr->right(), inst); + if (left && right) + result = Cudd_bddAnd(cudd_manager_, left, right); + else if (left) + result = left; + else if (right) + result = right; + break; + case FuncExpr::op_xor: + left = funcBdd(expr->left(), inst); + right = funcBdd(expr->right(), inst); + if (left && right) + result = Cudd_bddXor(cudd_manager_, left, right); + else if (left) + result = left; + else if (right) + result = right; + break; + case FuncExpr::op_one: + result = Cudd_ReadOne(cudd_manager_); + break; + case FuncExpr::op_zero: + result = Cudd_ReadLogicZero(cudd_manager_); + break; + default: + internalError("unknown function operator"); + } + if (result) + Cudd_Ref(result); + if (left) + Cudd_RecursiveDeref(cudd_manager_, left); + if (right) + Cudd_RecursiveDeref(cudd_manager_, right); + return result; +} + +DdNode * +Sim::ensureNode(LibertyPort *port) const +{ + const char *port_name = port->name(); + DdNode *node = symtab_.findKey(port_name); + if (node == nullptr) { + node = Cudd_bddNewVar(cudd_manager_); + symtab_[port_name] = node; + Cudd_Ref(node); + } + return node; +} + +#else +// No CUDD. + +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)]; +} + +static LogicValue +logicOr(LogicValue value1, + LogicValue value2) +{ + static LogicValue logic_or[5][5] = + {{LogicValue::zero, LogicValue::one, LogicValue::unknown, LogicValue::unknown, LogicValue::unknown}, + {LogicValue::one, LogicValue::one, LogicValue::one, LogicValue::one, LogicValue::one}, + {LogicValue::unknown,LogicValue::one, LogicValue::unknown, LogicValue::unknown, LogicValue::unknown}, + {LogicValue::unknown,LogicValue::one, LogicValue::unknown, LogicValue::unknown, LogicValue::unknown}, + {LogicValue::unknown,LogicValue::one, LogicValue::unknown, LogicValue::unknown, LogicValue::unknown}}; + return logic_or[int(value1)][int(value2)]; +} + +static LogicValue +logicAnd(LogicValue value1, + LogicValue value2) +{ + static LogicValue logic_and[5][5] = + {{LogicValue::zero,LogicValue::zero, LogicValue::zero, LogicValue::zero, LogicValue::zero}, + {LogicValue::zero,LogicValue::one, LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}, + {LogicValue::zero,LogicValue::unknown,LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}, + {LogicValue::zero,LogicValue::unknown,LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}, + {LogicValue::zero,LogicValue::unknown,LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}}; + return logic_and[int(value1)][int(value2)]; +} + +static LogicValue +logicXor(LogicValue value1, + LogicValue value2) +{ + static LogicValue logic_xor[5][5]= + {{LogicValue::zero, LogicValue::one, LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}, + {LogicValue::one, LogicValue::zero, LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}, + {LogicValue::unknown,LogicValue::unknown,LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}, + {LogicValue::unknown,LogicValue::unknown,LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}, + {LogicValue::unknown,LogicValue::unknown,LogicValue::unknown,LogicValue::unknown, LogicValue::unknown}}; + return logic_xor[int(value1)][int(value2)]; +} + +static TimingSense +senseNot(TimingSense sense) +{ + static TimingSense sense_not[5] = {TimingSense::negative_unate, + TimingSense::positive_unate, + TimingSense::non_unate, + TimingSense::none, + TimingSense::unknown}; + return sense_not[int(sense)]; +} + +static TimingSense +senseAndOr(TimingSense sense1, + TimingSense sense2) +{ + static TimingSense sense_and_or[5][5] = + {{TimingSense::positive_unate, TimingSense::non_unate, + TimingSense::non_unate, TimingSense::positive_unate, TimingSense::unknown}, + {TimingSense::non_unate, TimingSense::negative_unate, + TimingSense::non_unate, TimingSense::negative_unate, TimingSense::unknown}, + {TimingSense::non_unate, TimingSense::non_unate, TimingSense::non_unate, + TimingSense::non_unate, TimingSense::unknown}, + {TimingSense::positive_unate, TimingSense::negative_unate, + TimingSense::non_unate, TimingSense::none, TimingSense::unknown}, + {TimingSense::unknown, TimingSense::unknown, + TimingSense::unknown, TimingSense::non_unate, TimingSense::unknown}}; + return sense_and_or[int(sense1)][int(sense2)]; +} + +static TimingSense +senseXor(TimingSense sense1, + TimingSense sense2) +{ + static TimingSense xor_sense[5][5] = + {{TimingSense::non_unate, TimingSense::non_unate, + TimingSense::non_unate, TimingSense::non_unate, TimingSense::unknown}, + {TimingSense::non_unate, TimingSense::non_unate, + TimingSense::non_unate, TimingSense::non_unate, TimingSense::unknown}, + {TimingSense::non_unate, TimingSense::non_unate, + TimingSense::non_unate, TimingSense::non_unate, TimingSense::unknown}, + {TimingSense::non_unate, TimingSense::non_unate, + TimingSense::non_unate, TimingSense::none, TimingSense::unknown}, + {TimingSense::unknown, TimingSense::unknown, + TimingSense::unknown, TimingSense::unknown, TimingSense::unknown}}; + return xor_sense[int(sense1)][int(sense2)]; +} + +Sim::Sim(StaState *sta) : + StaState(sta), + observer_(nullptr), + valid_(false), + incremental_(false), + const_func_pins_valid_(false) +{ +} + +Sim::~Sim() +{ + delete observer_; +} + +TimingSense +Sim::functionSense(const FuncExpr *expr, + const Pin *input_pin, + const Instance *inst) +{ + TimingSense sense; + LogicValue value; + functionSense(expr, input_pin, inst, sense, value); + return sense; +} + +void +Sim::functionSense(const FuncExpr *expr, + const Pin *input_pin, + const Instance *inst, + // return values + TimingSense &sense, + LogicValue &value) const +{ + switch (expr->op()) { + case FuncExpr::op_port: { + Pin *pin = network_->findPin(inst, expr->port()); + if (pin) { + if (pin == input_pin) + sense = TimingSense::positive_unate; + else + sense = TimingSense::none; + value = logicValue(pin); + } + else { + sense = TimingSense::none; + value = LogicValue::unknown; + } + break; + } + case FuncExpr::op_not: { + TimingSense sense1; + LogicValue value1; + functionSense(expr->left(), input_pin, inst, sense1, value1); + if (value1 == LogicValue::zero) { + sense = TimingSense::none; + value = LogicValue::one; + } + else if (value1 == LogicValue::one) { + sense = TimingSense::none; + value = LogicValue::zero; + } + else { + sense = senseNot(sense1); + value = LogicValue::unknown; + } + break; + } + case FuncExpr::op_or: { + TimingSense sense1, sense2; + LogicValue value1, value2; + functionSense(expr->left(), input_pin, inst, sense1, value1); + functionSense(expr->right(), input_pin, inst, sense2, value2); + if (value1 == LogicValue::one || value2 == LogicValue::one) { + sense = TimingSense::none; + value = LogicValue::one; + } + else if (value1 == LogicValue::zero) { + sense = sense2; + value = value2; + } + else if (value2 == LogicValue::zero) { + sense = sense1; + value = value1; + } + else { + sense = senseAndOr(sense1, sense2); + value = LogicValue::unknown; + } + break; + } + case FuncExpr::op_and: { + TimingSense sense1, sense2; + LogicValue value1, value2; + functionSense(expr->left(), input_pin, inst, sense1, value1); + functionSense(expr->right(), input_pin, inst, sense2, value2); + if (value1 == LogicValue::zero || value2 == LogicValue::zero) { + sense = TimingSense::none; + value = LogicValue::zero; + } + else if (value1 == LogicValue::one) { + sense = sense2; + value = value2; + } + else if (value2 == LogicValue::one) { + sense = sense1; + value = value1; + } + else { + sense = senseAndOr(sense1, sense2); + value = LogicValue::unknown; + } + break; + } + case FuncExpr::op_xor: { + TimingSense sense1, sense2; + LogicValue value1, value2; + functionSense(expr->left(), input_pin, inst, sense1, value1); + functionSense(expr->right(), input_pin, inst, sense2, value2); + if ((value1 == LogicValue::zero && value2 == LogicValue::zero) + || (value1 == LogicValue::one && value2 == LogicValue::one)) { + sense = TimingSense::none; + value = LogicValue::zero; + } + else if ((value1 == LogicValue::zero && value2 == LogicValue::one) + || (value1 == LogicValue::one && value2 == LogicValue::zero)) { + sense = TimingSense::none; + value = LogicValue::one; + } + else if (value1 == LogicValue::zero) { + sense = sense2; + value = value2; + } + else if (value1 == LogicValue::one) { + sense = senseNot(sense2); + value = logicNot(value2); + } + else if (value2 == LogicValue::zero) { + sense = sense1; + value = value1; + } + else if (value2 == LogicValue::one) { + sense = senseNot(sense1); + value = logicNot(value1); + } + else { + sense = senseXor(sense1, sense2); + value = logicXor(value1, value2); + } + break; + } + case FuncExpr::op_one: + sense = TimingSense::none; + value = LogicValue::one; + break; + case FuncExpr::op_zero: + sense = TimingSense::none; + value = LogicValue::zero; + break; + } +} + +LogicValue +Sim::evalExpr(const FuncExpr *expr, + const Instance *inst) const +{ + switch (expr->op()) { + case FuncExpr::op_port: { + Pin *pin = network_->findPin(inst, expr->port()->name()); + if (pin) + return logicValue(pin); + else + // Internal ports don't have instance pins. + return LogicValue::unknown; + } + case FuncExpr::op_not: + return logicNot(evalExpr(expr->left(), inst)); + case FuncExpr::op_or: + return logicOr(evalExpr(expr->left(),inst), + evalExpr(expr->right(),inst)); + case FuncExpr::op_and: + return logicAnd(evalExpr(expr->left(),inst), + evalExpr(expr->right(),inst)); + case FuncExpr::op_xor: + return logicXor(evalExpr(expr->left(),inst), + evalExpr(expr->right(),inst)); + case FuncExpr::op_one: + return LogicValue::one; + case FuncExpr::op_zero: + return LogicValue::zero; + } + // Prevent warnings from lame compilers. + return LogicValue::zero; +} + +#endif // CUDD + +void +Sim::clear() +{ + valid_ = false; + 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(); +} + +void +Sim::setObserver(SimObserver *observer) +{ + delete observer_; + observer_ = observer; +} + +void +Sim::ensureConstantsPropagated() +{ + if (!valid_) { + Stats stats(debug_); + ensureConstantFuncPins(); + instances_to_annotate_.clear(); + if (incremental_) { + seedInvalidConstants(); + propagateToInvalidLoads(); + propagateFromInvalidDrvrsToLoads(); + } + else { + clearSimValues(); + seedConstants(); + } + invalid_insts_.clear(); + propagateConstants(); + annotateGraphEdges(); + valid_ = true; + incremental_ = true; + + stats.report("Propagate constants"); + } +} + +void +Sim::seedInvalidConstants() +{ + InstanceSet::Iterator inst_iter(invalid_insts_); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + eval_queue_.push(inst); + } +} + +void +Sim::propagateToInvalidLoads() +{ + PinSet::Iterator load_iter(invalid_load_pins_); + while (load_iter.hasNext()) { + Pin *load_pin = load_iter.next(); + Net *net = network_->net(load_pin); + if (network_->isGround(net)) + setPinValue(load_pin, LogicValue::zero, true); + else if (network_->isPower(net)) + setPinValue(load_pin, LogicValue::one, true); + else { + Pin *drvr_pin = findDrvrPin(load_pin, network_); + if (drvr_pin) + propagateDrvrToLoad(drvr_pin, load_pin); + } + } + invalid_load_pins_.clear(); +} + +void +Sim::propagateFromInvalidDrvrsToLoads() +{ + PinSet::Iterator drvr_iter(invalid_drvr_pins_); + while (drvr_iter.hasNext()) { + Pin *drvr_pin = drvr_iter.next(); + LogicValue value = logicValue(drvr_pin); + PinConnectedPinIterator *load_iter=network_->connectedPinIterator(drvr_pin); + while (load_iter->hasNext()) { + Pin *load_pin = load_iter->next(); + if (load_pin != drvr_pin + && network_->isLoad(load_pin)) + setPinValue(load_pin, value, true); + } + delete load_iter; + } + invalid_drvr_pins_.clear(); +} + +void +Sim::propagateDrvrToLoad(Pin *drvr_pin, + Pin *load_pin) +{ + LogicValue value = logicValue(drvr_pin); + setPinValue(load_pin, value, true); +} + +void +Sim::constantsInvalid() +{ + valid_ = false; + incremental_ = false; +} + +void +Sim::ensureConstantFuncPins() +{ + if (!const_func_pins_valid_) { + LeafInstanceIterator *inst_iter = network_->leafInstanceIterator(); + while (inst_iter->hasNext()) { + Instance *inst = inst_iter->next(); + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + recordConstPinFunc(pin); + } + delete pin_iter; + } + delete inst_iter; + const_func_pins_valid_ = true; + } +} + +void +Sim::recordConstPinFunc(Pin *pin) +{ + LibertyPort *port = network_->libertyPort(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)) + const_func_pins_.insert(pin); + } +} + +void +Sim::deleteInstanceBefore(Instance *inst) +{ + instances_with_const_pins_.erase(inst); + invalid_insts_.erase(inst); +} + +void +Sim::makePinAfter(Pin *pin) +{ + // Incrementally update const_func_pins_. + recordConstPinFunc(pin); +} + +void +Sim::deletePinBefore(Pin *pin) +{ + // Incrementally update const_func_pins_. + const_func_pins_.erase(pin); + invalid_load_pins_.erase(pin); + invalid_drvr_pins_.erase(pin); + invalid_insts_.insert(network_->instance(pin)); +} + +void +Sim::connectPinAfter(Pin *pin) +{ + // Incrementally update const_func_pins_. + recordConstPinFunc(pin); + if (incremental_) { + if (network_->isLoad(pin)) + invalid_load_pins_.insert(pin); + if (network_->isDriver(pin)) + invalid_drvr_pins_.insert(pin); + valid_ = false; + } +} + +void +Sim::disconnectPinBefore(Pin *pin) +{ + if (incremental_ + && network_->isLoad(pin)) + removePropagatedValue(pin); +} + +void +Sim::pinSetFuncAfter(Pin *pin) +{ + if (incremental_) { + Instance *inst = network_->instance(pin); + if (instances_with_const_pins_.hasKey(inst)) + invalid_insts_.insert(inst); + valid_ = false; + } + // Incrementally update const_func_pins_. + const_func_pins_.erase(pin); + recordConstPinFunc(pin); +} + +void +Sim::seedConstants() +{ + // Propagate constants from inputs tied hi/low in the network. + enqueueConstantPinInputs(true); + // Propagate set_LogicValue::zero, set_LogicValue::one, set_logic_dc constants. + setConstraintConstPins(sdc_->logicValues(), true); + // Propagate set_case_analysis constants. + setConstraintConstPins(sdc_->caseLogicValues(), true); + // Propagate 0/1 constant functions. + setConstFuncPins(true); +} + +void +Sim::propagateConstants() +{ + while (!eval_queue_.empty()) { + const Instance *inst = eval_queue_.front(); + eval_queue_.pop(); + evalInstance(inst); + } +} + +void +Sim::setConstraintConstPins(LogicValueMap *value_map, + bool propagate) +{ + LogicValueMap::ConstIterator value_iter(value_map); + while (value_iter.hasNext()) { + LogicValue value; + const Pin *pin; + value_iter.next(pin, value); + debugPrint2(debug_, "sim", 2, "case pin %s = %c\n", + 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); + while (pin_iter->hasNext()) { + 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, propagate); + } + delete pin_iter; + } + else + setPinValue(pin, value, propagate); + } +} + +// Propagate constants from outputs with constant functions +// (tie high and tie low cell instances). +void +Sim::setConstFuncPins(bool propagate) +{ + PinSet::Iterator const_pin_iter(const_func_pins_); + while (const_pin_iter.hasNext()) { + Pin *pin = const_pin_iter.next(); + LibertyPort *port = network_->libertyPort(pin); + if (port) { + FuncExpr *expr = port->function(); + if (expr->op() == FuncExpr::op_zero) { + debugPrint1(debug_, "sim", 2, "func pin %s = 0\n", + network_->pathName(pin)); + setPinValue(pin, LogicValue::zero, propagate); + } + else if (expr->op() == FuncExpr::op_one) { + debugPrint1(debug_, "sim", 2, "func pin %s = 1\n", + network_->pathName(pin)); + setPinValue(pin, LogicValue::one, propagate); + } + } + } +} + +void +Sim::enqueueConstantPinInputs(bool propagate) +{ + ConstantPinIterator *const_iter = network_->constantPinIterator(); + while (const_iter->hasNext()) { + LogicValue value; + Pin *pin; + const_iter->next(pin, value); + debugPrint2(debug_, "sim", 2, "network constant pin %s = %c\n", + network_->pathName(pin), + logicValueString(value)); + setPinValue(pin, value, propagate); + } + delete const_iter; +} + +void +Sim::removePropagatedValue(const Pin *pin) +{ + Instance *inst = network_->instance(pin); + if (instances_with_const_pins_.hasKey(inst)) { + invalid_insts_.insert(inst); + valid_ = false; + + LogicValue constraint_value; + bool exists; + sdc_->caseLogicValue(pin, constraint_value, exists); + if (!exists) { + sdc_->logicValue(pin, constraint_value, exists); + if (!exists) { + debugPrint1(debug_, "sim", 2, "pin %s remove prop constant\n", + network_->pathName(pin)); + Vertex *vertex = graph_->pinLoadVertex(pin); + setSimValue(vertex, LogicValue::unknown); + } + } + } +} + +void +Sim::setPinValue(const Pin *pin, + LogicValue value, + bool propagate) +{ + LogicValue constraint_value; + bool exists; + sdc_->caseLogicValue(pin, constraint_value, exists); + if (!exists) + sdc_->logicValue(pin, constraint_value, exists); + if (exists + && value != constraint_value) { + if (value != LogicValue::unknown) + report_->warn("propagated logic value %c differs from constraint value of %c on pin %s.\n", + logicValueString(value), + logicValueString(constraint_value), + sdc_network_->pathName(pin)); + } + else { + debugPrint2(debug_, "sim", 3, "pin %s = %c\n", + network_->pathName(pin), + logicValueString(value)); + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + // Set vertex constant flags. + if (vertex) + setSimValue(vertex, value); + if (bidirect_drvr_vertex) + setSimValue(bidirect_drvr_vertex, value); + Instance *inst = network_->instance(pin); + if (logicValueZeroOne(value)) + instances_with_const_pins_.insert(inst); + instances_to_annotate_.insert(inst); + if (propagate) { + 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); + while (pin_iter->hasNext()) { + Pin *pin1 = pin_iter->next(); + if (pin1 != pin + && network_->isLoad(pin1)) + setPinValue(pin1, value, propagate); + } + delete pin_iter; + } + } + } +} + +void +Sim::evalInstance(const Instance *inst) +{ + debugPrint1(debug_, "sim", 2, "eval %s\n", network_->pathName(inst)); + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + PortDirection *dir = network_->direction(pin); + if (dir->isAnyOutput()) { + LibertyPort *port = network_->libertyPort(pin); + if (port) { + FuncExpr *expr = port->function(); + if (expr) { + LogicValue value = evalExpr(expr, inst); + FuncExpr *tri_en_expr = port->tristateEnable(); + if (tri_en_expr == nullptr + || evalExpr(tri_en_expr, inst) == LogicValue::one) { + debugPrint3(debug_, "sim", 2, " %s %s = %c\n", + port->name(), + expr->asString(), + logicValueString(value)); + if (value != logicValue(pin)) + setPinValue(pin, value, true); + } + } + } + } + } + delete pin_iter; +} + +void +Sim::setSimValue(Vertex *vertex, + LogicValue value) +{ + if (value != vertex->simValue()) { + vertex->setSimValue(value); + if (observer_) + observer_->valueChangeAfter(vertex); + } +} + +TimingSense +Sim::functionSense(const Instance *inst, + const Pin *from_pin, + const Pin *to_pin) +{ + if (logicZeroOne(from_pin)) + return TimingSense::none; + else { + LibertyPort *from_port = network_->libertyPort(from_pin); + LibertyPort *to_port = network_->libertyPort(to_pin); + 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); + } + } + } + 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)) { + Pin *drvr_pin = findDrvrPin(pin, network_); + if (drvr_pin) + return logicValue(drvr_pin); + } + return LogicValue::unknown; + } +} + +static Pin * +findDrvrPin(const Pin *pin, + 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()); +} + +void +Sim::clearSimValues() +{ + InstanceSet::Iterator inst_iter(instances_with_const_pins_); + while (inst_iter.hasNext()) { + const Instance *inst = inst_iter.next(); + // 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) +{ + debugPrint1(debug_, "sim", 4, "clear %s\n", + 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() +{ + InstanceSet::Iterator inst_iter(instances_to_annotate_); + while (inst_iter.hasNext()) { + const Instance *inst = inst_iter.next(); + annotateVertexEdges(inst, true); + } +} + +void +Sim::annotateVertexEdges(const Instance *inst, + bool annotate) +{ + debugPrint2(debug_, "sim", 4, "annotate %s %s\n", + 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) +{ + 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_) + || isTestDisabled(inst, from_pin, pin, + 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 (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, + const Sim *sim) +{ + bool is_disabled; + FuncExpr *disable_cond; + isCondDisabled(edge, inst, from_pin, to_pin, network, sim, + 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, + const Sim *sim, + 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); + disable_cond = cond; + is_disabled = (cond_value == LogicValue::zero); + } + else { + // 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); + LibertyCellTimingArcSetIterator cond_iter(cell, from_port, to_port); + is_disabled = false; + while (cond_iter.hasNext()) { + TimingArcSet *cond_set = cond_iter.next(); + FuncExpr *cond = cond_set->cond(); + if (cond && sim->evalExpr(cond, inst) == LogicValue::one) { + disable_cond = cond; + is_disabled = true; + break; + } + } + } +} + +bool +isModeDisabled(Edge *edge, + const Instance *inst, + const Network *network, + const Sim *sim) +{ + bool is_disabled; + FuncExpr *disable_cond; + isModeDisabled(edge, inst, network, sim, + is_disabled, disable_cond); + return is_disabled; +} + +void +isModeDisabled(Edge *edge, + const Instance *inst, + const Network *network, + const Sim *sim, + bool &is_disabled, + FuncExpr *&disable_cond) +{ + // Default values. + is_disabled = false; + disable_cond = 0; + 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); + if (mode_def) { + 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. + ModeValueMap::Iterator iter(mode_def->values()); + while (iter.hasNext()) { + ModeValueDef *value_def1 = iter.next(); + if (value_def1) { + FuncExpr *cond1 = value_def1->cond(); + if (cond1) { + LogicValue cond_value1 = sim->evalExpr(cond1, inst); + if (cond_value1 == LogicValue::one) { + disable_cond = cond; + is_disabled = true; + break; + } + } + } + } + } + } + } + } + } +} + +bool +isTestDisabled(const Instance *inst, + const Pin *from_pin, + const Pin *to_pin, + const Network *network, + const Sim *sim) +{ + bool is_disabled; + Pin *scan_enable; + isTestDisabled(inst, from_pin, to_pin, network, sim, + is_disabled, scan_enable); + return is_disabled; +} + +void +isTestDisabled(const Instance *inst, + const Pin *from_pin, + const Pin *to_pin, + const Network *network, + const Sim *sim, + bool &is_disabled, + Pin *&scan_enable) +{ + is_disabled = false; + LibertyCell *cell = network->libertyCell(inst); + if (cell) { + TestCell *test = cell->testCell(); + if (test) { + LibertyPort *from_port = network->libertyPort(from_pin); + LibertyPort *to_port = network->libertyPort(to_pin); + LibertyPort *data_in_port = test->dataIn(); + LibertyPort *scan_in_port = test->scanIn(); + if (from_port == data_in_port + || to_port == data_in_port + || from_port == scan_in_port + || to_port == scan_in_port) { + LibertyPort *scan_enable_port = test->scanEnable(); + if (scan_enable_port) { + scan_enable = network->findPin(inst, scan_enable_port); + if (scan_enable) { + LogicValue scan_enable_value = sim->logicValue(scan_enable); + is_disabled = ((scan_enable_value == LogicValue::zero + && (from_port == scan_in_port + || to_port == scan_in_port)) + || (scan_enable_value == LogicValue::one + && (from_port == data_in_port + || to_port == data_in_port))); + } + } + } + } + } +} + +} // namespace diff --git a/search/Sim.hh b/search/Sim.hh new file mode 100644 index 0000000..a87c0c9 --- /dev/null +++ b/search/Sim.hh @@ -0,0 +1,221 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SIM_H +#define STA_SIM_H + +#include +#include +#include "StaConfig.hh" // CUDD +#include "DisallowCopyAssign.hh" +#include "Map.hh" +#include "StaState.hh" +#include "NetworkClass.hh" +#include "GraphClass.hh" +#include "SdcClass.hh" + +struct DdNode; +struct DdManager; + +namespace sta { + +class SimObserver; + +typedef Map PinValueMap; +typedef std::queue EvalQueue; +typedef Map BddSymbolTable; + +// Propagate constants from constraints and netlist tie high/low +// connections thru gates. +class Sim : public StaState +{ +public: + explicit Sim(StaState *sta); + virtual ~Sim(); + void clear(); + // Set the observer for simulation value changes. + void setObserver(SimObserver *observer); + void ensureConstantsPropagated(); + void constantsInvalid(); + LogicValue evalExpr(const FuncExpr *expr, + const Instance *inst) const; + LogicValue logicValue(const Pin *pin) const; + bool logicZeroOne(const Pin *pin) const; + bool logicZeroOne(const Vertex *vertex) const; + // 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); + + // Network edits. + void deleteInstanceBefore(Instance *inst); + void makePinAfter(Pin *pin); + void deletePinBefore(Pin *pin); + void connectPinAfter(Pin *pin); + void disconnectPinBefore(Pin *pin); + void pinSetFuncAfter(Pin *pin); + +protected: + void ensureConstantFuncPins(); + void recordConstPinFunc(Pin *pin); + virtual void seedConstants(); + void seedInvalidConstants(); + void propagateConstants(); + void setConstraintConstPins(LogicValueMap *pin_value_map, + bool propagate); + void setConstFuncPins(bool propagate); + void enqueueConstantPinInputs(bool propagate); + virtual void setPinValue(const Pin *pin, + LogicValue value, + bool propagate); + void enqueue(const Instance *inst); + void evalInstance(const Instance *inst); + TimingSense functionSense(const FuncExpr *expr, + 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; + 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 removePropagatedValue(const Pin *pin); + void propagateFromInvalidDrvrsToLoads(); + void propagateToInvalidLoads(); + void propagateDrvrToLoad(Pin *drvr_pin, + Pin *load_pin); + void setSimValue(Vertex *vertex, + LogicValue value); + + SimObserver *observer_; + bool valid_; + bool incremental_; + // Cache of pins that have constant functions (tie high and tie low + // cell instances). + PinSet const_func_pins_; + bool const_func_pins_valid_; + // Instances that require incremental constant propagation. + InstanceSet invalid_insts_; + // Driver pins waiting to propagate constant to loads. + PinSet invalid_drvr_pins_; + // 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_; + +#ifdef CUDD + DdNode *funcBdd(const FuncExpr *expr, + const Instance *inst) const; + DdNode *ensureNode(LibertyPort *port) const; + void clearSymtab() const; + + DdManager *cudd_manager_; + mutable BddSymbolTable symtab_; + mutable std::mutex cudd_lock_; +#endif // CUDD + +private: + DISALLOW_COPY_AND_ASSIGN(Sim); +}; + +// Abstract base class for Sim value change observer. +class SimObserver +{ +public: + SimObserver() {} + virtual ~SimObserver() {} + virtual void valueChangeAfter(Vertex *vertex) = 0; + virtual void faninEdgesChangeAfter(Vertex *vertex) = 0; + virtual void fanoutEdgesChangeAfter(Vertex *vertex) = 0; + +private: + DISALLOW_COPY_AND_ASSIGN(SimObserver); +}; + +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, + const 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, + const 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, + const Sim *sim); +void +isModeDisabled(Edge *edge, + const Instance *inst, + const Network *network, + const Sim *sim, + bool &is_disabled, + FuncExpr *&disable_cond); + +// Edge is disabled because by test scan enable. +// from scan_data_in and scan_enable=1 +// from scan_in and scan_enable=0 +bool +isTestDisabled(const Instance *inst, + const Pin *from_pin, + const Pin *to_pin, + const Network *network, + const Sim *sim); + +void +isTestDisabled(const Instance *inst, + const Pin *from_pin, + const Pin *to_pin, + const Network *network, + const Sim *sim, + bool &is_disabled, + Pin *&scan_enable); + +} // namespace +#endif diff --git a/search/Sta.cc b/search/Sta.cc new file mode 100644 index 0000000..49de3b4 --- /dev/null +++ b/search/Sta.cc @@ -0,0 +1,4986 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "ReportTcl.hh" +#include "Debug.hh" +#include "Stats.hh" +#include "Units.hh" +#include "Fuzzy.hh" +#include "PortDirection.hh" +#include "TimingRole.hh" +#include "TimingArc.hh" +#include "FuncExpr.hh" +#include "EquivCells.hh" +#include "Liberty.hh" +#include "LibertyReader.hh" +#include "Network.hh" +#include "MakeConcreteNetwork.hh" +#include "VerilogReader.hh" +#include "SdcNetwork.hh" +#include "Graph.hh" +#include "GraphCmp.hh" +#include "Levelize.hh" +#include "Sdc.hh" +#include "WriteSdc.hh" +#include "ExceptionPath.hh" +#include "MakeConcreteParasitics.hh" +#include "Parasitics.hh" +#include "DelayCalc.hh" +#include "ArcDelayCalc.hh" +#include "GraphDelayCalc1.hh" +#include "DcalcAnalysisPt.hh" +#include "Sim.hh" +#include "ClkInfo.hh" +#include "Tag.hh" +#include "TagGroup.hh" +#include "PathVertex.hh" +#include "PathAnalysisPt.hh" +#include "Corner.hh" +#include "Search.hh" +#include "Latches.hh" +#include "PathGroup.hh" +#include "CheckTiming.hh" +#include "SpefReader.hh" +#include "CheckSlewLimits.hh" +#include "CheckMinPulseWidths.hh" +#include "CheckMinPeriods.hh" +#include "CheckMaxSkews.hh" +#include "ClkSkew.hh" +#include "FindRegister.hh" +#include "ReportPath.hh" +#include "VisitPathGroupVertices.hh" +#include "SdfWriter.hh" +#include "Genclks.hh" +#include "Power.hh" +#include "Sta.hh" + +namespace sta { + +using std::min; + +static const ClockEdge *clk_edge_wildcard = reinterpret_cast(1); + +static bool +libertyPortCapsEqual(LibertyPort *port1, + LibertyPort *port2); +static bool +hasDisabledArcs(Edge *edge, + Graph *graph); +static InstanceSet * +pinInstances(PinSet *pins, + const Network *network); + +//////////////////////////////////////////////////////////////// +// +// Observers are used to propagate updates from a component +// to other components. +// +//////////////////////////////////////////////////////////////// + +// When an incremental change is made the delay calculation +// changes downstream. This invalidates the required times +// for all vertices upstream of the changes. +class StaDelayCalcObserver : public DelayCalcObserver +{ +public: + explicit StaDelayCalcObserver(Search *search); + virtual void delayChangedFrom(Vertex *vertex); + virtual void delayChangedTo(Vertex *vertex); + virtual void checkDelayChangedTo(Vertex *vertex); + +private: + DISALLOW_COPY_AND_ASSIGN(StaDelayCalcObserver); + + Search *search_; +}; + +StaDelayCalcObserver::StaDelayCalcObserver(Search *search) : + DelayCalcObserver(), + search_(search) +{ +} + +void +StaDelayCalcObserver::delayChangedFrom(Vertex *vertex) +{ + search_->requiredInvalid(vertex); +} + +void +StaDelayCalcObserver::delayChangedTo(Vertex *vertex) +{ + search_->arrivalInvalid(vertex); +} + +void +StaDelayCalcObserver::checkDelayChangedTo(Vertex *vertex) +{ + search_->requiredInvalid(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: + DISALLOW_COPY_AND_ASSIGN(StaSimObserver); + + GraphDelayCalc *graph_delay_calc_; + Levelize *levelize_; + Search *search_; +}; + +StaSimObserver::StaSimObserver(GraphDelayCalc *graph_delay_calc, + Levelize *levelize, + Search *search) : + SimObserver(), + graph_delay_calc_(graph_delay_calc), + levelize_(levelize), + search_(search) +{ +} + +// When pins with constant values are incrementally connected to a net +// the downstream delays and arrivals will not be updated (removed) +// because the search predicate does not search through constants. +// This observer makes sure the delays and arrivals are invalidated. +void +StaSimObserver::valueChangeAfter(Vertex *vertex) +{ + graph_delay_calc_->delayInvalid(vertex); + search_->arrivalInvalid(vertex); + search_->requiredInvalid(vertex); + search_->endpointInvalid(vertex); + levelize_->invalidFrom(vertex); +} + +void +StaSimObserver::faninEdgesChangeAfter(Vertex *vertex) +{ + graph_delay_calc_->delayInvalid(vertex); + search_->arrivalInvalid(vertex); + search_->endpointInvalid(vertex); +} + +void +StaSimObserver::fanoutEdgesChangeAfter(Vertex *vertex) +{ + search_->requiredInvalid(vertex); + search_->endpointInvalid(vertex); +} + +//////////////////////////////////////////////////////////////// + +class StaLevelizeObserver : public LevelizeObserver +{ +public: + StaLevelizeObserver(Search *search); + virtual void levelChangedBefore(Vertex *vertex); + +private: + DISALLOW_COPY_AND_ASSIGN(StaLevelizeObserver); + + Search *search_; +}; + +StaLevelizeObserver::StaLevelizeObserver(Search *search) : + search_(search) +{ +} + +void +StaLevelizeObserver::levelChangedBefore(Vertex *vertex) +{ + search_->levelChangedBefore(vertex); +} + +//////////////////////////////////////////////////////////////// + +void +initSta() +{ + initElapsedTime(); + TimingRole::init(); + PortDirection::init(); + initTmpStrings(); + initLiberty(); + initDelayConstants(); + registerDelayCalcs(); + initPathSenseThru(); +} + +void +deleteAllMemory() +{ + // Verilog modules refer to the network in the sta so it has + // to deleted before the sta. + deleteVerilogReader(); + Sta *sta = Sta::sta(); + if (sta) { + delete sta; + Sta::setSta(nullptr); + } + deleteDelayCalcs(); + deleteTmpStrings(); + TimingRole::destroy(); + PortDirection::destroy(); + deleteLiberty(); +} + +//////////////////////////////////////////////////////////////// + +// Singleton used by TCL commands. +Sta *Sta::sta_; + +Sta::Sta() : + StaState(), + current_instance_(nullptr), + check_timing_(nullptr), + check_slew_limits_(nullptr), + check_min_pulse_widths_(nullptr), + check_min_periods_(nullptr), + check_max_skews_(nullptr), + clk_skews_(nullptr), + report_path_(nullptr), + power_(nullptr), + link_make_black_boxes_(true), + update_genclks_(false), + equiv_cells_(nullptr) +{ +} + +void +Sta::makeComponents() +{ + makeReport(); + makeDebug(); + makeUnits(); + makeNetwork(); + makeSdc(); + makeLevelize(); + makeParasitics(); + makeCorners(); + makeArcDelayCalc(); + makeGraphDelayCalc(); + makeSim(); + makeSearch(); + makeLatches(); + makeCmdNetwork(); + makeReportPath(); + makePower(); + updateComponentsState(); + + makeObservers(); + // This must follow updateComponentsState. + corners_->makeParasiticAnalysisPtsSingle(); + setThreadCount(defaultThreadCount()); +} + +void +Sta::makeObservers() +{ + graph_delay_calc_->setObserver(new StaDelayCalcObserver(search_)); + sim_->setObserver(new StaSimObserver(graph_delay_calc_, levelize_, search_)); + levelize_->setObserver(new StaLevelizeObserver(search_)); +} + +int +Sta::defaultThreadCount() const +{ + return 1; +} + +void +Sta::setThreadCount(int thread_count) +{ + thread_count_ = thread_count; + updateComponentsState(); +} + +void +Sta::updateComponentsState() +{ + // These components do not use StaState: + // units_ + network_->copyState(this); + cmd_network_->copyState(this); + sdc_network_->copyState(this); + if (graph_) + graph_->copyState(this); + sdc_->copyState(this); + corners_->copyState(this); + levelize_->copyState(this); + parasitics_->copyState(this); + if (arc_delay_calc_) + 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); + if (power_) + power_->copyState(this); +} + +void +Sta::makeReport() +{ + report_ = new ReportTcl(); +} + +void +Sta::makeDebug() +{ + debug_ = new Debug(report_); +} + +void +Sta::makeUnits() +{ + units_ = new Units(); +} + +void +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() +{ + arc_delay_calc_ = makeDelayCalc("dmp_ceff_elmore", this); +} + +void +Sta::makeGraphDelayCalc() +{ + graph_delay_calc_ = new GraphDelayCalc1(this); +} + +void +Sta::makeSim() +{ + sim_ = new Sim(this); +} + +void +Sta::makeSearch() +{ + search_ = new Search(this); +} + +void +Sta::makeLatches() +{ + latches_ = new Latches(this); +} + +void +Sta::makeCmdNetwork() +{ + sdc_network_ = makeSdcNetwork(network_); + cmd_network_ = sdc_network_; + cmd_namespace_ = CmdNamespace::sdc; +} + +void +Sta::makeCheckTiming() +{ + check_timing_ = new CheckTiming(this); +} + +void +Sta::makeCheckSlewLimits() +{ + check_slew_limits_ = new CheckSlewLimits(this); +} + +void +Sta::makeCheckMinPulseWidths() +{ + check_min_pulse_widths_ = new CheckMinPulseWidths(this); +} + +void +Sta::makeCheckMinPeriods() +{ + check_min_periods_ = new CheckMinPeriods(this); +} + +void +Sta::makeCheckMaxSkews() +{ + check_max_skews_ = new CheckMaxSkews(this); +} + +void +Sta::makeReportPath() +{ + report_path_ = new ReportPath(this); +} + +void +Sta::makePower() +{ + power_ = new Power(this); +} + +void +Sta::setSta(Sta *sta) +{ + sta_ = sta; +} + +Sta * +Sta::sta() +{ + return sta_; +} + +Sta::~Sta() +{ + // Delete "top down" to minimize chance of referencing deleted memory. + delete check_slew_limits_; + delete check_min_pulse_widths_; + delete check_min_periods_; + delete check_max_skews_; + delete clk_skews_; + delete check_timing_; + delete report_path_; + // Constraints reference search filter, so delete search first. + delete search_; + delete latches_; + delete parasitics_; + if (arc_delay_calc_) + 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 power_; + delete equiv_cells_; +} + +void +Sta::clear() +{ + // Constraints reference search filter, so clear search first. + search_->clear(); + sdc_->clear(); + // corners are NOT cleared because they are used to index liberty files. + 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(); + delete graph_; + graph_ = nullptr; + current_instance_ = nullptr; + // Notify components that graph is toast. + updateComponentsState(); +} + +void +Sta::setTclInterp(Tcl_Interp *interp) +{ + tcl_interp_ = interp; + report_->setTclInterp(interp); +} + +Tcl_Interp * +Sta::tclInterp() +{ + return tcl_interp_; +} + +CmdNamespace +Sta::cmdNamespace() +{ + return cmd_namespace_; +} + +void +Sta::setCmdNamespace(CmdNamespace namespc) +{ + cmd_namespace_ = namespc; + switch (cmd_namespace_) { + case CmdNamespace::sta: + cmd_network_ = network_; + break; + case CmdNamespace::sdc: + cmd_network_ = sdc_network_; + break; + } + updateComponentsState(); +} + +Instance * +Sta::currentInstance() const +{ + if (current_instance_ == nullptr) + return network_->topInstance(); + else + return current_instance_; +} + +void +Sta::setCurrentInstance(Instance *inst) +{ + current_instance_ = inst; +} + +//////////////////////////////////////////////////////////////// + +LibertyLibrary * +Sta::readLiberty(const char *filename, + Corner *corner, + const MinMaxAll *min_max, + bool infer_latches) +{ + Stats stats(debug_); + LibertyLibrary *library = readLibertyFile(filename, corner, min_max, + infer_latches, network_); + if (library + // The default library is the first library read. + // This corresponds to a link_path of '*'. + && network_->defaultLibertyLibrary() == nullptr) { + network_->setDefaultLibertyLibrary(library); + // Set units from default (first) library. + units_->copy(library->units()); + } + stats.report("Read liberty"); + return library; +} + +LibertyLibrary * +Sta::readLibertyFile(const char *filename, + Corner *corner, + const MinMaxAll *min_max, + bool infer_latches, + Network *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()); + network_->readLibertyAfter(liberty); + } + return liberty; +} + +LibertyLibrary * +Sta::readLibertyFile(const char *filename, + bool infer_latches, + Network *network) +{ + return sta::readLibertyFile(filename, infer_latches, network); +} + +void +Sta::readLibertyAfter(LibertyLibrary *liberty, + Corner *corner, + const MinMax *min_max) +{ + corner->addLiberty(liberty, min_max); + LibertyLibrary::makeCornerMap(liberty, corner->libertyIndex(min_max), + network_, report_); +} + +bool +Sta::setMinLibrary(const char *min_filename, + const char *max_filename) +{ + LibertyLibrary *max_lib = network_->findLibertyFilename(max_filename); + if (max_lib) { + LibertyLibrary *min_lib = readLibertyFile(min_filename, cmd_corner_, + MinMaxAll::min(), false, + network_); + return min_lib != nullptr; + } + else + return false; +} + +void +Sta::readNetlistBefore() +{ + clear(); + NetworkReader *network_reader = networkReader(); + if (network_reader) + network_reader->readNetlistBefore(); +} + +bool +Sta::linkDesign(const char *top_cell_name) +{ + clear(); + Stats stats(debug_); + bool status = network_->linkNetwork(top_cell_name, + link_make_black_boxes_, + report_); + stats.report("Link"); + return status; +} + +bool +Sta::linkMakeBlackBoxes() const +{ + return link_make_black_boxes_; +} + +void +Sta::setLinkMakeBlackBoxes(bool make) +{ + link_make_black_boxes_ = make; +} + +//////////////////////////////////////////////////////////////// + +void +Sta::setDebugLevel(const char *what, + int level) +{ + debug_->setLevel(what, level); +} + +//////////////////////////////////////////////////////////////// + +void +Sta::setAnalysisType(AnalysisType analysis_type) +{ + if (analysis_type != sdc_->analysisType()) { + sdc_->setAnalysisType(analysis_type); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); + search_->deletePathGroups(); + corners_->analysisTypeChanged(); + if (graph_) + graph_->setDelayCount(corners_->dcalcAnalysisPtCount()); + } +} + +OperatingConditions * +Sta::operatingConditions(const MinMax *min_max) const +{ + return sdc_->operatingConditions(min_max); +} + +void +Sta::setOperatingConditions(OperatingConditions *op_cond, + const MinMaxAll *min_max) +{ + sdc_->setOperatingConditions(op_cond, min_max); + corners_->operatingConditionsChanged(); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); +} + +Pvt * +Sta::pvt(Instance *inst, + const MinMax *min_max) +{ + return sdc_->pvt(inst, min_max); +} + +void +Sta::setPvt(Instance *inst, + const MinMaxAll *min_max, + float process, + float voltage, + float temperature) +{ + Pvt *pvt = new Pvt(process, voltage, temperature); + setPvt(inst, min_max, pvt); +} + +void +Sta::setPvt(Instance *inst, + const MinMaxAll *min_max, + Pvt *pvt) +{ + sdc_->setPvt(inst, min_max, pvt); + delaysInvalidFrom(inst); +} + +void +Sta::setTimingDerate(TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate) +{ + sdc_->setTimingDerate(type, clk_data, tr, early_late, derate); + // Delay calculation results are still valid. + // The search derates delays while finding arrival times. + search_->arrivalsInvalid(); +} + +void +Sta::setTimingDerate(const Net *net, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate) +{ + sdc_->setTimingDerate(net, clk_data, tr, early_late, derate); + // Delay calculation results are still valid. + // The search derates delays while finding arrival times. + search_->arrivalsInvalid(); +} + +void +Sta::setTimingDerate(const Instance *inst, + TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate) +{ + sdc_->setTimingDerate(inst, type, clk_data, tr, early_late, derate); + // Delay calculation results are still valid. + // The search derates delays while finding arrival times. + search_->arrivalsInvalid(); +} + +void +Sta::setTimingDerate(const LibertyCell *cell, + TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate) +{ + sdc_->setTimingDerate(cell, type, clk_data, tr, early_late, derate); + // Delay calculation results are still valid. + // The search derates delays while finding arrival times. + search_->arrivalsInvalid(); +} + +void +Sta::unsetTimingDerate() +{ + sdc_->unsetTimingDerate(); + // Delay calculation results are still valid. + // The search derates delays while finding arrival times. + search_->arrivalsInvalid(); +} + +void +Sta::setInputSlew(Port *port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float slew) +{ + sdc_->setInputSlew(port, tr, min_max, slew); + delaysInvalidFrom(port); +} + +void +Sta::setDriveCell(LibertyLibrary *library, + LibertyCell *cell, + Port *port, + LibertyPort *from_port, + float *from_slews, + LibertyPort *to_port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max) +{ + sdc_->setDriveCell(library, cell, port, from_port, from_slews, to_port, + tr, min_max); + delaysInvalidFrom(port); +} + +void +Sta::setDriveResistance(Port *port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float res) +{ + sdc_->setDriveResistance(port, tr, min_max, res); + delaysInvalidFrom(port); +} + +void +Sta::setLatchBorrowLimit(Pin *pin, + float limit) +{ + sdc_->setLatchBorrowLimit(pin, limit); + search_->requiredInvalid(pin); +} + +void +Sta::setLatchBorrowLimit(Instance *inst, + float limit) +{ + sdc_->setLatchBorrowLimit(inst, limit); + search_->requiredInvalid(inst); +} + +void +Sta::setLatchBorrowLimit(Clock *clk, + float limit) +{ + sdc_->setLatchBorrowLimit(clk, limit); + search_->arrivalsInvalid(); +} + +void +Sta::setMinPulseWidth(const TransRiseFallBoth *tr, + float min_width) +{ + sdc_->setMinPulseWidth(tr, min_width); +} + +void +Sta::setMinPulseWidth(const Pin *pin, + const TransRiseFallBoth *tr, + float min_width) +{ + sdc_->setMinPulseWidth(pin, tr, min_width); +} + +void +Sta::setMinPulseWidth(const Instance *inst, + const TransRiseFallBoth *tr, + float min_width) +{ + sdc_->setMinPulseWidth(inst, tr, min_width); +} + +void +Sta::setMinPulseWidth(const Clock *clk, + const TransRiseFallBoth *tr, + float min_width) +{ + sdc_->setMinPulseWidth(clk, tr, min_width); +} + +void +Sta::setWireloadMode(WireloadMode mode) +{ + sdc_->setWireloadMode(mode); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); +} + +void +Sta::setWireload(Wireload *wireload, + const MinMaxAll *min_max) +{ + sdc_->setWireload(wireload, min_max); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); +} + +void +Sta::setWireloadSelection(WireloadSelection *selection, + const MinMaxAll *min_max) +{ + sdc_->setWireloadSelection(selection, min_max); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); +} + +void +Sta::setSlewLimit(Clock *clk, + const TransRiseFallBoth *tr, + const PathClkOrData clk_data, + const MinMax *min_max, + float slew) +{ + sdc_->setSlewLimit(clk, tr, clk_data, min_max, slew); +} + +void +Sta::setSlewLimit(Port *port, + const MinMax *min_max, + float slew) +{ + sdc_->setSlewLimit(port, min_max, slew); +} + +void +Sta::setSlewLimit(Pin *pin, + const MinMax *min_max, + float slew) +{ + sdc_->setSlewLimit(pin, min_max, slew); +} + +void +Sta::setSlewLimit(Cell *cell, + const MinMax *min_max, + float slew) +{ + sdc_->setSlewLimit(cell, min_max, slew); +} + +void +Sta::setCapacitanceLimit(Cell *cell, + const MinMax *min_max, + float cap) +{ + sdc_->setCapacitanceLimit(cell, min_max, cap); +} + +void +Sta::setCapacitanceLimit(Port *port, + const MinMax *min_max, + float cap) +{ + sdc_->setCapacitanceLimit(port, min_max, cap); +} + +void +Sta::setCapacitanceLimit(Pin *pin, + const MinMax *min_max, + float cap) +{ + sdc_->setCapacitanceLimit(pin, min_max, cap); +} + +void +Sta::setFanoutLimit(Cell *cell, + const MinMax *min_max, + float fanout) +{ + sdc_->setFanoutLimit(cell, min_max, fanout); +} + +void +Sta::setFanoutLimit(Port *port, + const MinMax *min_max, + float fanout) +{ + sdc_->setFanoutLimit(port, min_max, fanout); +} + +void +Sta::setMaxArea(float area) +{ + sdc_->setMaxArea(area); +} + +void +Sta::makeClock(const char *name, + PinSet *pins, + bool add_to_pins, + float period, + FloatSeq *waveform, + char *comment) +{ + sdc_->makeClock(name, pins, add_to_pins, period, waveform, comment); + update_genclks_ = true; + search_->arrivalsInvalid(); +} + +void +Sta::makeGeneratedClock(const char *name, + PinSet *pins, + bool add_to_pins, + Pin *src_pin, + Clock *master_clk, + Pin *pll_out, + Pin *pll_fdbk, + 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, + pll_out, pll_fdbk, + divide_by, multiply_by, duty_cycle, + invert, combinational, + edges, edge_shifts, comment); + update_genclks_ = true; + search_->arrivalsInvalid(); +} + +void +Sta::removeClock(Clock *clk) +{ + sdc_->removeClock(clk); + search_->arrivalsInvalid(); +} + +Clock * +Sta::findClock(const char *name) const +{ + return sdc_->findClock(name); +} + +void +Sta::findClocksMatching(PatternMatch *pattern, + ClockSeq *clks) const +{ + sdc_->findClocksMatching(pattern, clks); +} + +ClockIterator * +Sta::clockIterator() const +{ + return new ClockIterator(sdc_); +} + +bool +Sta::isClockSrc(const Pin *pin) const +{ + return sdc_->isClock(pin); +} + +Clock * +Sta::defaultArrivalClock() const +{ + return sdc_->defaultArrivalClock(); +} + +void +Sta::setPropagatedClock(Clock *clk) +{ + sdc_->setPropagatedClock(clk); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); +} + +void +Sta::removePropagatedClock(Clock *clk) +{ + sdc_->removePropagatedClock(clk); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); +} + +void +Sta::setPropagatedClock(Pin *pin) +{ + sdc_->setPropagatedClock(pin); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); +} + +void +Sta::removePropagatedClock(Pin *pin) +{ + sdc_->removePropagatedClock(pin); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); +} + +void +Sta::setClockSlew(Clock *clk, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float slew) +{ + sdc_->setClockSlew(clk, tr, min_max, slew); + clockSlewChanged(clk); +} + +void +Sta::removeClockSlew(Clock *clk) +{ + sdc_->removeClockSlew(clk); + clockSlewChanged(clk); +} + +void +Sta::clockSlewChanged(Clock *clk) +{ + ClockPinIterator pin_iter(clk); + while (pin_iter.hasNext()) { + const Pin *pin = pin_iter.next(); + graph_delay_calc_->delayInvalid(pin); + } + search_->arrivalsInvalid(); +} + +void +Sta::setClockLatency(Clock *clk, + Pin *pin, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float delay) +{ + sdc_->setClockLatency(clk, pin, tr, min_max, delay); + search_->arrivalsInvalid(); +} + +void +Sta::removeClockLatency(const Clock *clk, + const Pin *pin) +{ + sdc_->removeClockLatency(clk, pin); + search_->arrivalsInvalid(); +} + +void +Sta::setClockInsertion(const Clock *clk, + const Pin *pin, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay) +{ + sdc_->setClockInsertion(clk, pin, tr, min_max, early_late, delay); + search_->arrivalsInvalid(); +} + +void +Sta::removeClockInsertion(const Clock *clk, + const Pin *pin) +{ + sdc_->removeClockInsertion(clk, pin); + search_->arrivalsInvalid(); +} + +void +Sta::setClockUncertainty(Clock *clk, + const SetupHoldAll *setup_hold, + float uncertainty) +{ + clk->setUncertainty(setup_hold, uncertainty); + search_->arrivalsInvalid(); +} + +void +Sta::removeClockUncertainty(Clock *clk, + const SetupHoldAll *setup_hold) +{ + clk->removeUncertainty(setup_hold); + search_->arrivalsInvalid(); +} + +void +Sta::setClockUncertainty(Pin *pin, + const SetupHoldAll *setup_hold, + float uncertainty) +{ + sdc_->setClockUncertainty(pin, setup_hold, uncertainty); + search_->arrivalsInvalid(); +} + +void +Sta::removeClockUncertainty(Pin *pin, + const SetupHoldAll *setup_hold) +{ + sdc_->removeClockUncertainty(pin, setup_hold); + search_->arrivalsInvalid(); +} + +void +Sta::setClockUncertainty(Clock *from_clk, + const TransRiseFallBoth *from_tr, + Clock *to_clk, + const TransRiseFallBoth *to_tr, + const SetupHoldAll *setup_hold, + float uncertainty) +{ + sdc_->setClockUncertainty(from_clk, from_tr, to_clk, to_tr, + setup_hold, uncertainty); + search_->arrivalsInvalid(); +} + +void +Sta::removeClockUncertainty(Clock *from_clk, + const TransRiseFallBoth *from_tr, + Clock *to_clk, + const TransRiseFallBoth *to_tr, + const SetupHoldAll *setup_hold) +{ + sdc_->removeClockUncertainty(from_clk, from_tr, to_clk, to_tr, + 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); + search_->requiredsInvalid(); + return groups; +} + +void +Sta::removeClockGroupsLogicallyExclusive(const char *name) +{ + sdc_->removeClockGroupsLogicallyExclusive(name); + search_->requiredsInvalid(); +} + +void +Sta::removeClockGroupsPhysicallyExclusive(const char *name) +{ + sdc_->removeClockGroupsPhysicallyExclusive(name); + search_->requiredsInvalid(); +} + +void +Sta::removeClockGroupsAsynchronous(const char *name) +{ + sdc_->removeClockGroupsAsynchronous(name); + search_->requiredsInvalid(); +} + +void +Sta::makeClockGroup(ClockGroups *clk_groups, + ClockSet *clks) +{ + sdc_->makeClockGroup(clk_groups, clks); +} + +void +Sta::setClockSense(PinSet *pins, + ClockSet *clks, + ClockSense sense) +{ + sdc_->setClockSense(pins, clks, sense); + search_->arrivalsInvalid(); +} + +//////////////////////////////////////////////////////////////// + +void +Sta::setClockGatingCheck(const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin) +{ + sdc_->setClockGatingCheck(tr, setup_hold, margin); + search_->arrivalsInvalid(); +} + +void +Sta::setClockGatingCheck(Clock *clk, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin) +{ + sdc_->setClockGatingCheck(clk, tr, setup_hold, margin); + search_->arrivalsInvalid(); +} + +void +Sta::setClockGatingCheck(Instance *inst, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) +{ + sdc_->setClockGatingCheck(inst, tr, setup_hold, margin,active_value); + search_->arrivalsInvalid(); +} + +void +Sta::setClockGatingCheck(Pin *pin, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) +{ + sdc_->setClockGatingCheck(pin, tr, setup_hold, margin,active_value); + search_->arrivalsInvalid(); +} + +void +Sta::setDataCheck(Pin *from, + const TransRiseFallBoth *from_tr, + Pin *to, + const TransRiseFallBoth *to_tr, + Clock *clk, + const SetupHoldAll *setup_hold, + float margin) +{ + sdc_->setDataCheck(from, from_tr, to, to_tr, clk, setup_hold,margin); + search_->requiredInvalid(to); +} + +void +Sta::removeDataCheck(Pin *from, + const TransRiseFallBoth *from_tr, + Pin *to, + const TransRiseFallBoth *to_tr, + Clock *clk, + const SetupHoldAll *setup_hold) +{ + sdc_->removeDataCheck(from, from_tr, to, to_tr, clk, setup_hold); + search_->requiredInvalid(to); +} + +//////////////////////////////////////////////////////////////// + +void +Sta::disable(Pin *pin) +{ + sdc_->disable(pin); + // Levelization respects disabled edges. + levelize_->invalid(); + graph_delay_calc_->delayInvalid(pin); + search_->arrivalsInvalid(); +} + +void +Sta::removeDisable(Pin *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) +{ + sdc_->disable(inst, from, to); + + if (from) { + Pin *from_pin = network_->findPin(inst, from); + graph_delay_calc_->delayInvalid(from_pin); + } + if (to) { + Pin *to_pin = network_->findPin(inst, to); + graph_delay_calc_->delayInvalid(to_pin); + } + if (from == nullptr && to == nullptr) { + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + graph_delay_calc_->delayInvalid(pin); + } + delete pin_iter; + } + // Levelization respects disabled edges. + levelize_->invalid(); + search_->arrivalsInvalid(); +} + +void +Sta::removeDisable(Instance *inst, + LibertyPort *from, + LibertyPort *to) +{ + sdc_->removeDisable(inst, from, to); + + if (from) { + Pin *from_pin = network_->findPin(inst, from); + graph_delay_calc_->delayInvalid(from_pin); + } + if (to) { + Pin *to_pin = network_->findPin(inst, to); + graph_delay_calc_->delayInvalid(to_pin); + } + if (from == nullptr && to == nullptr) { + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + graph_delay_calc_->delayInvalid(pin); + } + delete pin_iter; + } + // Levelization respects disabled edges. + levelize_->invalid(); + search_->arrivalsInvalid(); +} + +void +Sta::disable(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to) +{ + sdc_->disable(cell, from, to); + disableAfter(); +} + +void +Sta::removeDisable(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to) +{ + sdc_->removeDisable(cell, from, to); + disableAfter(); +} + +void +Sta::disable(LibertyPort *port) +{ + sdc_->disable(port); + disableAfter(); +} + +void +Sta::removeDisable(LibertyPort *port) +{ + sdc_->removeDisable(port); + disableAfter(); +} + +void +Sta::disable(Port *port) +{ + sdc_->disable(port); + disableAfter(); +} + +void +Sta::removeDisable(Port *port) +{ + sdc_->removeDisable(port); + disableAfter(); +} + +void +Sta::disable(Edge *edge) +{ + sdc_->disable(edge); + disableAfter(); +} + +void +Sta::removeDisable(Edge *edge) +{ + sdc_->removeDisable(edge); + disableAfter(); +} + +void +Sta::disable(TimingArcSet *arc_set) +{ + sdc_->disable(arc_set); + disableAfter(); +} + +void +Sta::removeDisable(TimingArcSet *arc_set) +{ + sdc_->removeDisable(arc_set); + disableAfter(); +} + +void +Sta::disableAfter() +{ + // Levelization respects disabled edges. + levelize_->invalid(); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); +} + +//////////////////////////////////////////////////////////////// + +EdgeSeq * +Sta::disabledEdges() +{ + ensureLevelized(); + EdgeSeq *disabled_edges = new EdgeSeq; + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + 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); + } + } + return disabled_edges; +} + + +EdgeSeq * +Sta::disabledEdgesSorted() +{ + EdgeSeq *disabled_edges = disabledEdges(); + sortEdges(disabled_edges, network_, graph_); + return disabled_edges; +} + +bool +Sta::isDisabledConstraint(Edge *edge) +{ + 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); +} + +bool +Sta::isDisabledConstant(Edge *edge) +{ + 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_) + || isTestDisabled(inst, from_pin, to_pin, network_, sim_) + || hasDisabledArcs(edge, graph_))); +} + +static bool +hasDisabledArcs(Edge *edge, + Graph *graph) +{ + TimingArcSet *arc_set = edge->timingArcSet(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *arc = arc_iter.next(); + if (!searchThru(edge, arc, graph)) + return true; + } + return false; +} + +bool +Sta::isDisabledLoop(Edge *edge) const +{ + return levelize_->isDisabledLoop(edge); +} + +bool +Sta::isDisabledCondDefault(Edge *edge) +{ + return sdc_->isDisabledCondDefault(edge); +} + +PinSet * +Sta::disabledConstantPins(Edge *edge) +{ + sim_->ensureConstantsPropagated(); + PinSet *pins = new PinSet; + Vertex *from_vertex = edge->from(graph_); + Pin *from_pin = from_vertex->pin(); + Vertex *to_vertex = edge->to(graph_); + Pin *to_pin = to_vertex->pin(); + if (sim_->logicZeroOne(from_vertex)) + pins->insert(from_pin); + if (edge->role()->isWire()) { + if (sim_->logicZeroOne(to_vertex)) + pins->insert(to_pin); + } + else { + 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); + if (is_disabled) + exprConstantPins(disable_cond, inst, pins); + isModeDisabled(edge, inst, network_, sim_, + is_disabled, disable_cond); + if (is_disabled) + exprConstantPins(disable_cond, inst, pins); + Pin *scan_enable; + isTestDisabled(inst, from_pin, to_pin, network_, sim_, + is_disabled, scan_enable); + if (is_disabled) + pins->insert(scan_enable); + if (hasDisabledArcs(edge, graph_)) { + 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); + } + } + } + return pins; +} + +TimingSense +Sta::simTimingSense(Edge *edge) +{ + 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); +} + +void +Sta::exprConstantPins(FuncExpr *expr, Instance *inst, PinSet *pins) +{ + FuncExprPortIterator port_iter(expr); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + Pin *pin = network_->findPin(inst, port); + if (pin) { + LogicValue value = sim_->logicValue(pin); + if (value != LogicValue::unknown) + pins->insert(pin); + } + } +} + +bool +Sta::isDisabledBidirectInstPath(Edge *edge) const +{ + return !sdc_->bidirectInstPathsEnabled() + && edge->isBidirectInstPath(); +} + +bool +Sta::isDisabledBidirectNetPath(Edge *edge) const +{ + return !sdc_->bidirectNetPathsEnabled() + && edge->isBidirectNetPath(); +} + +bool +Sta::isDisabledPresetClr(Edge *edge) const +{ + return !sdc_->presetClrArcsEnabled() + && edge->role() == TimingRole::regSetClr(); +} + +void +Sta::disableClockGatingCheck(Instance *inst) +{ + sdc_->disableClockGatingCheck(inst); + search_->endpointsInvalid(); +} + +void +Sta::disableClockGatingCheck(Pin *pin) +{ + sdc_->disableClockGatingCheck(pin); + search_->endpointsInvalid(); +} + +void +Sta::removeDisableClockGatingCheck(Instance *inst) +{ + sdc_->removeDisableClockGatingCheck(inst); + search_->endpointsInvalid(); +} + +void +Sta::removeDisableClockGatingCheck(Pin *pin) +{ + sdc_->removeDisableClockGatingCheck(pin); + search_->endpointsInvalid(); +} + +void +Sta::setLogicValue(Pin *pin, + LogicValue value) +{ + sdc_->setLogicValue(pin, value); + // Levelization respects constant disabled edges. + levelize_->invalid(); + 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 more incremental if the graph delay + // calculator searched thru disabled edges but ignored their + // results. + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); +} + +void +Sta::setCaseAnalysis(Pin *pin, + LogicValue value) +{ + sdc_->setCaseAnalysis(pin, value); + // Levelization respects constant disabled edges. + levelize_->invalid(); + 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. + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); +} + +void +Sta::removeCaseAnalysis(Pin *pin) +{ + sdc_->removeCaseAnalysis(pin); + // Levelization respects constant disabled edges. + levelize_->invalid(); + 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. + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); +} + +void +Sta::setInputDelay(Pin *pin, + const TransRiseFallBoth *tr, + Clock *clk, + const TransRiseFall *clk_tr, + Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, + float delay) +{ + sdc_->setInputDelay(pin, tr, clk, clk_tr, ref_pin, + source_latency_included, network_latency_included, + min_max, add, delay); + + search_->arrivalInvalid(pin); +} + +void +Sta::removeInputDelay(Pin *pin, + TransRiseFallBoth *tr, + Clock *clk, + TransRiseFall *clk_tr, + MinMaxAll *min_max) +{ + sdc_->removeInputDelay(pin, tr, clk, clk_tr, min_max); + search_->arrivalInvalid(pin); +} + +void +Sta::setOutputDelay(Pin *pin, + const TransRiseFallBoth *tr, + Clock *clk, + const TransRiseFall *clk_tr, + Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, + float delay) +{ + sdc_->setOutputDelay(pin, tr, clk, clk_tr, ref_pin, + source_latency_included,network_latency_included, + min_max, add, delay); + search_->requiredInvalid(pin); +} + +void +Sta::removeOutputDelay(Pin *pin, + TransRiseFallBoth *tr, + Clock *clk, + TransRiseFall *clk_tr, + MinMaxAll *min_max) +{ + sdc_->removeOutputDelay(pin, tr, clk, clk_tr, min_max); + search_->arrivalInvalid(pin); +} + +void +Sta::makeFalsePath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + const char *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); + search_->arrivalsInvalid(); +} + +void +Sta::makePathDelay(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, + float delay, + const char *comment) +{ + sdc_->makePathDelay(from, thrus, to, min_max, + ignore_clk_latency, delay, + comment); + search_->endpointsInvalid(); + search_->arrivalsInvalid(); +} + +void +Sta::resetPath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *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) +{ + sdc_->makeGroupPath(name, is_default, from, thrus, to, + comment); + search_->arrivalsInvalid(); +} + +bool +Sta::isGroupPathName(const char *group_name) +{ + return PathGroups::isGroupPathName(group_name) + || sdc_->findClock(group_name) + || sdc_->isGroupPathName(group_name); +} + +ExceptionFrom * +Sta::makeExceptionFrom(PinSet *from_pins, + ClockSet *from_clks, + InstanceSet *from_insts, + const TransRiseFallBoth *from_tr) +{ + return sdc_->makeExceptionFrom(from_pins, from_clks, from_insts, + from_tr); +} + +void +Sta::checkExceptionFromPins(ExceptionFrom *from, + const char *file, + int line) const +{ + if (from) { + PinSet::ConstIterator pin_iter(from->pins()); + while (pin_iter.hasNext()) { + const Pin *pin = pin_iter.next(); + if (exceptionFromInvalid(pin)) { + if (line) + report_->fileWarn(file, line, "'%s' is not a valid startpoint.\n", + cmd_network_->pathName(pin)); + else + report_->warn("'%s' is not a valid startoint.\n", + cmd_network_->pathName(pin)); + } + } + } +} + +bool +Sta::exceptionFromInvalid(const Pin *pin) const +{ + Net *net = network_->net(pin); + // Floating pins are invalid. + return (net == nullptr + && !network_->isTopLevelPort(pin)) + || (net + // Pins connected to power/ground are invalid. + && (network_->isPower(net) + || network_->isGround(net))) + || !((network_->isTopLevelPort(pin) + && network_->direction(pin)->isAnyInput()) + || network_->isRegClkPin(pin) + || network_->isLatchData(pin)); +} + +void +Sta::deleteExceptionFrom(ExceptionFrom *from) +{ + delete from; +} + +ExceptionThru * +Sta::makeExceptionThru(PinSet *pins, + NetSet *nets, + InstanceSet *insts, + const TransRiseFallBoth *tr) +{ + return sdc_->makeExceptionThru(pins, nets, insts, tr); +} + +void +Sta::deleteExceptionThru(ExceptionThru *thru) +{ + delete thru; +} + +ExceptionTo * +Sta::makeExceptionTo(PinSet *to_pins, + ClockSet *to_clks, + InstanceSet *to_insts, + const TransRiseFallBoth *tr, + TransRiseFallBoth *end_tr) +{ + return sdc_->makeExceptionTo(to_pins, to_clks, to_insts, tr, end_tr); +} + +void +Sta::deleteExceptionTo(ExceptionTo *to) +{ + delete to; +} + +void +Sta::checkExceptionToPins(ExceptionTo *to, + const char *file, + int line) const +{ + if (to) { + PinSet::Iterator pin_iter(to->pins()); + while (pin_iter.hasNext()) { + const Pin *pin = pin_iter.next(); + if (sdc_->exceptionToInvalid(pin)) { + if (line) + report_->fileWarn(file, line, "'%s' is not a valid endpoint.\n", + cmd_network_->pathName(pin)); + else + report_->warn("'%s' is not a valid endpoint.\n", + cmd_network_->pathName(pin)); + } + } + } +} + +void +Sta::removeConstraints() +{ + levelize_->invalid(); + graph_delay_calc_->clear(); + search_->clear(); + sim_->constantsInvalid(); + if (graph_) + // Remove graph constraint annotations. + sdc_->annotateGraph(false); + sdc_->clear(); +} + +void +Sta::constraintsChanged() +{ + levelize_->invalid(); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); + sim_->constantsInvalid(); +} + +void +Sta::writeSdc(const char *filename, + bool compatible, + bool no_timestamp, + int digits) +{ + sta::writeSdc(network_->topInstance(), filename, "write_sdc", + compatible, no_timestamp, digits, sdc_); +} + +//////////////////////////////////////////////////////////////// + +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. + 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); +} + +bool +Sta::crprEnabled() const +{ + return sdc_->crprEnabled(); +} + +void +Sta::setCrprEnabled(bool enabled) +{ + // Pessimism is only relevant for on_chip_variation analysis. + if (sdc_->analysisType() == AnalysisType::ocv + && enabled != sdc_->crprEnabled()) + search_->arrivalsInvalid(); + sdc_->setCrprEnabled(enabled); +} + +CrprMode +Sta::crprMode() const +{ + return sdc_->crprMode(); +} + +void +Sta::setCrprMode(CrprMode mode) +{ + // Pessimism is only relevant for on_chip_variation analysis. + if (sdc_->analysisType() == AnalysisType::ocv + && sdc_->crprEnabled() + && sdc_->crprMode() != mode) + search_->arrivalsInvalid(); + sdc_->setCrprMode(mode); +} + +bool +Sta::pocvEnabled() const +{ + return pocv_enabled_; +} + +void +Sta::setPocvEnabled(bool enabled) +{ + if (enabled != pocv_enabled_) { + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); + } + pocv_enabled_ = enabled; + updateComponentsState(); +} + +void +Sta::setSigmaFactor(float factor) +{ + if (!fuzzyEqual(factor, sigma_factor_)) { + sigma_factor_ = factor; + search_->arrivalsInvalid(); + updateComponentsState(); + } +} + +bool +Sta::propagateGatedClockEnable() const +{ + return sdc_->propagateGatedClockEnable(); +} + +void +Sta::setPropagateGatedClockEnable(bool enable) +{ + if (sdc_->propagateGatedClockEnable() != enable) + search_->arrivalsInvalid(); + sdc_->setPropagateGatedClockEnable(enable); +} + +bool +Sta::presetClrArcsEnabled() const +{ + return sdc_->presetClrArcsEnabled(); +} + +void +Sta::setPresetClrArcsEnabled(bool enable) +{ + if (sdc_->presetClrArcsEnabled() != enable) { + levelize_->invalid(); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); + } + sdc_->setPresetClrArcsEnabled(enable); +} + +bool +Sta::condDefaultArcsEnabled() const +{ + return sdc_->condDefaultArcsEnabled(); +} + +void +Sta::setCondDefaultArcsEnabled(bool enabled) +{ + if (sdc_->condDefaultArcsEnabled() != enabled) { + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); + sdc_->setCondDefaultArcsEnabled(enabled); + } +} + +bool +Sta::bidirectInstPathsEnabled() const +{ + return sdc_->bidirectInstPathsEnabled(); +} + +void +Sta::setBidirectInstPathsEnabled(bool enabled) +{ + if (sdc_->bidirectInstPathsEnabled() != enabled) { + levelize_->invalid(); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); + sdc_->setBidirectInstPathsEnabled(enabled); + } +} + +bool +Sta::bidirectNetPathsEnabled() const +{ + return sdc_->bidirectNetPathsEnabled(); +} + +void +Sta::setBidirectNetPathsEnabled(bool enabled) +{ + if (sdc_->bidirectNetPathsEnabled() != enabled) { + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); + sdc_->setBidirectNetPathsEnabled(enabled); + } +} + +bool +Sta::recoveryRemovalChecksEnabled() const +{ + return sdc_->recoveryRemovalChecksEnabled(); +} + +void +Sta::setRecoveryRemovalChecksEnabled(bool enabled) +{ + if (sdc_->recoveryRemovalChecksEnabled() != enabled) { + search_->arrivalsInvalid(); + sdc_->setRecoveryRemovalChecksEnabled(enabled); + } +} + +bool +Sta::gatedClkChecksEnabled() const +{ + return sdc_->gatedClkChecksEnabled(); +} + +void +Sta::setGatedClkChecksEnabled(bool enabled) +{ + if (sdc_->gatedClkChecksEnabled() != enabled) { + search_->arrivalsInvalid(); + sdc_->setGatedClkChecksEnabled(enabled); + } +} + +bool +Sta::dynamicLoopBreaking() const +{ + return sdc_->dynamicLoopBreaking(); +} + +void +Sta::setDynamicLoopBreaking(bool enable) +{ + if (sdc_->dynamicLoopBreaking() != enable) { + sdc_->setDynamicLoopBreaking(enable); + search_->arrivalsInvalid(); + } +} + +bool +Sta::useDefaultArrivalClock() const +{ + return sdc_->useDefaultArrivalClock(); +} + +void +Sta::setUseDefaultArrivalClock(bool enable) +{ + if (sdc_->useDefaultArrivalClock() != enable) { + sdc_->setUseDefaultArrivalClock(enable); + search_->arrivalsInvalid(); + } +} + +bool +Sta::propagateAllClocks() const +{ + return sdc_->propagateAllClocks(); +} + +void +Sta::setPropagateAllClocks(bool prop) +{ + sdc_->setPropagateAllClocks(prop); +} + +bool +Sta::clkThruTristateEnabled() const +{ + return sdc_->clkThruTristateEnabled(); +} + +void +Sta::setClkThruTristateEnabled(bool enable) +{ + if (enable != sdc_->clkThruTristateEnabled()) { + search_->arrivalsInvalid(); + sdc_->setClkThruTristateEnabled(enable); + } +} + +//////////////////////////////////////////////////////////////// + +Corner * +Sta::findCorner(const char *corner_name) +{ + return corners_->findCorner(corner_name); +} + +bool +Sta::multiCorner() +{ + return corners_->multiCorner(); +} + +// Init one corner named "default". +void +Sta::makeCorners() +{ + corners_ = new Corners(this); + StringSet corner_names; + corner_names.insert("default"); + makeCorners(&corner_names); +} + +void +Sta::makeCorners(StringSet *corner_names) +{ + corners_->makeCorners(corner_names); + cmd_corner_ = corners_->findCorner(0); +} + +Corner * +Sta::cmdCorner() const +{ + return cmd_corner_; +} + +void +Sta::setCmdCorner(Corner *corner) +{ + cmd_corner_ = corner; +} + +void +Sta::setPathMinMax(const MinMaxAll *) +{ +} + +//////////////////////////////////////////////////////////////// + +// from/thrus/to are owned and deleted by Search. +// Returned sequence is owned by the caller. +// 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_count, + int endpoint_count, + bool unique_pins, + 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) +{ + searchPreamble(); + return search_->findPathEnds(from, thrus, to, unconstrained, + corner, min_max, group_count, endpoint_count, + unique_pins, 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 +// find arrivals + +void +Sta::searchPreamble() +{ + findDelays(); + updateGeneratedClks(); + sdc_->searchPreamble(); + search_->deleteFilteredArrivals(); +} + +void +Sta::setReportPathFormat(ReportPathFormat format) +{ + report_path_->setPathFormat(format); +} + +void +Sta::setReportPathFieldOrder(StringSeq *field_names) +{ + report_path_->setReportFieldOrder(field_names); +} + +void +Sta::setReportPathFields(bool report_input_pin, + bool report_net, + bool report_cap, + bool report_slew) +{ + report_path_->setReportFields(report_input_pin, report_net, report_cap, + report_slew); +} + +ReportField * +Sta::findReportPathField(const char *name) +{ + return report_path_->findField(name); +} + +void +Sta::setReportPathDigits(int digits) +{ + report_path_->setDigits(digits); +} + +void +Sta::setReportPathNoSplit(bool no_split) +{ + report_path_->setNoSplit(no_split); +} + +void +Sta::reportPathEnds(PathEndSeq *ends) +{ + report_path_->reportPathEnds(ends); +} + +void +Sta::reportPathEndHeader() +{ + report_path_->reportPathEndHeader(); +} + +void +Sta::reportPathEndFooter() +{ + report_path_->reportPathEndFooter(); +} + +void +Sta::reportPathEnd(PathEnd *end) +{ + report_path_->reportPathEnd(end); +} + +void +Sta::reportPathEnd(PathEnd *end, + PathEnd *prev_end) +{ + report_path_->reportPathEnd(end, prev_end); +} + +void +Sta::reportPath(Path *path) +{ + report_path_->reportPath(path); +} + +void +Sta::updateTiming(bool full) +{ + searchPreamble(); + if (full) + search_->arrivalsInvalid(); + search_->findAllArrivals(); +} + +void +Sta::reportClkSkew(ClockSet *clks, + const Corner *corner, + const SetupHold *setup_hold, + int digits) +{ + ensureClkArrivals(); + if (clk_skews_ == nullptr) + clk_skews_ = new ClkSkews(this); + clk_skews_->reportClkSkew(clks, corner, setup_hold, digits); +} + +//////////////////////////////////////////////////////////////// + +void +Sta::arrivalsInvalid() +{ + search_->arrivalsInvalid(); +} + +void +Sta::ensureClkArrivals() +{ + searchPreamble(); + search_->findClkArrivals(); +} + +//////////////////////////////////////////////////////////////// + +void +Sta::visitStartpoints(VertexVisitor *visitor) +{ + ensureGraph(); + search_->visitStartpoints(visitor); +} + +void +Sta::visitEndpoints(VertexVisitor *visitor) +{ + ensureGraph(); + search_->visitEndpoints(visitor); +} + +PinSet * +Sta::findGroupPathPins(const char *group_path_name) +{ + if (!search_->havePathGroups()) { + PathEndSeq *path_ends = findPathEnds(// from, thrus, to, unconstrained + nullptr, nullptr, nullptr, false, + // corner, min_max, + nullptr, MinMaxAll::max(), + // group_count, endpoint_count, unique_pins + 1, 1, false, + -INF, INF, // slack_min, slack_max, + false, // sort_by_slack + nullptr, // group_names + // setup, hold, recovery, removal, + true, true, true, true, + // clk_gating_setup, clk_gating_hold + true, true); + // No use for the path end sequence. + delete path_ends; + } + + PathGroup *path_group = search_->findPathGroup(group_path_name, + MinMax::max()); + PinSet *pins = new PinSet; + VertexPinCollector visitor(pins); + visitPathGroupVertices(path_group, &visitor, this); + return pins; +} + +//////////////////////////////////////////////////////////////// + +void +Sta::findRequireds() +{ + searchPreamble(); + search_->findAllArrivals(); + search_->findRequireds(); +} + +//////////////////////////////////////////////////////////////// + +VertexPathIterator * +Sta::vertexPathIterator(Vertex *vertex, + const TransRiseFall *tr, + const PathAnalysisPt *path_ap) +{ + return new VertexPathIterator(vertex, tr, path_ap, this); +} + +VertexPathIterator * +Sta::vertexPathIterator(Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max) +{ + return new VertexPathIterator(vertex, tr, min_max, this); +} + +void +Sta::vertexWorstArrivalPath(Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max, + // Return value. + PathRef &worst_path) +{ + Arrival worst_arrival = min_max->initValue(); + VertexPathIterator path_iter(vertex, tr, min_max, this); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + Arrival arrival = path->arrival(this); + if (!path->tag(this)->isGenClkSrcPath() + && fuzzyGreater(arrival, worst_arrival, min_max)) { + worst_arrival = arrival; + worst_path.init(path); + } + } +} + +void +Sta::vertexWorstArrivalPath(Vertex *vertex, + const MinMax *min_max, + // Return value. + PathRef &worst_path) +{ + Arrival worst_arrival = min_max->initValue(); + VertexPathIterator path_iter(vertex, this); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + Arrival arrival = path->arrival(this); + if (path->minMax(this) == min_max + && !path->tag(this)->isGenClkSrcPath() + && fuzzyGreater(arrival, worst_arrival, min_max)) { + worst_arrival = arrival; + worst_path.init(path); + } + } +} + +void +Sta::vertexWorstSlackPath(Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max, + // Return value. + PathRef &worst_path) +{ + Slack min_slack = MinMax::min()->initValue(); + VertexPathIterator path_iter(vertex, tr, min_max, this); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + Slack slack = path->slack(this); + if (!path->tag(this)->isGenClkSrcPath() + && slack < min_slack) { + min_slack = slack; + worst_path.init(path); + } + } +} + +void +Sta::vertexWorstSlackPath(Vertex *vertex, + const MinMax *min_max, + // Return value. + PathRef &worst_path) + +{ + Slack min_slack = MinMax::min()->initValue(); + VertexPathIterator path_iter(vertex, this); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + if (path->minMax(this) == min_max + && !path->tag(this)->isGenClkSrcPath()) { + Slack slack = path->slack(this); + if (fuzzyLess(slack, min_slack)) { + min_slack = slack; + worst_path.init(path); + } + } + } +} + +Arrival +Sta::vertexArrival(Vertex *vertex, + const TransRiseFall *tr, + const PathAnalysisPt *path_ap) +{ + return vertexArrival(vertex, tr, clk_edge_wildcard, path_ap); +} + +Arrival +Sta::vertexArrival(Vertex *vertex, + const TransRiseFall *tr, + const ClockEdge *clk_edge, + const PathAnalysisPt *path_ap) +{ + searchPreamble(); + search_->findArrivals(vertex->level()); + const MinMax *min_max = path_ap->pathMinMax(); + Arrival arrival = min_max->initValue(); + VertexPathIterator path_iter(vertex, tr, path_ap, this); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + const Arrival &path_arrival = path->arrival(this); + ClkInfo *clk_info = path->clkInfo(search_); + if ((clk_edge == clk_edge_wildcard + || clk_info->clkEdge() == clk_edge) + && !clk_info->isGenClkSrcPath() + && fuzzyGreater(path->arrival(this), arrival, min_max)) + arrival = path_arrival; + } + return arrival; +} + +Required +Sta::vertexRequired(Vertex *vertex, + const TransRiseFall *tr, + const PathAnalysisPt *path_ap) +{ + return vertexRequired(vertex, tr, clk_edge_wildcard, path_ap); +} + +Required +Sta::vertexRequired(Vertex *vertex, + const TransRiseFall *tr, + const ClockEdge *clk_edge, + const PathAnalysisPt *path_ap) +{ + findRequired(vertex); + const MinMax *min_max = path_ap->pathMinMax()->opposite(); + Required required = min_max->initValue(); + VertexPathIterator path_iter(vertex, tr, path_ap, this); + while (path_iter.hasNext()) { + const Path *path = path_iter.next(); + const Required path_required = path->required(this); + if ((clk_edge == clk_edge_wildcard + || path->clkEdge(search_) == clk_edge) + && fuzzyGreater(path_required, required, min_max)) + required = path_required; + } + return required; +} + +Slack +Sta::pinSlack(const Pin *pin, + 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, min_max); + if (bidirect_drvr_vertex) + slack = min(slack, vertexSlack(bidirect_drvr_vertex, min_max)); + return slack; +} + +Slack +Sta::pinSlack(const Pin *pin, + const TransRiseFall *tr, + 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, tr, min_max); + if (bidirect_drvr_vertex) + slack = min(slack, vertexSlack(bidirect_drvr_vertex, tr, min_max)); + return slack; +} + +Slack +Sta::vertexSlack(Vertex *vertex, + const MinMax *min_max) +{ + findRequired(vertex); + MinMax *min = MinMax::min(); + Slack slack = 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 (path_slack < slack) + slack = path_slack; + } + } + return slack; +} + +Slack +Sta::vertexSlack(Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max) +{ + findRequired(vertex); + Slack slack = MinMax::min()->initValue(); + VertexPathIterator path_iter(vertex, tr, min_max, this); + while (path_iter.hasNext()) { + Path *path = path_iter.next(); + Slack path_slack = path->slack(this); + if (path_slack < slack) + slack = path_slack; + } + return slack; +} + +Slack +Sta::vertexSlack(Vertex *vertex, + const TransRiseFall *tr, + const PathAnalysisPt *path_ap) +{ + findRequired(vertex); + return vertexSlack1(vertex, tr, clk_edge_wildcard, path_ap); +} + +Slack +Sta::vertexSlack(Vertex *vertex, + const TransRiseFall *tr, + const ClockEdge *clk_edge, + const PathAnalysisPt *path_ap) +{ + findRequired(vertex); + return vertexSlack1(vertex, tr, clk_edge, path_ap); +} + +Slack +Sta::vertexSlack1(Vertex *vertex, + const TransRiseFall *tr, + const ClockEdge *clk_edge, + const PathAnalysisPt *path_ap) +{ + MinMax *min = MinMax::min(); + Slack slack = min->initValue(); + VertexPathIterator path_iter(vertex, tr, 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) + && path_slack < slack) + slack = path_slack; + } + return slack; +} + +void +Sta::findRequired(Vertex *vertex) +{ + searchPreamble(); + search_->findAllArrivals(); + search_->findRequireds(vertex->level()); + if (sdc_->crprEnabled() + && search_->crprPathPruningEnabled() + && !search_->crprApproxMissingRequireds() + // Clocks invariably have requireds that are pruned but isn't + // worth finding arrivals and requireds all over again for + // the entire fanout of the clock. + && !search_->isClock(vertex) + && vertex->requiredsPruned()) { + // Invalidate arrivals and requireds and disable + // path pruning on fanout vertices with DFS. + int fanout = 0; + disableFanoutCrprPruning(vertex, fanout); + debugPrint2(debug_, "search", 1, "resurrect pruned required %s fanout %d\n", + vertex->name(sdc_network_), + fanout); + // Find fanout arrivals and requireds with pruning disabled. + search_->findArrivals(); + search_->findRequireds(vertex->level()); + } +} + +void +Sta::disableFanoutCrprPruning(Vertex *vertex, + int &fanout) +{ + if (!vertex->crprPathPruningDisabled()) { + search_->arrivalInvalid(vertex); + search_->requiredInvalid(vertex); + vertex->setCrprPathPruningDisabled(true); + fanout++; + SearchPred *pred = search_->searchAdj(); + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (pred->searchThru(edge) + && pred->searchTo(to_vertex)) + disableFanoutCrprPruning(to_vertex, fanout); + } + } +} + +Slack +Sta::totalNegativeSlack(const MinMax *min_max) +{ + searchPreamble(); + return search_->totalNegativeSlack(min_max); +} + +Slack +Sta::totalNegativeSlack(const Corner *corner, + const MinMax *min_max) +{ + searchPreamble(); + return search_->totalNegativeSlack(corner, min_max); +} + +void +Sta::worstSlack(const MinMax *min_max, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) +{ + searchPreamble(); + return 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) +{ + searchPreamble(); + return search_->worstSlack(corner, min_max, worst_slack, worst_vertex); +} + +//////////////////////////////////////////////////////////////// + +string * +Sta::reportDelayCalc(Edge *edge, + TimingArc *arc, + const Corner *corner, + const MinMax *min_max, + int digits) +{ + findDelays(); + return graph_delay_calc_->reportDelayCalc(edge, arc, corner, min_max, digits); +} + +void +Sta::setArcDelayCalc(const char *delay_calc_name) +{ + delete arc_delay_calc_; + arc_delay_calc_ = makeDelayCalc(delay_calc_name, sta_); + // Update pointers to arc_delay_calc. + updateComponentsState(); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); +} + +void +Sta::findDelays(Vertex *to_vertex) +{ + delayCalcPreamble(); + graph_delay_calc_->findDelays(to_vertex->level()); +} + +void +Sta::findDelays() +{ + delayCalcPreamble(); + graph_delay_calc_->findDelays(levelize_->maxLevel()); +} + +void +Sta::findDelays(Level level) +{ + delayCalcPreamble(); + graph_delay_calc_->findDelays(level); +} + +void +Sta::delayCalcPreamble() +{ + ensureLevelized(); +} + +void +Sta::setIncrementalDelayTolerance(float tol) +{ + graph_delay_calc_->setIncrementalDelayTolerance(tol); +} + +ArcDelay +Sta::arcDelay(Edge *edge, + TimingArc *arc, + const DcalcAnalysisPt *dcalc_ap) +{ + findDelays(edge->to(graph_)); + return graph_->arcDelay(edge, arc, dcalc_ap->index()); +} + +bool +Sta::arcDelayAnnotated(Edge *edge, + TimingArc *arc, + DcalcAnalysisPt *dcalc_ap) +{ + return graph_->arcDelayAnnotated(edge, arc, dcalc_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 TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap) +{ + findDelays(vertex); + return graph_->slew(vertex, tr, dcalc_ap->index()); +} + +Slew +Sta::vertexSlew(Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max) +{ + findDelays(vertex); + Slew mm_slew = min_max->initValue(); + for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { + Slew slew = graph_->slew(vertex, tr, dcalc_ap->index()); + if (fuzzyGreater(slew, mm_slew, min_max)) + mm_slew = slew; + } + return mm_slew; +} + +//////////////////////////////////////////////////////////////// + +Graph * +Sta::ensureGraph() +{ + if (graph_ == nullptr && network_) { + makeGraph(); + // Update pointers to graph. + updateComponentsState(); + sdc_->annotateGraph(true); + } + return graph_; +} + +void +Sta::makeGraph() +{ + graph_ = new Graph(this, 2, true, corners_->dcalcAnalysisPtCount()); + graph_->makeGraph(); +} + +void +Sta::ensureLevelized() +{ + ensureGraph(); + // Need constant propagation before levelization to know edges that + // are disabled by constants. + sim_->ensureConstantsPropagated(); + levelize_->ensureLevelized(); +} + +void +Sta::updateGeneratedClks() +{ + if (update_genclks_) { + ensureLevelized(); + bool gen_clk_changed = true; + while (gen_clk_changed) { + gen_clk_changed = false; + for (auto 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; + } + } + } + } + } + update_genclks_ = false; +} + +Level +Sta::vertexLevel(Vertex *vertex) +{ + ensureLevelized(); + return vertex->level(); +} + +GraphLoopSeq * +Sta::graphLoops() +{ + ensureLevelized(); + return levelize_->loops(); +} + +PathAnalysisPt * +Sta::pathAnalysisPt(Path *path) +{ + return path->pathAnalysisPt(this); +} + +DcalcAnalysisPt * +Sta::pathDcalcAnalysisPt(Path *path) +{ + return pathAnalysisPt(path)->dcalcAnalysisPt(); +} + +Vertex * +Sta::maxArrivalCountVertex() const +{ + Vertex *max_vertex = nullptr; + VertexIndex max_count = 0; + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + VertexIndex count = vertexArrivalCount(vertex); + if (count > max_count) { + max_count = count; + max_vertex = vertex; + } + } + return max_vertex; +} + +int +Sta::vertexArrivalCount(Vertex *vertex) const +{ + auto tag_group = search_->tagGroup(vertex); + if (tag_group) + return tag_group->arrivalCount(); + else + return 0; +} + +int +Sta::arrivalCount() const +{ + int count = 0; + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + count += vertexArrivalCount(vertex); + } + return count; +} + +TagIndex +Sta::tagCount() const +{ + return search_->tagCount(); +} + +TagGroupIndex +Sta::tagGroupCount() const +{ + return search_->tagGroupCount(); +} + +int +Sta::clkInfoCount() const +{ + return search_->clkInfoCount(); +} + +void +Sta::setArcDelay(Edge *edge, + TimingArc *arc, + const Corner *corner, + const MinMaxAll *min_max, + ArcDelay delay) +{ + for (auto mm : min_max->range()) { + const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm); + DcalcAPIndex ap_index = dcalc_ap->index(); + graph_->setArcDelay(edge, arc, ap_index, delay); + // Don't let delay calculation clobber the value. + graph_->setArcDelayAnnotated(edge, arc, ap_index, true); + } + if (edge->role()->isTimingCheck()) + search_->requiredInvalid(edge->to(graph_)); + else { + search_->arrivalInvalid(edge->to(graph_)); + search_->requiredInvalid(edge->from(graph_)); + } +} + +void +Sta::setAnnotatedSlew(Vertex *vertex, + const Corner *corner, + const MinMaxAll *min_max, + const TransRiseFallBoth *tr, + float slew) +{ + for (auto mm : min_max->range()) { + const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm); + DcalcAPIndex ap_index = dcalc_ap->index(); + for (auto tr1 : tr->range()) { + graph_->setSlew(vertex, tr1, ap_index, slew); + // Don't let delay calculation clobber the value. + vertex->setSlewAnnotated(true, tr1, ap_index); + } + } + graph_delay_calc_->delayInvalid(vertex); +} + +void +Sta::writeSdf(const char *filename, + Corner *corner, + char sdf_divider, + int digits, + bool gzip, + bool no_timestamp, + bool no_version) +{ + findDelays(); + sta::writeSdf(filename, corner, sdf_divider, digits, gzip, no_timestamp, + no_version, this); +} + +void +Sta::removeDelaySlewAnnotations() +{ + graph_->removeDelaySlewAnnotations(); + graph_delay_calc_->delaysInvalid(); +} + +LogicValue +Sta::simLogicValue(const Pin *pin) +{ + ensureGraph(); + sim_->ensureConstantsPropagated(); + return sim_->logicValue(pin); +} + +float +Sta::portExtPinCap(Port *port, + const TransRiseFall *tr, + const MinMax *min_max) +{ + float pin_cap, wire_cap; + int fanout; + bool pin_exists, wire_exists, fanout_exists; + sdc_->portExtCap(port, tr, min_max, + pin_cap, pin_exists, + wire_cap, wire_exists, + fanout, fanout_exists); + if (pin_exists) + return pin_cap; + else + return 0.0; +} + +void +Sta::setPortExtPinCap(Port *port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float cap) +{ + for (auto tr1 : tr->range()) { + for (auto mm : min_max->range()) { + sdc_->setPortExtPinCap(port, tr1, mm, cap); + } + } + delaysInvalidFromFanin(port); +} + +float +Sta::portExtWireCap(Port *port, + const TransRiseFall *tr, + const MinMax *min_max) +{ + float pin_cap, wire_cap; + int fanout; + bool pin_exists, wire_exists, fanout_exists; + sdc_->portExtCap(port, tr, min_max, + pin_cap, pin_exists, + wire_cap, wire_exists, + fanout, fanout_exists); + if (wire_exists) + return wire_cap; + else + return 0.0; +} + +void +Sta::setPortExtWireCap(Port *port, + bool subtract_pin_cap, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float cap) +{ + Corner *corner = cmd_corner_; + for (auto tr1 : tr->range()) { + for (auto mm : min_max->range()) { + sdc_->setPortExtWireCap(port, subtract_pin_cap, tr1, corner, mm, cap); + } + } + delaysInvalidFromFanin(port); +} + +void +Sta::removeNetLoadCaps() const +{ + sdc_->removeNetLoadCaps(); + graph_delay_calc_->delaysInvalid(); +} + +int +Sta::portExtFanout(Port *port, + const MinMax *min_max) +{ + return sdc_->portExtFanout(port, min_max); +} + +void +Sta::setPortExtFanout(Port *port, + int fanout, + const MinMaxAll *min_max) +{ + for (auto mm : min_max->range()) + sdc_->setPortExtFanout(port, mm, fanout); + delaysInvalidFromFanin(port); +} + +void +Sta::setNetWireCap(Net *net, + bool subtract_pin_cap, + const Corner *corner, + const MinMaxAll *min_max, + float cap) +{ + for (auto mm : min_max->range()) + sdc_->setNetWireCap(net, subtract_pin_cap, corner, mm, cap); + delaysInvalidFromFanin(net); +} + +void +Sta::connectedCap(Pin *drvr_pin, + const TransRiseFall *tr, + const Corner *corner, + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const +{ + pin_cap = 0.0; + wire_cap = 0.0; + bool cap_exists = false; + const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); + Parasitic *parasitic = arc_delay_calc_->findParasitic(drvr_pin, tr, dcalc_ap); + float ap_pin_cap = 0.0; + float ap_wire_cap = 0.0; + graph_delay_calc_->loadCap(drvr_pin, parasitic, tr, dcalc_ap, + ap_pin_cap, ap_wire_cap); + arc_delay_calc_->finishDrvrPin(); + if (!cap_exists + || min_max->compare(ap_pin_cap, pin_cap)) { + pin_cap = ap_pin_cap; + wire_cap = ap_wire_cap; + cap_exists = true; + } +} + +void +Sta::connectedCap(Net *net, + const TransRiseFall *tr, + const Corner *corner, + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const +{ + Pin *drvr_pin = findNetParasiticDrvrPin(net); + if (drvr_pin) + connectedCap(drvr_pin, tr, corner, min_max, pin_cap, wire_cap); + else { + pin_cap = 0.0; + wire_cap = 0.0; + } +} + +// Look for a driver to find a parasitic if the net has one. +// Settle for a load pin if there are no drivers. +Pin * +Sta::findNetParasiticDrvrPin(Net *net) const +{ + Pin *load_pin = nullptr; + NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (network_->isDriver(pin)) { + delete pin_iter; + return pin; + } + if (network_->isLoad(pin)) + load_pin = pin; + } + delete pin_iter; + return load_pin; +} + +void +Sta::setResistance(Net *net, + const MinMaxAll *min_max, + float res) +{ + sdc_->setResistance(net, min_max, res); +} + +//////////////////////////////////////////////////////////////// + +bool +Sta::readSpef(const char *filename, + Instance *instance, + const MinMaxAll *min_max, + bool increment, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + ReduceParasiticsTo reduce_to, + bool delete_after_reduce, + bool save, + bool quiet) +{ + Corner *corner = cmd_corner_; + const MinMax *cnst_min_max; + ParasiticAnalysisPt *ap; + if (min_max == MinMaxAll::all()) { + corners_->makeParasiticAnalysisPtsSingle(); + ap = corner->findParasiticAnalysisPt(MinMax::max()); + cnst_min_max = MinMax::max(); + } + else { + corners_->makeParasiticAnalysisPtsMinMax(); + cnst_min_max = min_max->asMinMax(); + ap = corner->findParasiticAnalysisPt(cnst_min_max); + } + const OperatingConditions *op_cond = + sdc_->operatingConditions(cnst_min_max); + bool success = readSpefFile(filename, instance, ap, increment, + pin_cap_included, + keep_coupling_caps, coupling_cap_factor, + reduce_to, delete_after_reduce, + op_cond, corner, cnst_min_max, save, quiet, + report_, network_, parasitics_); + graph_delay_calc_->delaysInvalid(); + search_->arrivalsInvalid(); + return success; +} + +void +Sta::findPiElmore(Pin *drvr_pin, + const TransRiseFall *tr, + 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, tr, ap); + if (pi_elmore) { + parasitics_->piModel(pi_elmore, c2, rpi, c1); + exists = true; + } + else + exists = false; +} + +void +Sta::makePiElmore(Pin *drvr_pin, + const TransRiseFall *tr, + const MinMaxAll *min_max, + float c2, + float rpi, + float c1) +{ + Corner *corner = cmd_corner_; + for (auto mm : min_max->range()) { + ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm); + parasitics_->makePiElmore(drvr_pin, tr, ap, c2, rpi, c1); + } + delaysInvalidFrom(drvr_pin); +} + +void +Sta::findElmore(Pin *drvr_pin, + Pin *load_pin, + const TransRiseFall *tr, + 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, tr, ap); + if (pi_elmore) { + exists = false; + parasitics_->findElmore(pi_elmore, load_pin, elmore, exists); + } + else + exists = false; +} + +void +Sta::setElmore(Pin *drvr_pin, + Pin *load_pin, + const TransRiseFall *tr, + const MinMaxAll *min_max, + float elmore) +{ + Corner *corner = cmd_corner_; + for (auto mm : min_max->range()) { + const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm); + Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, tr, ap); + if (pi_elmore) + parasitics_->setElmore(pi_elmore, load_pin, elmore); + } + delaysInvalidFrom(drvr_pin); +} + +//////////////////////////////////////////////////////////////// +// +// Network edit commands. +// +// This implementation calls Sta before/after methods to +// update the Sta components. +// A different implementation may let the network edits +// call the before/after methods implicitly so these functions +// should not (Verific). +// +//////////////////////////////////////////////////////////////// + +NetworkEdit * +Sta::networkCmdEdit() +{ + return dynamic_cast(cmd_network_); +} + +Instance * +Sta::makeInstance(const char *name, + LibertyCell *cell, + Instance *parent) +{ + NetworkEdit *network = networkCmdEdit(); + Instance *inst = network->makeInstance(cell, name, parent); + network->makePins(inst); + makeInstanceAfter(inst); + return inst; +} + +void +Sta::deleteInstance(Instance *inst) +{ + NetworkEdit *network = networkCmdEdit(); + deleteInstanceBefore(inst); + network->deleteInstance(inst); +} + +void +Sta::replaceCell(Instance *inst, + LibertyCell *to_lib_cell) +{ + Cell *to_cell = network_->cell(to_lib_cell); + replaceCell(inst, to_cell, to_lib_cell); +} + +void +Sta::replaceCell(Instance *inst, + Cell *to_cell) +{ + LibertyCell *to_lib_cell = network_->libertyCell(to_cell); + replaceCell(inst, to_cell, to_lib_cell); +} + +void +Sta::replaceCell(Instance *inst, + Cell *to_cell, + LibertyCell *to_lib_cell) +{ + NetworkEdit *network = networkCmdEdit(); + LibertyCell *from_lib_cell = network->libertyCell(inst); + if (sta::equivCells(from_lib_cell, to_lib_cell)) { + replaceEquivCellBefore(inst, to_lib_cell); + network->replaceCell(inst, to_cell); + replaceEquivCellAfter(inst); + } + else { + replaceCellBefore(inst, to_lib_cell); + network->replaceCell(inst, to_cell); + replaceCellAfter(inst); + } +} + +Net * +Sta::makeNet(const char *name, + Instance *parent) +{ + NetworkEdit *network = networkCmdEdit(); + Net *net = network->makeNet(name, parent); + // Sta notification unnecessary. + return net; +} + +void +Sta::deleteNet(Net *net) +{ + NetworkEdit *network = networkCmdEdit(); + deleteNetBefore(net); + network->deleteNet(net); +} + +void +Sta::connectPin(Instance *inst, + Port *port, + Net *net) +{ + NetworkEdit *network = networkCmdEdit(); + Pin *pin = network->connect(inst, port, net); + connectPinAfter(pin); +} + +void +Sta::connectPin(Instance *inst, + LibertyPort *port, + Net *net) +{ + NetworkEdit *network = networkCmdEdit(); + Pin *pin = network->connect(inst, port, net); + connectPinAfter(pin); +} + +void +Sta::disconnectPin(Pin *pin) +{ + NetworkEdit *network = networkCmdEdit(); + disconnectPinBefore(pin); + network->disconnectPin(pin); +} + +LibertyPort * +Sta::findCellPort(LibertyCell *cell, + PortDirection *dir) +{ + LibertyCellPortIterator port_iter(cell); + while (port_iter.hasNext()) { + auto port = port_iter.next(); + if (port->direction() == dir) + return port; + } + return nullptr; +} + +//////////////////////////////////////////////////////////////// +// +// Network edit before/after methods. +// +//////////////////////////////////////////////////////////////// + +// Network::makePins with connectPinAfter. +void +Sta::makeInstanceAfter(Instance *inst) +{ + LibertyCell *lib_cell = network_->libertyCell(inst); + if (lib_cell) { + LibertyCellPortBitIterator port_iter(lib_cell); + while (port_iter.hasNext()) { + LibertyPort *lib_port = port_iter.next(); + Pin *pin = network_->findPin(inst, lib_port); + connectPinAfter(pin); + } + } +} + +// Not used by Sta (connectPinAfter). +void +Sta::makePinAfter(Pin *pin) +{ + if (!network_->isHierarchical(pin) && graph_) { + Vertex *vertex, *bidir_drvr_vertex; + graph_->makePinVertices(pin, vertex, bidir_drvr_vertex); + graph_->makePinInstanceEdges(pin); + search_->arrivalInvalid(vertex); + search_->requiredInvalid(vertex); + if (bidir_drvr_vertex) { + search_->arrivalInvalid(bidir_drvr_vertex); + search_->requiredInvalid(bidir_drvr_vertex); + } + if (network_->net(pin)) + connectPinAfter(pin); + } + sim_->makePinAfter(pin); +} + +void +Sta::replaceEquivCellBefore(Instance *inst, + LibertyCell *to_cell) +{ + if (graph_) { + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + LibertyPort *port = network_->libertyPort(pin); + if (port->direction()->isAnyInput()) { + Vertex *vertex = graph_->pinLoadVertex(pin); + replaceCellPinInvalidate(port, vertex, to_cell); + + // Replace the timing arc sets in the graph edges. + 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) { + TimingArcSet *from_set = edge->timingArcSet(); + // Find corresponding timing arc set. + TimingArcSet *to_set = to_cell->findTimingArcSet(from_set); + if (to_set) + edge->setTimingArcSet(to_set); + else + internalError("corresponding timing arc set not found in equiv cells"); + } + } + } + else { + // Force delay calculation on output pins. + Vertex *vertex = graph_->pinDrvrVertex(pin); + graph_delay_calc_->delayInvalid(vertex); + } + } + delete pin_iter; + } +} + +void +Sta::replaceEquivCellAfter(Instance *inst) +{ + if (graph_) { + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (network_->direction(pin)->isAnyInput()) + parasitics_->loadPinCapacitanceChanged(pin); + } + delete pin_iter; + } +} + +void +Sta::replaceCellPinInvalidate(LibertyPort *from_port, + Vertex *vertex, + LibertyCell *to_cell) +{ + LibertyPort *to_port = to_cell->findLibertyPort(from_port->name()); + if (!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())) + // Input port capacitance changed, so invalidate delay + // calculation from input driver. + delaysInvalidFromFanin(vertex); + else + delaysInvalidFrom(vertex); +} + +bool +Sta::idealClockMode() +{ + for (auto clk : sdc_->clks()) { + if (clk->isPropagated()) + return false; + } + return true; +} + +static bool +libertyPortCapsEqual(LibertyPort *port1, + LibertyPort *port2) +{ + return port1->capacitance(TransRiseFall::rise(), MinMax::min()) + == port2->capacitance(TransRiseFall::rise(), MinMax::min()) + && port1->capacitance(TransRiseFall::rise(), MinMax::max()) + == port2->capacitance(TransRiseFall::rise(), MinMax::max()) + && port1->capacitance(TransRiseFall::fall(), MinMax::min()) + == port2->capacitance(TransRiseFall::fall(), MinMax::min()) + && port1->capacitance(TransRiseFall::fall(), MinMax::max()) + == port2->capacitance(TransRiseFall::fall(), MinMax::max()); +} + +void +Sta::replaceCellBefore(Instance *inst, + LibertyCell *to_cell) +{ + if (graph_) { + // Delete all graph edges between instance pins. + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + 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); + } + } + } + delete pin_iter; + } +} + +void +Sta::replaceCellAfter(Instance *inst) +{ + if (graph_) { + graph_->makeInstanceEdges(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); + } + delete pin_iter; + } +} + +void +Sta::connectPinAfter(Pin *pin) +{ + if (graph_) { + if (network_->isHierarchical(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_)); + } + } + else { + Vertex *vertex, *bidir_drvr_vertex; + if (network_->vertexIndex(pin) == 0) { + graph_->makePinVertices(pin, vertex, bidir_drvr_vertex); + graph_->makePinInstanceEdges(pin); + } + else + graph_->pinVertices(pin, vertex, bidir_drvr_vertex); + search_->arrivalInvalid(vertex); + search_->requiredInvalid(vertex); + if (bidir_drvr_vertex) { + search_->arrivalInvalid(bidir_drvr_vertex); + search_->requiredInvalid(bidir_drvr_vertex); + } + + // Make interconnect edges from/to pin. + if (network_->isDriver(pin)) { + graph_->makeWireEdgesFromPin(pin); + connectDrvrPinAfter(bidir_drvr_vertex ? bidir_drvr_vertex : vertex); + } + // Note that a bidirect is both a driver and a load so this + // is NOT an else clause for the above "if". + if (network_->isLoad(pin)) { + graph_->makeWireEdgesToPin(pin); + connectLoadPinAfter(vertex); + } + } + } + sdc_->connectPinAfter(pin); + sim_->connectPinAfter(pin); +} + +void +Sta::connectDrvrPinAfter(Vertex *vertex) +{ + // Invalidate arrival at fanout vertices. + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + search_->arrivalInvalid(to_vertex); + search_->endpointInvalid(to_vertex); + sdc_->clkHpinDisablesChanged(to_vertex->pin()); + } + sdc_->clkHpinDisablesChanged(vertex->pin()); + graph_delay_calc_->delayInvalid(vertex); + search_->requiredInvalid(vertex); + search_->endpointInvalid(vertex); + levelize_->invalidFrom(vertex); +} + +void +Sta::connectLoadPinAfter(Vertex *vertex) +{ + // Invalidate delays and required at fanin vertices. + 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()); + } + sdc_->clkHpinDisablesChanged(vertex->pin()); + graph_delay_calc_->delayInvalid(vertex); + levelize_->invalidFrom(vertex); + search_->arrivalInvalid(vertex); + search_->endpointInvalid(vertex); +} + +void +Sta::disconnectPinBefore(Pin *pin) +{ + parasitics_->disconnectPinBefore(pin); + sdc_->disconnectPinBefore(pin); + sim_->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); + } + } + } + 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); + } + } + } + if (network_->isHierarchical(pin)) { + // 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); + } + } + } +} + +void +Sta::deleteEdge(Edge *edge) +{ + Vertex *from = edge->from(graph_); + Vertex *to = edge->to(graph_); + search_->arrivalInvalid(to); + search_->requiredInvalid(from); + graph_delay_calc_->delayInvalid(to); + levelize_->relevelizeFrom(to); + levelize_->deleteEdgeBefore(edge); + sdc_->clkHpinDisablesChanged(edge->from(graph_)->pin()); + graph_->deleteEdge(edge); +} + +void +Sta::deleteNetBefore(Net *net) +{ + if (graph_) { + NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); + while (pin_iter->hasNext()) { + 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); + } + } + } + } + delete pin_iter; + } +} + +void +Sta::deleteInstanceBefore(Instance *inst) +{ + if (network_->isLeaf(inst)) + deleteLeafInstanceBefore(inst); + else { + // Delete hierarchical instance children. + InstanceChildIterator *child_iter = network_->childIterator(inst); + while (child_iter->hasNext()) { + Instance *child = child_iter->next(); + deleteInstanceBefore(child); + } + delete child_iter; + } +} + +void +Sta::deleteLeafInstanceBefore(Instance *inst) +{ + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + deletePinBefore(pin); + } + delete pin_iter; + sim_->deleteInstanceBefore(inst); +} + +void +Sta::deletePinBefore(Pin *pin) +{ + if (graph_) { + if (network_->isLoad(pin)) { + Vertex *vertex = graph_->pinLoadVertex(pin); + + levelize_->deleteVertexBefore(vertex); + graph_delay_calc_->deleteVertexBefore(vertex); + search_->deleteVertexBefore(vertex); + + VertexInEdgeIterator in_edge_iter(vertex, graph_); + while (in_edge_iter.hasNext()) { + Edge *edge = in_edge_iter.next(); + if (edge->role()->isWire()) { + Vertex *from = edge->from(graph_); + // Only notify from vertex (to vertex will be deleted). + search_->requiredInvalid(from); + } + levelize_->deleteEdgeBefore(edge); + } + graph_->deleteVertex(vertex); + } + if (network_->isDriver(pin)) { + Vertex *vertex = graph_->pinDrvrVertex(pin); + + levelize_->deleteVertexBefore(vertex); + graph_delay_calc_->deleteVertexBefore(vertex); + search_->deleteVertexBefore(vertex); + + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (edge->role()->isWire()) { + // Only notify to vertex (from will be deleted). + Vertex *to = edge->to(graph_); + // to->prev_paths point to vertex, so delete them. + search_->arrivalInvalidDelete(to); + graph_delay_calc_->delayInvalid(to); + levelize_->relevelizeFrom(to); + } + levelize_->deleteEdgeBefore(edge); + } + graph_->deleteVertex(vertex); + } + if (network_->direction(pin) == PortDirection::internal()) { + // Internal pins are not loads or drivers. + Vertex *vertex = graph_->pinLoadVertex(pin); + levelize_->deleteVertexBefore(vertex); + graph_delay_calc_->deleteVertexBefore(vertex); + search_->deleteVertexBefore(vertex); + graph_->deleteVertex(vertex); + } + } + sim_->deletePinBefore(pin); +} + +void +Sta::delaysInvalidFrom(Port *port) +{ + if (graph_) { + Instance *top_inst = network_->topInstance(); + Pin *pin = network_->findPin(top_inst, port); + delaysInvalidFrom(pin); + } +} + +void +Sta::delaysInvalidFrom(Instance *inst) +{ + if (graph_) { + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + delaysInvalidFrom(pin); + } + delete pin_iter; + } +} + +void +Sta::delaysInvalidFrom(Pin *pin) +{ + if (graph_) { + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + delaysInvalidFrom(vertex); + if (bidirect_drvr_vertex) + delaysInvalidFrom(bidirect_drvr_vertex); + } +} + +void +Sta::delaysInvalidFrom(Vertex *vertex) +{ + search_->arrivalInvalid(vertex); + search_->requiredInvalid(vertex); + graph_delay_calc_->delayInvalid(vertex); +} + +void +Sta::delaysInvalidFromFanin(Port *port) +{ + if (graph_) { + Instance *top_inst = network_->topInstance(); + Pin *pin = network_->findPin(top_inst, port); + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + delaysInvalidFromFanin(vertex); + if (bidirect_drvr_vertex) + delaysInvalidFromFanin(bidirect_drvr_vertex); + } +} + +void +Sta::delaysInvalidFromFanin(Pin *pin) +{ + if (graph_) { + Vertex *vertex, *bidirect_drvr_vertex; + graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); + if (vertex) + delaysInvalidFromFanin(vertex); + if (bidirect_drvr_vertex) + delaysInvalidFromFanin(bidirect_drvr_vertex); + } +} + +void +Sta::delaysInvalidFromFanin(Net *net) +{ + if (graph_) { + NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); + while (pin_iter->hasNext()) { + 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); + } + } + delete pin_iter; + } +} + +void +Sta::delaysInvalidFromFanin(Vertex *vertex) +{ + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *from_vertex = edge->from(graph_); + delaysInvalidFrom(from_vertex); + search_->requiredInvalid(from_vertex); + } +} + +//////////////////////////////////////////////////////////////// + +void +Sta::clocks(const Pin *pin, + // Return value. + ClockSet &clks) +{ + ensureClkArrivals(); + search_->clocks(pin, clks); +} + +//////////////////////////////////////////////////////////////// + +InstanceSet * +Sta::findRegisterInstances(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + findRegisterPreamble(); + return findRegInstances(clks, clk_tr, edge_triggered, latches, this); +} + +PinSet * +Sta::findRegisterDataPins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + findRegisterPreamble(); + return findRegDataPins(clks, clk_tr, edge_triggered, latches, this); +} + +PinSet * +Sta::findRegisterClkPins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + findRegisterPreamble(); + return findRegClkPins(clks, clk_tr, edge_triggered, latches, this); +} + +PinSet * +Sta::findRegisterAsyncPins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + findRegisterPreamble(); + return findRegAsyncPins(clks, clk_tr, edge_triggered, latches, this); +} + +PinSet * +Sta::findRegisterOutputPins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + findRegisterPreamble(); + return findRegOutputPins(clks, clk_tr, edge_triggered, latches, this); +} + +void +Sta::findRegisterPreamble() +{ + ensureGraph(); + sim_->ensureConstantsPropagated(); +} + +//////////////////////////////////////////////////////////////// + +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); + +protected: + bool crossesHierarchy(Edge *edge); + virtual bool searchThruRole(Edge *edge); + + bool thru_disabled_; + bool thru_constants_; + const StaState *sta_; +}; + +FanInOutSrchPred::FanInOutSrchPred(bool thru_disabled, + bool thru_constants, + const StaState *sta) : + SearchPred(), + thru_disabled_(thru_disabled), + thru_constants_(thru_constants), + sta_(sta) +{ +} + +bool +FanInOutSrchPred::searchFrom(const Vertex *from_vertex) +{ + return (thru_disabled_ + || !from_vertex->isDisabledConstraint()) + && (thru_constants_ + || !from_vertex->isConstant()); +} + +bool +FanInOutSrchPred::searchThru(Edge *edge) +{ + const Sdc *sdc = sta_->sdc(); + return searchThruRole(edge) + && (thru_disabled_ + || !(edge->isDisabledConstraint() + || edge->isDisabledCond() + || sdc->isDisabledCondDefault(edge))) + && (thru_constants_ + || edge->simTimingSense() != TimingSense::none); +} + +bool +FanInOutSrchPred::searchThruRole(Edge *edge) +{ + TimingRole *role = edge->role(); + return role == TimingRole::wire() + || role == TimingRole::combinational() + || role == TimingRole::tristateEnable() + || role == TimingRole::tristateDisable(); +} + +bool +FanInOutSrchPred::crossesHierarchy(Edge *edge) +{ + Network *network = sta_->network(); + Graph *graph = sta_->graph(); + Vertex *from = edge->from(graph); + Vertex *to = edge->to(graph); + Instance *from_inst = network->instance(from->pin()); + Instance *to_inst = network->instance(to->pin()); + return network->parent(from_inst) != network->parent(to_inst); +} + +bool +FanInOutSrchPred::searchTo(const Vertex *to_vertex) +{ + return (thru_disabled_ + || !to_vertex->isDisabledConstraint()) + && (thru_constants_ + || !to_vertex->isConstant()); +} + +class FaninSrchPred : public FanInOutSrchPred +{ +public: + FaninSrchPred(bool thru_disabled, + bool thru_constants, + const StaState *sta); + +protected: + virtual bool searchThruRole(Edge *edge); +}; + +FaninSrchPred::FaninSrchPred(bool thru_disabled, + bool thru_constants, + const StaState *sta) : + FanInOutSrchPred(thru_disabled, thru_constants, sta) +{ +} + +bool +FaninSrchPred::searchThruRole(Edge *edge) +{ + TimingRole *role = edge->role(); + 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) +{ + ensureGraph(); + ensureLevelized(); + PinSet *fanin = new PinSet; + FaninSrchPred pred(thru_disabled, thru_constants, this); + PinSeq::Iterator to_iter(to); + while (to_iter.hasNext()) { + Pin *pin = to_iter.next(); + 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); + } + } + else { + Vertex *vertex = graph_->pinLoadVertex(pin); + findFaninPins(vertex, flat, startpoints_only, + inst_levels, pin_levels, fanin, pred); + } + } + return fanin; +} + +void +Sta::findFaninPins(Vertex *vertex, + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + PinSet *fanin, + SearchPred &pred) +{ + VertexSet visited; + 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(); + Pin *visited_pin = visited_vertex->pin(); + if (!startpoints_only + || network_->isRegClkPin(visited_pin) + || !hasFanin(visited_vertex, &pred, graph_)) + 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) +{ + debugPrint1(debug_, "fanin", 1, "%s\n", + to->name(sdc_network_)); + if (!visited.hasKey(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)) { + 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); + } + } + } + } +} + +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); + 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) +{ + ensureGraph(); + ensureLevelized(); + PinSet *fanout = new PinSet; + FanInOutSrchPred pred(thru_disabled, thru_constants, this); + PinSeq::Iterator from_iter(from); + while (from_iter.hasNext()) { + Pin *pin = from_iter.next(); + 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); + } + } + else { + Vertex *vertex = graph_->pinDrvrVertex(pin); + findFanoutPins(vertex, flat, endpoints_only, + inst_levels, pin_levels, fanout, pred); + } + } + return fanout; +} + +void +Sta::findFanoutPins(Vertex *vertex, + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + PinSet *fanout, + SearchPred &pred) +{ + VertexSet visited; + 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(); + Pin *visited_pin = visited_vertex->pin(); + if (!endpoints_only + || search_->isEndpoint(visited_vertex, &pred)) + fanout->insert(visited_pin); + } +} + +// 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) +{ + debugPrint1(debug_, "fanout", 1, "%s\n", + from->name(sdc_network_)); + if (!visited.hasKey(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)) { + 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); + } + } + } + } +} + +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); + return pinInstances(pins, network_); +} + +static InstanceSet * +pinInstances(PinSet *pins, + const Network *network) +{ + InstanceSet *insts = new InstanceSet; + PinSet::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + insts->insert(network->instance(pin)); + } + delete pins; + return insts; +} + +bool +Sta::crossesHierarchy(Edge *edge) const +{ + Vertex *from = edge->from(graph_); + Vertex *to = edge->to(graph_); + const Pin *from_pin = from->pin(); + Instance *from_inst = network_->instance(from_pin); + Instance *to_inst = network_->instance(to->pin()); + Instance *from_parent, *to_parent; + // Treat input/output port pins as "inside". + if (network_->isTopInstance(from_inst)) + from_parent = from_inst; + else + from_parent = network_->parent(from_inst); + if (network_->isTopInstance(to_inst)) + to_parent = to_inst; + else + to_parent = network_->parent(to_inst); + return from_parent != to_parent; +} + +//////////////////////////////////////////////////////////////// + +class InstanceMaxSlewGreater +{ +public: + explicit InstanceMaxSlewGreater(const StaState *sta); + bool operator()(const Instance *inst1, + const Instance *inst2) const; + +protected: + Slew instMaxSlew(const Instance *inst) const; + const StaState *sta_; +}; + +InstanceMaxSlewGreater::InstanceMaxSlewGreater(const StaState *sta) : + sta_(sta) +{ +} + +bool +InstanceMaxSlewGreater::operator()(const Instance *inst1, + const Instance *inst2) const +{ + return instMaxSlew(inst1) > instMaxSlew(inst2); +} + +Slew +InstanceMaxSlewGreater::instMaxSlew(const Instance *inst) const +{ + Network *network = sta_->network(); + Graph *graph = sta_->graph(); + Slew max_slew = 0.0; + InstancePinIterator *pin_iter = network->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (network->isDriver(pin)) { + Vertex *vertex = graph->pinDrvrVertex(pin); + for (auto tr : TransRiseFall::range()) { + for (auto dcalc_ap : sta_->corners()->dcalcAnalysisPts()) { + Slew slew = graph->slew(vertex, tr, dcalc_ap->index()); + if (slew > max_slew) + max_slew = slew; + } + } + } + } + delete pin_iter; + return max_slew; +} + +SlowDrvrIterator * +Sta::slowDrvrIterator() +{ + InstanceSeq *insts = new InstanceSeq; + LeafInstanceIterator *leaf_iter = network_->leafInstanceIterator(); + while (leaf_iter->hasNext()) { + Instance *leaf = leaf_iter->next(); + insts->push_back(leaf); + } + delete leaf_iter; + + sort(insts, InstanceMaxSlewGreater(this)); + return new SlowDrvrIterator(insts); +} + +////////////////////////////////////////////////////////////////' + +void +Sta::checkSlewLimitPreamble() +{ + if (sdc_->haveClkSlewLimits()) + // Arrivals are needed to know what pin clock domains. + updateTiming(false); + else + findDelays(); + if (check_slew_limits_ == nullptr) + makeCheckSlewLimits(); +} + +Pin * +Sta::pinMinSlewLimitSlack(const Corner *corner, + const MinMax *min_max) +{ + checkSlewLimitPreamble(); + return check_slew_limits_->pinMinSlewLimitSlack(corner, min_max); +} + +PinSeq * +Sta::pinSlewLimitViolations(const Corner *corner, + const MinMax *min_max) +{ + checkSlewLimitPreamble(); + return check_slew_limits_->pinSlewLimitViolations(corner, min_max); +} + +void +Sta::reportSlewLimitShortHeader() +{ + report_path_->reportSlewLimitShortHeader(); +} + +void +Sta::reportSlewLimitShort(Pin *pin, + const Corner *corner, + const MinMax *min_max) +{ + const Corner *corner1; + const TransRiseFall *tr; + Slew slew; + float limit, slack; + check_slew_limits_->checkSlews(pin, corner, min_max, + corner1, tr, slew, limit, slack); + report_path_->reportSlewLimitShort(pin, tr, slew, limit, slack); +} + +void +Sta::reportSlewLimitVerbose(Pin *pin, + const Corner *corner, + const MinMax *min_max) +{ + const Corner *corner1; + const TransRiseFall *tr; + Slew slew; + float limit, slack; + check_slew_limits_->checkSlews(pin, corner, min_max, + corner1, tr, slew, limit, slack); + report_path_->reportSlewLimitVerbose(pin, corner1, tr, slew, + limit, slack, min_max); +} + +void +Sta::checkSlews(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + // Return values. + const Corner *&corner1, + const TransRiseFall *&tr, + Slew &slew, + float &limit, + float &slack) +{ + checkSlewLimitPreamble(); + check_slew_limits_->init(min_max); + check_slew_limits_->checkSlews(pin, corner, min_max, + corner1, tr, slew, limit, slack); +} + +////////////////////////////////////////////////////////////////' + +void +Sta::minPulseWidthPreamble() +{ + 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) +{ + report_path_->reportMpwChecks(checks, verbose); +} + +void +Sta::reportMpwCheck(MinPulseWidthCheck *check, + bool verbose) +{ + report_path_->reportMpwCheck(check, verbose); +} + +//////////////////////////////////////////////////////////////// + +MinPeriodCheckSeq & +Sta::minPeriodViolations() +{ + minPeriodPreamble(); + return check_min_periods_->violations(); +} + +MinPeriodCheck * +Sta::minPeriodSlack() +{ + minPeriodPreamble(); + return check_min_periods_->minSlackCheck(); +} + +void +Sta::minPeriodPreamble() +{ + // 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) +{ + 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() +{ + maxSkewPreamble(); + return check_max_skews_->minSlackCheck(); +} + +void +Sta::maxSkewPreamble() +{ + ensureClkArrivals(); + if (check_max_skews_ == nullptr) + 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) +{ + delete equiv_cells_; + equiv_cells_ = new EquivCells(equiv_libs, map_libs); +} + +LibertyCellSeq * +Sta::equivCells(LibertyCell *cell) +{ + if (equiv_cells_) + return equiv_cells_->equivs(cell); + else + return nullptr; +} + +//////////////////////////////////////////////////////////////// +void +Sta::powerPreamble() +{ + // Use arrivals to find clocking info. + searchPreamble(); + search_->findAllArrivals(); +} + +void +Sta::power(const Corner *corner, + // Return values. + PowerResult &total, + PowerResult &sequential, + PowerResult &combinational, + PowerResult ¯o, + PowerResult &pad) +{ + powerPreamble(); + power_->power(corner, total, sequential, combinational, macro, pad); +} + +void +Sta::power(const Instance *inst, + const Corner *corner, + // Return values. + PowerResult &result) +{ + powerPreamble(); + power_->power(inst, corner, result); +} + +} // namespace diff --git a/search/Sta.hh b/search/Sta.hh new file mode 100644 index 0000000..1a0f8f1 --- /dev/null +++ b/search/Sta.hh @@ -0,0 +1,1331 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_STA_H +#define STA_STA_H + +#include +#include "DisallowCopyAssign.hh" +#include "StringSeq.hh" +#include "StaState.hh" +#include "LibertyClass.hh" +#include "NetworkClass.hh" +#include "SdcClass.hh" +#include "GraphClass.hh" +#include "SearchClass.hh" +#include "ParasiticsClass.hh" +#include "VertexVisitor.hh" + +struct Tcl_Interp; + +namespace sta { + +using std::string; +using ::Tcl_Interp; + +// Don't include headers to minimize dependencies. +class MinMax; +class MinMaxAll; +class TransRiseFallBoth; +class TransRiseFall; +class ReportPath; +class CheckTiming; +class DcalcAnalysisPt; +class CheckSlewLimits; +class CheckMinPulseWidths; +class CheckMinPeriods; +class CheckMaxSkews; +class PatternMatch; +class CheckPeriods; +class LibertyReader; +class SearchPred; +class Corner; +class ClkSkews; +class ReportField; +class Power; +class PowerResult; +class ClockIterator; +class EquivCells; + +typedef InstanceSeq::Iterator SlowDrvrIterator; +typedef Vector CheckError; +typedef Vector CheckErrorSeq; + +enum class CmdNamespace { sta, sdc }; + +// Initialize sta functions that are not part of the Sta class. +void initSta(); + +// Call before exit to make leak detection simpler for purify and valgrind. +void +deleteAllMemory(); + +// The Lord, God, King, Master of the Timing Universe. +// This class is a FACADE used to present an API to the collection of +// objects that hold the collective state of the static timing analyzer. +// It should only hold pointers to objects so that only the referenced +// class declarations and not their definitions are needed by this header. +// +// The report object is not owned by the sta object. +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 + // virtual function called in a base class constructor does not + // call the derived class function. + virtual void makeComponents(); + // Call copyState for each component to notify it that some + // pointers to some components have changed. + // This must be called after changing any of the StaState components. + virtual void updateComponentsState(); + virtual ~Sta(); + + // Singleton accessor used by tcl command interpreter. + static Sta *sta(); + static void setSta(Sta *sta); + + // Default number of threads to use. + 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 setMinLibrary(const char *min_filename, + const char *max_filename); + // Network readers call this to notify the Sta to delete any previously + // linked network. + void readNetlistBefore(); + // Return true if successful. + bool linkDesign(const char *top_cell_name); + bool linkMakeBlackBoxes() const; + void setLinkMakeBlackBoxes(bool make); + + // SDC Swig API. + Instance *currentInstance() const; + void setCurrentInstance(Instance *inst); + virtual void setAnalysisType(AnalysisType analysis_type); + void setOperatingConditions(OperatingConditions *op_cond, + const MinMaxAll *min_max); + void setTimingDerate(TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate); + // Delay type is always net for net derating. + void setTimingDerate(const Net *net, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate); + void setTimingDerate(const Instance *inst, + TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate); + void setTimingDerate(const LibertyCell *cell, + TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate); + void unsetTimingDerate(); + void setInputSlew(Port *port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float slew); + // Port external pin load. + float portExtPinCap(Port *port, + const TransRiseFall *tr, + const MinMax *min_max); + // Set port external pin load (set_load -pin port). + void setPortExtPinCap(Port *port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float cap); + // Port external wire load. + float portExtWireCap(Port *port, + const TransRiseFall *tr, + const MinMax *min_max); + // Set port external wire load (set_load -wire port). + void setPortExtWireCap(Port *port, + bool subtract_pin_cap, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float cap); + // Set net wire capacitance (set_load -wire net). + void setNetWireCap(Net *net, + bool subtract_pin_load, + const Corner *corner, + const MinMaxAll *min_max, + float cap); + // Port external fanout (used by wireload models). + int portExtFanout(Port *port, + const MinMax *min_max); + // Set port external fanout (used by wireload models). + void setPortExtFanout(Port *port, + int fanout, + const MinMaxAll *min_max); + // Remove all "set_load net" annotations. + void removeNetLoadCaps() const; + // pin_cap = net pin capacitances + port external pin capacitance, + // wire_cap = annotated net capacitance + port external wire capacitance. + void connectedCap(Pin *drvr_pin, + const TransRiseFall *tr, + const Corner *corner, + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const; + void connectedCap(Net *net, + const TransRiseFall *tr, + const Corner *corner, + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const; + void setResistance(Net *net, + const MinMaxAll *min_max, + float res); + void setDriveCell(LibertyLibrary *library, + LibertyCell *cell, + Port *port, + LibertyPort *from_port, + float *from_slews, + LibertyPort *to_port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max); + void setDriveResistance(Port *port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float res); + void setLatchBorrowLimit(Pin *pin, + float limit); + void setLatchBorrowLimit(Instance *inst, + float limit); + void setLatchBorrowLimit(Clock *clk, + float limit); + void setMinPulseWidth(const TransRiseFallBoth *tr, + float min_width); + void setMinPulseWidth(const Pin *pin, + const TransRiseFallBoth *tr, + float min_width); + void setMinPulseWidth(const Instance *inst, + const TransRiseFallBoth *tr, + float min_width); + void setMinPulseWidth(const Clock *clk, + const TransRiseFallBoth *tr, + float min_width); + void setWireload(Wireload *wireload, + const MinMaxAll *min_max); + void setWireloadMode(WireloadMode mode); + void setWireloadSelection(WireloadSelection *selection, + const MinMaxAll *min_max); + void setSlewLimit(Clock *clk, + const TransRiseFallBoth *tr, + const PathClkOrData clk_data, + const MinMax *min_max, + float slew); + void setSlewLimit(Port *port, + const MinMax *min_max, + float slew); + void setSlewLimit(Pin *pin, + const MinMax *min_max, + float slew); + void setSlewLimit(Cell *cell, + const MinMax *min_max, + float slew); + void setCapacitanceLimit(Cell *cell, + const MinMax *min_max, + float cap); + void setCapacitanceLimit(Port *port, + const MinMax *min_max, + float cap); + void setCapacitanceLimit(Pin *pin, + const MinMax *min_max, + float cap); + void setFanoutLimit(Cell *cell, + const MinMax *min_max, + float fanout); + void setFanoutLimit(Port *port, + const MinMax *min_max, + float fanout); + void setMaxArea(float area); + + void makeClock(const char *name, + PinSet *pins, + bool add_to_pins, + float period, + FloatSeq *waveform, + char *comment); + // edges size must be 3. + void makeGeneratedClock(const char *name, + PinSet *pins, + bool add_to_pins, + Pin *src_pin, + Clock *master_clk, + Pin *pll_out, + Pin *pll_fdbk, + int divide_by, + int multiply_by, + float duty_cycle, + bool invert, + bool combinational, + IntSeq *edges, + FloatSeq *edge_shifts, + char *comment); + void removeClock(Clock *clk); + // Update period/waveform for generated clocks from source pin clock. + void updateGeneratedClks(); + // Use Sdc::findClock + Clock *findClock(const char *name) const __attribute__ ((deprecated)); + // Use findClocksMatching. + void findClocksMatching(PatternMatch *pattern, + ClockSeq *clks) const __attribute__ ((deprecated)); + // Use Sdc::clockIterator. + ClockIterator *clockIterator() const __attribute__ ((deprecated)); + // True if pin is defined as a clock source (pin may be hierarchical). + bool isClockSrc(const Pin *pin) const; + // Use Sdc::defaultArrivalClock. + Clock *defaultArrivalClock() const __attribute__ ((deprecated)); + // 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 TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float slew); + void removeClockSlew(Clock *clk); + // Clock latency. + // Latency can be on a clk, pin, or clk/pin combination. + void setClockLatency(Clock *clk, + Pin *pin, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float delay); + void removeClockLatency(const Clock *clk, + const Pin *pin); + // Clock insertion delay (source latency). + void setClockInsertion(const Clock *clk, + const Pin *pin, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay); + void removeClockInsertion(const Clock *clk, + const Pin *pin); + // 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); + // Inter-clock uncertainty. + virtual void setClockUncertainty(Clock *from_clk, + const TransRiseFallBoth *from_tr, + Clock *to_clk, + const TransRiseFallBoth *to_tr, + const SetupHoldAll *setup_hold, + float uncertainty); + virtual void removeClockUncertainty(Clock *from_clk, + const TransRiseFallBoth *from_tr, + Clock *to_clk, + const TransRiseFallBoth *to_tr, + 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 makeClockGroup(ClockGroups *clk_groups, + ClockSet *clks); + void setClockSense(PinSet *pins, + ClockSet *clks, + ClockSense sense); + void setClockGatingCheck(const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin); + void setClockGatingCheck(Clock *clk, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin); + void setClockGatingCheck(Instance *inst, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin, + LogicValue active_value); + void setClockGatingCheck(Pin *pin, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin, + LogicValue active_value); + void setDataCheck(Pin *from, + const TransRiseFallBoth *from_tr, + Pin *to, + const TransRiseFallBoth *to_tr, + Clock *clk, + const SetupHoldAll *setup_hold, + float margin); + void removeDataCheck(Pin *from, + const TransRiseFallBoth *from_tr, + Pin *to, + const TransRiseFallBoth *to_tr, + Clock *clk, + const SetupHoldAll *setup_hold); + // 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); + void removeDisable(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to); + // set_disable_timing liberty port. + // Bus and bundle ports are NOT supported. + void disable(LibertyPort *port); + void removeDisable(LibertyPort *port); + // set_disable_timing port (top level instance port). + // Bus and bundle ports are NOT supported. + void disable(Port *port); + void removeDisable(Port *port); + // 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); + void removeDisable(Instance *inst, + LibertyPort *from, + LibertyPort *to); + // set_disable_timing pin + void disable(Pin *pin); + void removeDisable(Pin *pin); + // set_disable_timing [get_timing_arc -of_objects instance]] + void disable(Edge *edge); + void removeDisable(Edge *edge); + // set_disable_timing [get_timing_arc -of_objects lib_cell]] + void disable(TimingArcSet *arc_set); + void removeDisable(TimingArcSet *arc_set); + // Edge is disabled by constant. + bool isDisabledConstant(Edge *edge); + // Edge is default cond disabled by timing_disable_cond_default_arcs var. + bool isDisabledCondDefault(Edge *edge); + // Edge is disabled to prpath a clock from propagating. + bool isDisabledClock(Edge *edge); + // Return a set of constant pins that disabled edge. + // Caller owns the returned set. + PinSet *disabledConstantPins(Edge *edge); + // Edge timing sense with propagated constants. + TimingSense simTimingSense(Edge *edge); + // Edge is disabled by set_disable_timing constraint. + bool isDisabledConstraint(Edge *edge); + // Edge is disabled to break combinational loops. + bool isDisabledLoop(Edge *edge) const; + // Edge is disabled internal bidirect output path. + bool isDisabledBidirectInstPath(Edge *edge) const; + // Edge is disabled bidirect net path. + bool isDisabledBidirectNetPath(Edge *edge) const; + 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 removeDisableClockGatingCheck(Instance *inst); + void removeDisableClockGatingCheck(Pin *pin); + void setLogicValue(Pin *pin, + LogicValue value); + void setCaseAnalysis(Pin *pin, + LogicValue value); + void removeCaseAnalysis(Pin *pin); + void setInputDelay(Pin *pin, + const TransRiseFallBoth *tr, + Clock *clk, + const TransRiseFall *clk_tr, + Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, + float delay); + void removeInputDelay(Pin *pin, + TransRiseFallBoth *tr, + Clock *clk, + TransRiseFall *clk_tr, + MinMaxAll *min_max); + void setOutputDelay(Pin *pin, + const TransRiseFallBoth *tr, + Clock *clk, + const TransRiseFall *clk_tr, + Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, + float delay); + void removeOutputDelay(Pin *pin, + TransRiseFallBoth *tr, + Clock *clk, + TransRiseFall *clk_tr, + MinMaxAll *min_max); + void makeFalsePath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + const char *comment); + void makeMulticyclePath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + const char *comment); + void makePathDelay(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, + float delay, + const char *comment); + void makeGroupPath(const char *name, + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const char *comment); + bool isGroupPathName(const char *group_name); + void resetPath(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max); + // Make an exception -from specification. + ExceptionFrom *makeExceptionFrom(PinSet *from_pins, + ClockSet *from_clks, + InstanceSet *from_insts, + const TransRiseFallBoth *from_tr); + void checkExceptionFromPins(ExceptionFrom *from, + const char *file, + int line) const; + bool exceptionFromInvalid(const Pin *pin) const; + void deleteExceptionFrom(ExceptionFrom *from); + // Make an exception -through specification. + ExceptionThru *makeExceptionThru(PinSet *pins, + NetSet *nets, + InstanceSet *insts, + const TransRiseFallBoth *tr); + void deleteExceptionThru(ExceptionThru *thru); + // Make an exception -to specification. + ExceptionTo *makeExceptionTo(PinSet *to_pins, + ClockSet *to_clks, + InstanceSet *to_insts, + const TransRiseFallBoth *tr, + TransRiseFallBoth *end_tr); + void checkExceptionToPins(ExceptionTo *to, + const char *file, int) const; + void deleteExceptionTo(ExceptionTo *to); + InstanceSet *findRegisterInstances(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches); + PinSet *findRegisterDataPins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool registers, + bool latches); + PinSet *findRegisterClkPins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool registers, + bool latches); + PinSet *findRegisterAsyncPins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool registers, + bool latches); + PinSet *findRegisterOutputPins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool registers, + bool latches); + PinSet * + findFaninPins(PinSeq *to, + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants); + InstanceSet * + findFaninInstances(PinSeq *to, + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants); + PinSet * + findFanoutPins(PinSeq *from, + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants); + InstanceSet * + findFanoutInstances(PinSeq *from, + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants); + + // The set of clocks that reach pin. + void clocks(const Pin *pin, + // Return value. + ClockSet &clks); + + // Return the pin with the min/max slew limit slack. + // corner=nullptr checks all corners. + Pin *pinMinSlewLimitSlack(const Corner *corner, + const MinMax *min_max); + // Return all pins with min/max slew violations. + // corner=nullptr checks all corners. + PinSeq *pinSlewLimitViolations(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); + void checkSlews(const Pin *pin, + const Corner *corner, + const MinMax *min_max, + // Return values. + const Corner *&corner1, + const TransRiseFall *&tr, + Slew &slew, + float &limit, + float &slack); + // 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); + + + //////////////////////////////////////////////////////////////// + // User visible but non SDC commands. + + // Instance specific process/voltage/temperature. + // Defaults to operating condition if instance is not annotated. + 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(Instance *inst, + const MinMaxAll *min_max, + Pvt *pvt); + // 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(); + // Namespace used by command interpreter. + CmdNamespace cmdNamespace(); + void setCmdNamespace(CmdNamespace namespc); + OperatingConditions *operatingConditions(const MinMax *min_max) 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); + // Set annotated slew on a vertex for delay calculation. + void setAnnotatedSlew(Vertex *vertex, + const Corner *corner, + const MinMaxAll *min_max, + const TransRiseFallBoth *tr, + float slew); + void writeSdf(const char *filename, + Corner *corner, + char sdf_divider, + int digits, + bool gzip, + bool no_timestamp, + bool no_version); + // Remove all delay and slew annotations. + void removeDelaySlewAnnotations(); + // TCL variable sta_crpr_enabled. + // Common Reconvergent Clock Removal (CRPR). + // Timing check source/target common clock path overlap for search + // with analysis mode on_chip_variation. + bool crprEnabled() const; + void setCrprEnabled(bool enabled); + // TCL variable sta_crpr_mode. + CrprMode crprMode() const; + void setCrprMode(CrprMode mode); + // TCL variable sta_pocv_enabled. + // Parametric on chip variation (statisical sta). + bool pocvEnabled() const; + void setPocvEnabled(bool enabled); + // Number of std deviations from mean to use for normal distributions. + void setSigmaFactor(float factor); + // TCL variable sta_propagate_gated_clock_enable. + // Propagate gated clock enable arrivals. + bool propagateGatedClockEnable() const; + void setPropagateGatedClockEnable(bool enable); + // TCL variable sta_preset_clear_arcs_enabled. + // Enable search through preset/clear arcs. + bool presetClrArcsEnabled() const; + void setPresetClrArcsEnabled(bool enable); + // TCL variable sta_cond_default_arcs_enabled. + // Enable/disable default arcs when conditional arcs exist. + bool condDefaultArcsEnabled() const; + void setCondDefaultArcsEnabled(bool enabled); + // TCL variable sta_internal_bidirect_instance_paths_enabled. + // 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); + // TCL variable sta_gated_clock_checks_enabled. + bool gatedClkChecksEnabled() const; + void setGatedClkChecksEnabled(bool enabled); + // TCL variable sta_dynamic_loop_breaking. + bool dynamicLoopBreaking() const; + void setDynamicLoopBreaking(bool enable); + // TCL variable sta_propagate_all_clocks. + // Clocks defined after sta_propagate_all_clocks is true + // are propagated (existing clocks are not effected). + bool propagateAllClocks() const; + void setPropagateAllClocks(bool prop); + // TCL var sta_clock_through_tristate_enabled. + bool clkThruTristateEnabled() const; + void setClkThruTristateEnabled(bool enable); + // TCL variable sta_input_port_default_clock. + bool useDefaultArrivalClock() const; + void setUseDefaultArrivalClock(bool enable); + 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); + // Path from/thrus/to filter. + // from/thrus/to are owned and deleted by Search. + // Returned sequence is owned by the caller. + // PathEnds 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_count, + // Number of paths to report for + // each endpoint. + int endpoint_count, + // endpoint_count paths report unique pins + // without rise/fall variations. + bool unique_pins, + // 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 *reportTiming(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + // 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_count, + // Number of paths to report for + // each endpoint. + int endpoint_count, + // endpoint_count paths report unique pins + // without rise/fall variations. + bool unique_pins, + // 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) __attribute__ ((deprecated)); + void setReportPathFormat(ReportPathFormat format); + void setReportPathFieldOrder(StringSeq *field_names); + void setReportPathFields(bool report_input_pin, + bool report_net, + bool report_cap, + bool report_slew); + ReportField *findReportPathField(const char *name); + void setReportPathDigits(int digits); + void setReportPathNoSplit(bool no_split); + // Report clk skews for clks. + void reportClkSkew(ClockSet *clks, + const Corner *corner, + const SetupHold *setup_hold, + int digits); + // Header above reportPathEnd results. + void reportPathEndHeader(); + // Footer below reportPathEnd results. + void reportPathEndFooter(); + // Format report_path_endpoint only: + // Previous path end is used to detect path group changes + // so headers are reported by group. + void reportPathEnd(PathEnd *end, + PathEnd *prev_end); + void reportPathEnd(PathEnd *end); + void reportPathEnds(PathEndSeq *ends); + ReportPath *reportPath() { return report_path_; } + void reportPath(Path *path); + // Update arrival times for all pins. + // If necessary updateTiming propagates arrivals around latch + // loops until the arrivals converge. + // If full=false update arrivals incrementally. + // If full=true update all arrivals from scratch. + void updateTiming(bool full); + // Invalidate all arrival and required times. + void arrivalsInvalid(); + void setPathMinMax(const MinMaxAll *min_max) __attribute__ ((deprecated)); + void visitStartpoints(VertexVisitor *visitor); + void visitEndpoints(VertexVisitor *visitor); + // Find the fanin vertices for a group path. + // Vertices in the clock network are NOT included. + // Return value is owned by the caller. + PinSet *findGroupPathPins(const char *group_path_name); + // Find all required times after updateTiming(). + void findRequireds(); + string *reportDelayCalc(Edge *edge, + TimingArc *arc, + const Corner *corner, + const MinMax *min_max, + int digits); + void writeSdc(const char *filename, + bool native, + bool no_timestamp, + int digits); + + // The sum of all negative endpoints slacks. + // Incrementally updated. + Slack totalNegativeSlack(const MinMax *min_max); + Slack totalNegativeSlack(const Corner *corner, + 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); + VertexPathIterator *vertexPathIterator(Vertex *vertex, + const TransRiseFall *tr, + const PathAnalysisPt *path_ap); + VertexPathIterator *vertexPathIterator(Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max); + void + vertexWorstArrivalPath(Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max, + // Return value. + PathRef &worst_path); + void + vertexWorstArrivalPath(Vertex *vertex, + const MinMax *min_max, + // Return value. + PathRef &worst_path); + void + vertexWorstSlackPath(Vertex *vertex, + const MinMax *min_max, + // Return value. + PathRef &worst_path); + void + vertexWorstSlackPath(Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max, + // Return value. + PathRef &worst_path); + + // The following arrival/required/slack functions incrementally + // 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 TransRiseFall *tr, + const ClockEdge *clk_edge, + const PathAnalysisPt *path_ap); + // Min/max across all clock tags. + Arrival vertexArrival(Vertex *vertex, + const TransRiseFall *tr, + const PathAnalysisPt *path_ap); + Required vertexRequired(Vertex *vertex, + const TransRiseFall *tr, + const ClockEdge *clk_edge, + const PathAnalysisPt *path_ap); + // Min/max across all clock tags. + Required vertexRequired(Vertex *vertex, + const TransRiseFall *tr, + const PathAnalysisPt *path_ap); + Slack pinSlack(const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max); + Slack pinSlack(const Pin *pin, + const MinMax *min_max); + Slack vertexSlack(Vertex *vertex, + const MinMax *min_max); + Slack vertexSlack(Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max); + // Slack with respect to clk_edge. + Slack vertexSlack(Vertex *vertex, + const TransRiseFall *tr, + const ClockEdge *clk_edge, + const PathAnalysisPt *path_ap); + // Min slack across all clock tags. + Slack vertexSlack(Vertex *vertex, + const TransRiseFall *tr, + const PathAnalysisPt *path_ap); + // Slew for one delay calc analysis pt(corner). + Slew vertexSlew(Vertex *vertex, + const TransRiseFall *tr, + const DcalcAnalysisPt *dcalc_ap); + // Slew across all corners. + Slew vertexSlew(Vertex *vertex, + const TransRiseFall *tr, + const MinMax *min_max); + ArcDelay arcDelay(Edge *edge, + TimingArc *arc, + const DcalcAnalysisPt *dcalc_ap); + // True if the timing arc has been back-annotated. + bool arcDelayAnnotated(Edge *edge, + TimingArc *arc, + DcalcAnalysisPt *dcalc_ap); + // Set/unset the back-annotation flag for a timing arc. + void setArcDelayAnnotated(Edge *edge, + TimingArc *arc, + DcalcAnalysisPt *dcalc_ap, + 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; + int arrivalCount() const; + int vertexArrivalCount(Vertex *vertex) const; + Vertex *maxArrivalCountVertex() const; + + LogicValue simLogicValue(const Pin *pin); + // Iterator for instances sorted by max driver pin slew. + // Caller owns iterator and iterator->container(). + SlowDrvrIterator *slowDrvrIterator(); + + // Annotate hierarchical "instance" with parasitics. + // The parasitic analysis point is ap_name. + // The parasitics are used by delay calculation analysis points + // specfied by min_max. + // 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 MinMaxAll *min_max, + bool increment, + bool pin_cap_included, + bool keep_coupling_caps, + float coupling_cap_factor, + ReduceParasiticsTo reduce_to, + bool delete_after_reduce, + bool save, + bool quiet); + // Parasitics. + void findPiElmore(Pin *drvr_pin, + const TransRiseFall *tr, + const MinMax *min_max, + float &c2, + float &rpi, + float &c1, + bool &exists) const; + void findElmore(Pin *drvr_pin, + Pin *load_pin, + const TransRiseFall *tr, + const MinMax *min_max, + float &elmore, + bool &exists) const; + void makePiElmore(Pin *drvr_pin, + const TransRiseFall *tr, + const MinMaxAll *min_max, + float c2, + float rpi, + float c1); + void setElmore(Pin *drvr_pin, + Pin *load_pin, + const TransRiseFall *tr, + const MinMaxAll *min_max, + float elmore); + + // TCL network edit function support. + virtual Instance *makeInstance(const char *name, + LibertyCell *cell, + Instance *parent); + virtual void deleteInstance(Instance *inst); + // replace_cell + virtual void replaceCell(Instance *inst, + Cell *to_cell); + virtual void replaceCell(Instance *inst, + LibertyCell *to_lib_cell); + virtual Net *makeNet(const char *name, + Instance *parent); + virtual void deleteNet(Net *net); + // connect_net + virtual void connectPin(Instance *inst, + Port *port, + Net *net); + virtual void connectPin(Instance *inst, + LibertyPort *port, + Net *net); + // disconnect_net + virtual void disconnectPin(Pin *pin); + + // Network edit before/after methods. + void makeInstanceAfter(Instance *inst); + // Not used by Sta (connectPinAfter). + void makePinAfter(Pin *pin) __attribute__ ((deprecated)); + // Replace the instance cell with to_cell. + // equivCells(from_cell, to_cell) must be true. + virtual void replaceEquivCellBefore(Instance *inst, + LibertyCell *to_cell); + virtual void replaceEquivCellAfter(Instance *inst); + // Replace the instance cell with to_cell. + // equivCellPorts(from_cell, to_cell) must be true. + virtual void replaceCellBefore(Instance *inst, + LibertyCell *to_cell); + virtual void replaceCellAfter(Instance *inst); + virtual void connectPinAfter(Pin *pin); + virtual void disconnectPinBefore(Pin *pin); + virtual void deleteNetBefore(Net *net); + virtual void deleteInstanceBefore(Instance *inst); + virtual void deletePinBefore(Pin *pin); + + //////////////////////////////////////////////////////////////// + + void setTclInterp(Tcl_Interp *interp); + Tcl_Interp *tclInterp(); + // Ensure that the timing graph has been built. + Graph *ensureGraph(); + Corner *cmdCorner() const; + void setCmdCorner(Corner *corner); + Corner *findCorner(const char *corner_name); + bool multiCorner(); + 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. + virtual void findDelays(Vertex *to_vertex); + // Find arc delays and vertex slews thru to level. + virtual void findDelays(Level level); + // Percentage (0.0:1.0) change in delay that causes downstream + // delays to be recomputed during incremental delay calculation. + // Defaults to 0.0 for maximum accuracy and slowest incremental speed. + void setIncrementalDelayTolerance(float tol); + // Make graph and find delays. + void searchPreamble(); + + // Define the delay calculator implementation. + void setArcDelayCalc(const char *delay_calc_name); + + void setDebugLevel(const char *what, + int level); + + // Delays and arrivals downsteam from inst are invalid. + void delaysInvalidFrom(Instance *inst); + // Delays and arrivals downsteam from pin are invalid. + void delaysInvalidFrom(Pin *pin); + void delaysInvalidFrom(Vertex *vertex); + // Delays to driving pins of net (fanin) are invalid. + // Arrivals downsteam from net are invalid. + void delaysInvalidFromFanin(Net *net); + void delaysInvalidFromFanin(Pin *pin); + void delaysInvalidFromFanin(Vertex *vertex); + void replaceCellPinInvalidate(LibertyPort *from_port, + Vertex *vertex, + LibertyCell *to_cell); + + // Power API. + Power *power() { return power_; } + const Power *power() const { return power_; } + void power(const Corner *corner, + // Return values. + PowerResult &total, + PowerResult &sequential, + PowerResult &combinational, + PowerResult ¯o, + PowerResult &pad); + void power(const Instance *inst, + const Corner *corner, + // Return values. + PowerResult &result); + + // Find equivalent cells in equiv_libs. + // Optionally add mappings for cells in map_libs. + void makeEquivCells(LibertyLibrarySeq *equiv_libs, + LibertyLibrarySeq *map_libs); + LibertyCellSeq *equivCells(LibertyCell *cell); + +protected: + // Default constructors that are called by makeComponents in the Sta + // constructor. These can be redefined by a derived class to + // specialize the sta components. + virtual void makeReport(); + virtual void makeDebug(); + virtual void makeUnits(); + virtual void makeNetwork(); + virtual void makeCmdNetwork(); + virtual void makeSdc(); + virtual void makeGraph(); + virtual void makeCorners(); + virtual void makeLevelize(); + virtual void makeParasitics(); + virtual void makeArcDelayCalc(); + virtual void makeGraphDelayCalc(); + virtual void makeSim(); + virtual void makeSearch(); + virtual void makeLatches(); + virtual void makeCheckTiming(); + virtual void makeCheckSlewLimits(); + virtual void makeCheckMinPulseWidths(); + virtual void makeCheckMinPeriods(); + virtual void makeCheckMaxSkews(); + virtual void makeReportPath(); + virtual void makePower(); + virtual void makeObservers(); + NetworkEdit *networkCmdEdit(); + + LibertyLibrary *readLibertyFile(const char *filename, + Corner *corner, + const MinMaxAll *min_max, + bool infer_latches, + Network *network); + // Allow external Liberty reader to parse forms not used by Sta. + virtual LibertyLibrary *readLibertyFile(const char *filename, + bool infer_latches, + Network *network); + void ensureLevelized(); + void ensureClkArrivals(); + void delayCalcPreamble(); + void delaysInvalidFrom(Port *port); + void delaysInvalidFromFanin(Port *port); + void deleteEdge(Edge *edge); + void netParasiticCaps(Net *net, + const TransRiseFall *tr, + const MinMax *min_max, + float &pin_cap, + float &wire_cap) const; + Pin *findNetParasiticDrvrPin(Net *net) const; + void exprConstantPins(FuncExpr *expr, + Instance *inst, + PinSet *pins); + Slack vertexSlack1(Vertex *vertex, + const TransRiseFall *tr, + const ClockEdge *clk_edge, + const PathAnalysisPt *path_ap); + void findRequired(Vertex *vertex); + void connectDrvrPinAfter(Vertex *vertex); + void connectLoadPinAfter(Vertex *vertex); + Path *latchEnablePath(Path *q_path, + Edge *d_q_edge, + const ClockEdge *en_clk_edge); + void clockSlewChanged(Clock *clk); + void checkSlewLimitPreamble(); + 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); + void findFaninPins(Vertex *to, + bool flat, + int inst_levels, + int pin_levels, + VertexSet &visited, + SearchPred *pred, + int inst_level, + int pin_level); + void findFanoutPins(Vertex *vertex, + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + PinSet *fanout, + SearchPred &pred); + 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 crossesHierarchy(Edge *edge) const; + void deleteLeafInstanceBefore(Instance *inst); + void readLibertyAfter(LibertyLibrary *liberty, + Corner *corner, + const MinMax *min_max); + void powerPreamble(); + void disableFanoutCrprPruning(Vertex *vertex, + int &fanou); + LibertyPort *findCellPort(LibertyCell *cell, + PortDirection *dir); + void replaceCell(Instance *inst, + Cell *to_cell, + LibertyCell *to_lib_cell); + + CmdNamespace cmd_namespace_; + Instance *current_instance_; + Corner *cmd_corner_; + CheckTiming *check_timing_; + CheckSlewLimits *check_slew_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 link_make_black_boxes_; + bool update_genclks_; + EquivCells *equiv_cells_; + + // Singleton sta used by tcl command interpreter. + static Sta *sta_; + +private: + DISALLOW_COPY_AND_ASSIGN(Sta); +}; + +} // namespace +#endif diff --git a/search/StaState.cc b/search/StaState.cc new file mode 100644 index 0000000..e4cbaed --- /dev/null +++ b/search/StaState.cc @@ -0,0 +1,116 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "Network.hh" +#include "StaState.hh" + +namespace sta { + +StaState::StaState() : + report_(nullptr), + 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), + latches_(nullptr), + thread_count_(1), + pocv_enabled_(false), + sigma_factor_(1.0) +{ +} + +StaState::StaState(const StaState *sta) : + report_(sta->report_), + debug_(sta->debug_), + units_(sta->units_), + network_(sta->network_), + sdc_network_(sta->sdc_network_), + cmd_network_(sta->cmd_network_), + sdc_(sta->sdc_), + corners_(sta->corners_), + graph_(sta->graph_), + levelize_(sta->levelize_), + parasitics_(sta->parasitics_), + arc_delay_calc_(sta->arc_delay_calc_), + graph_delay_calc_(sta->graph_delay_calc_), + sim_(sta->sim_), + search_(sta->search_), + latches_(sta->latches_), + thread_count_(sta->thread_count_), + pocv_enabled_(sta->pocv_enabled_), + sigma_factor_(sta->sigma_factor_) +{ +} + +void +StaState::copyState(const StaState *sta) +{ + report_ = sta->report_; + debug_ = sta->debug_; + units_ = sta->units_; + network_ = sta->network_; + sdc_network_ = sta->sdc_network_; + cmd_network_ = sta->cmd_network_; + sdc_ = sta->sdc_; + corners_ = sta->corners_; + graph_ = sta->graph_; + levelize_ = sta->levelize_; + parasitics_ = sta->parasitics_; + arc_delay_calc_ = sta->arc_delay_calc_; + graph_delay_calc_ = sta->graph_delay_calc_; + sim_ = sta->sim_; + search_ = sta->search_; + latches_ = sta->latches_; + thread_count_ = sta->thread_count_; + pocv_enabled_ = sta->pocv_enabled_; + sigma_factor_ = sta->sigma_factor_; +} + +NetworkEdit * +StaState::networkEdit() +{ + return dynamic_cast(network_); +} + +NetworkEdit * +StaState::networkEdit() const +{ + return dynamic_cast(network_); +} + +NetworkReader * +StaState::networkReader() +{ + return dynamic_cast(network_); +} + +NetworkReader * +StaState::networkReader() const +{ + return dynamic_cast(network_); +} + +} // namespace diff --git a/search/StaState.hh b/search/StaState.hh new file mode 100644 index 0000000..d74cdde --- /dev/null +++ b/search/StaState.hh @@ -0,0 +1,123 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_STA_STATE_H +#define STA_STA_STATE_H + +#include "DisallowCopyAssign.hh" + +namespace sta { + +class Report; +class Debug; +class Units; +class Network; +class NetworkEdit; +class NetworkReader; +class Sdc; +class Corners; +class Graph; +class Levelize; +class Sim; +class Search; +class Parasitics; +class ArcDelayCalc; +class GraphDelayCalc; +class Latches; + +// Most STA components use functionality in other components. +// This class simplifies the process of copying pointers to the +// components. It is deliberately simple to minimize circular +// dependencies between the it and the components. +class StaState +{ +public: + // Make an empty state. + StaState(); + explicit StaState(const StaState *sta); + // Copy the state from sta. This is virtual so that a component + // can notify sub-components. + virtual void copyState(const StaState *sta); + virtual ~StaState() {} + Report *report() { return report_; } + Report *report() const { return report_; } + Debug *debug() { return debug_; } + Debug *debug() const { return debug_; } + Units *units() { return units_; } + Units *units() const { return units_; } + Network *network() { return network_; } + Network *network() const { return network_; } + NetworkEdit *networkEdit(); + NetworkEdit *networkEdit() const; + NetworkReader *networkReader(); + NetworkReader *networkReader() const; + Network *sdcNetwork() { return sdc_network_; } + Network *sdcNetwork() const { return sdc_network_; } + // 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_; } + 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_; } + Latches *latches() { return latches_; } + Latches *latches() const { return latches_; } + unsigned threadCount() const { return thread_count_; } + bool pocvEnabled() const { return pocv_enabled_; } + float sigmaFactor() const { return sigma_factor_; } + +protected: + Report *report_; + Debug *debug_; + Units *units_; + Network *network_; + Network *sdc_network_; + // Network used by command interpreter (SdcNetwork). + Network *cmd_network_; + Sdc *sdc_; + Corners *corners_; + Graph *graph_; + Levelize *levelize_; + Parasitics *parasitics_; + ArcDelayCalc *arc_delay_calc_; + GraphDelayCalc *graph_delay_calc_; + Sim *sim_; + Search *search_; + Latches *latches_; + int thread_count_; + bool pocv_enabled_; + float sigma_factor_; + +private: + DISALLOW_COPY_AND_ASSIGN(StaState); +}; + +} // namespace +#endif diff --git a/search/Tag.cc b/search/Tag.cc new file mode 100644 index 0000000..06a33b3 --- /dev/null +++ b/search/Tag.cc @@ -0,0 +1,693 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Report.hh" +#include "Network.hh" +#include "Clock.hh" +#include "PortDelay.hh" +#include "ExceptionPath.hh" +#include "Sdc.hh" +#include "Graph.hh" +#include "Corner.hh" +#include "Search.hh" +#include "PathAnalysisPt.hh" +#include "ClkInfo.hh" +#include "Tag.hh" + +namespace sta { + +static int +tagStateCmp(const Tag *tag1, + const Tag *tag2); +static bool +tagStateEqual(ExceptionStateSet *states1, + ExceptionStateSet *states2); +static bool +tagStateEqualCrpr(const Tag *tag1, + const Tag *tag2); + +Tag::Tag(TagIndex index, + int tr_index, + PathAPIndex path_ap_index, + ClkInfo *clk_info, + bool is_clk, + InputDelay *input_delay, + bool is_segment_start, + ExceptionStateSet *states, + bool own_states, + const StaState *sta) : + clk_info_(clk_info), + input_delay_(input_delay), + states_(states), + is_clk_(is_clk), + is_filter_(false), + is_loop_(false), + is_segment_start_(is_segment_start), + own_states_(own_states), + index_(index), + tr_index_(tr_index), + path_ap_index_(path_ap_index) +{ + findHash(); + if (states_) { + FilterPath *filter = sta->search()->filter(); + ExceptionStateSet::ConstIterator state_iter(states_); + while (state_iter.hasNext()) { + ExceptionState *state = state_iter.next(); + ExceptionPath *exception = state->exception(); + if (exception->isLoop()) + is_loop_ = true; + if (exception == filter) + is_filter_ = true; + } + } +} + +Tag::~Tag() +{ + if (own_states_ && states_) + delete states_; +} + +const char * +Tag::asString(const StaState *sta) const +{ + return asString(true, sta); +} + +const char * +Tag::asString(bool report_index, + const StaState *sta) const +{ + const Network *network = sta->network(); + const Corners *corners = sta->corners(); + string str; + + if (report_index) + str += stringPrintTmp("%4d ", index_); + + const TransRiseFall *tr = transition(); + PathAnalysisPt *path_ap = corners->findPathAnalysisPt(path_ap_index_); + str += stringPrintTmp("%s %s/%d ", + tr->asString(), + path_ap->pathMinMax()->asString(), + path_ap_index_); + + ClockEdge *clk_edge = clkEdge(); + if (clk_edge) + str += clk_edge->name(); + else + str += "unclocked"; + + bool is_genclk_src = clk_info_->isGenClkSrcPath(); + if (is_clk_ || is_genclk_src) { + str += " ("; + if (is_clk_) { + str += "clock"; + if (clk_info_->isPropagated()) + str += " prop"; + else + str += " ideal"; + if (is_genclk_src) + str += " "; + } + if (clk_info_->isGenClkSrcPath()) + str += "genclk"; + str += ")"; + } + + const Pin *clk_src = clkSrc(); + if (clk_src) { + str += " clk_src "; + str += network->pathName(clk_src); + } + + const PathVertex crpr_clk_path(clk_info_->crprClkPath(), sta); + if (!crpr_clk_path.isNull()) { + str += " crpr_clk "; + str += crpr_clk_path.name(sta); + } + + if (input_delay_) { + str += " input "; + str += network->pathName(input_delay_->pin()); + } + + if (is_segment_start_) + str += " segment_start"; + + if (states_) { + ExceptionStateSet::ConstIterator state_iter(states_); + while (state_iter.hasNext()) { + ExceptionState *state = state_iter.next(); + ExceptionPath *exception = state->exception(); + str += " "; + str += exception->asString(network); + if (state->nextThru()) { + str += " (next thru "; + str += state->nextThru()->asString(network); + str += ")"; + } + else { + if (exception->thrus() != nullptr) + str += " (thrus complete)"; + } + } + } + + char *result = makeTmpString(str.size() + 1); + strcpy(result, str.c_str()); + return result; +} + +const TransRiseFall * +Tag::transition() const +{ + return TransRiseFall::find(tr_index_); +} + +PathAnalysisPt * +Tag::pathAnalysisPt(const StaState *sta) const +{ + const Corners *corners = sta->corners(); + return corners->findPathAnalysisPt(path_ap_index_); +} + +void +Tag::setStates(ExceptionStateSet *states) +{ + states_ = states; +} + +ClockEdge * +Tag::clkEdge() const +{ + return clk_info_->clkEdge(); +} + +Clock * +Tag::clock() const +{ + return clk_info_->clock(); +} + +const Pin * +Tag::clkSrc() const +{ + return clk_info_->clkSrc(); +} + +bool +Tag::isGenClkSrcPath() const +{ + return clk_info_->isGenClkSrcPath(); +} + +Clock * +Tag::genClkSrcPathClk(const StaState *sta) const +{ + if (clk_info_->isGenClkSrcPath() + && states_) { + FilterPath *filter = sta->search()->filter(); + ExceptionStateSet::ConstIterator state_iter(states_); + while (state_iter.hasNext()) { + ExceptionState *state = state_iter.next(); + 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); + Clock *clk = clk_iter.next(); + return clk; + } + } + } + } + } + return nullptr; +} + +void +Tag::findHash() +{ + // Common to hash_ and match_hash_. + hash_ = hash_init_value; + hashIncr(hash_, tr_index_); + hashIncr(hash_, path_ap_index_); + hashIncr(hash_, is_clk_); + hashIncr(hash_, is_segment_start_); + if (states_) { + ExceptionStateSet::Iterator state_iter(states_); + while (state_iter.hasNext()) { + ExceptionState *state = state_iter.next(); + hashIncr(hash_, state->hash()); + } + } + match_hash_ = hash_; + + // Finish hash_. + hashIncr(hash_, clk_info_->hash()); + if (input_delay_) + hashIncr(hash_, input_delay_->index()); + + // Finish match_hash_. + ClockEdge *clk_edge = clk_info_->clkEdge(); + if (clk_edge) + hashIncr(match_hash_, clk_edge->index()); + hashIncr(match_hash_, clk_info_->isGenClkSrcPath()); +} + +size_t +Tag::matchHash(bool match_crpr_clk_pin) const +{ + if (match_crpr_clk_pin) + // match_hash_ with crpr clk pin thrown in. + return hashSum(match_hash_, clk_info_->crprClkVertexIndex()); + else + return match_hash_; +} + +//////////////////////////////////////////////////////////////// + +bool +TagLess::operator()(const Tag *tag1, + const Tag *tag2) const +{ + return tagCmp(tag1, tag2, true) < 0; +} + +int +tagCmp(const Tag *tag1, + const Tag *tag2, + bool cmp_tr) +{ + if (tag1 == tag2) + return 0; + + if (cmp_tr) { + int tr_index1 = tag1->trIndex(); + int tr_index2 = tag2->trIndex(); + if (tr_index1 < tr_index2) + return -1; + if (tr_index1 > tr_index2) + return 1; + } + + 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; + + size_t clk_info1 = tag1->clkInfo()->hash(); + size_t clk_info2 = tag2->clkInfo()->hash(); + if (clk_info1 < clk_info2) + return -1; + if (clk_info1 > clk_info2) + return 1; + + bool is_clk1 = tag1->isClock(); + bool is_clk2 = tag2->isClock(); + if (!is_clk1 && is_clk2) + return -1; + if (is_clk1 && !is_clk2) + return 1; + + InputDelay *input_delay1 = tag1->inputDelay(); + InputDelay *input_delay2 = tag2->inputDelay(); + int input_delay_index1 = input_delay1 ? input_delay1->index() : 0; + int input_delay_index2 = input_delay2 ? input_delay2->index() : 0; + if (input_delay_index1 < input_delay_index2) + return -1; + if (input_delay_index1 > input_delay_index2) + return 1; + + bool is_segment_start1 = tag1->isSegmentStart(); + bool is_segment_start2 = tag2->isSegmentStart(); + if (!is_segment_start1 && is_segment_start2) + return -1; + if (is_segment_start1 && !is_segment_start2) + return 1; + + return tagStateCmp(tag1, tag2); +} + +int +tagEqual(const Tag *tag1, + const Tag *tag2) +{ + return tag1 == tag2 + || (tag1->trIndex() == tag2->trIndex() + && tag1->pathAPIndex() == tag2->pathAPIndex() + && tag1->clkInfo() == tag2->clkInfo() + && tag1->isClock() == tag2->isClock() + && tag1->inputDelay() == tag2->inputDelay() + && tag1->isSegmentStart() == tag2->isSegmentStart() + && tagStateEqual(tag1, tag2)); +} + +//////////////////////////////////////////////////////////////// + +bool +TagIndexLess::operator()(const Tag *tag1, + const Tag *tag2) const +{ + return tag1->index() < tag2->index(); +} + +//////////////////////////////////////////////////////////////// + +TagMatchLess::TagMatchLess(bool match_crpr_clk_pin, + const StaState *sta) : + match_crpr_clk_pin_(match_crpr_clk_pin), + sta_(sta) +{ +} + +bool +TagMatchLess::operator()(const Tag *tag1, + const Tag *tag2) const +{ + return tagMatchCmp(tag1, tag2, match_crpr_clk_pin_, sta_) < 0; +} + +//////////////////////////////////////////////////////////////// + +bool +tagMatch(const Tag *tag1, + const Tag *tag2, + const StaState *sta) +{ + return tagMatch(tag1, tag2, true, sta); +} + +bool +tagMatch(const Tag *tag1, + const Tag *tag2, + bool match_crpr_clk_pin, + const StaState *sta) +{ + const ClkInfo *clk_info1 = tag1->clkInfo(); + const ClkInfo *clk_info2 = tag2->clkInfo(); + return tag1 == tag2 + || (clk_info1->clkEdge() == clk_info2->clkEdge() + && tag1->trIndex() == tag2->trIndex() + && tag1->pathAPIndex() == tag2->pathAPIndex() + && tag1->isClock() == tag2->isClock() + && tag1->isSegmentStart() == tag2->isSegmentStart() + && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() + && (!match_crpr_clk_pin + || !sta->sdc()->crprActive() + || clk_info1->crprClkVertexIndex() == clk_info2->crprClkVertexIndex()) + && tagStateEqual(tag1, tag2)); +} + +int +tagMatchCmp(const Tag *tag1, + const Tag *tag2, + bool match_crpr_clk_pin, + const StaState *sta) +{ + if (tag1 == tag2) + return 0; + + int tr_index1 = tag1->trIndex(); + int tr_index2 = tag2->trIndex(); + if (tr_index1 < tr_index2) + return -1; + if (tr_index1 > tr_index2) + return 1; + + 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; + + const ClkInfo *clk_info1 = tag1->clkInfo(); + const ClkInfo *clk_info2 = tag2->clkInfo(); + const ClockEdge *clk_edge1 = clk_info1->clkEdge(); + const ClockEdge *clk_edge2 = clk_info2->clkEdge(); + int edge_index1 = clk_edge1 ? clk_edge1->index() : -1; + int edge_index2 = clk_edge2 ? clk_edge2->index() : -1; + if (edge_index1 < edge_index2) + return -1; + if (edge_index1 > edge_index2) + return 1; + + bool is_clk1 = tag1->isClock(); + bool is_clk2 = tag2->isClock(); + if (!is_clk1 && is_clk2) + return -1; + if (is_clk1 && !is_clk2) + return 1; + + bool is_genclk_src1 = clk_info1->isGenClkSrcPath(); + bool is_genclk_src2 = clk_info2->isGenClkSrcPath(); + if (!is_genclk_src1 && is_genclk_src2) + return -1; + if (is_genclk_src1 && !is_genclk_src2) + return 1; + + bool is_segment_start1 = tag1->isSegmentStart(); + bool is_segment_start2 = tag2->isSegmentStart(); + if (!is_segment_start1 && is_segment_start2) + return -1; + if (is_segment_start1 && !is_segment_start2) + return 1; + + if (match_crpr_clk_pin + && sta->sdc()->crprActive()) { + VertexIndex crpr_vertex1 = clk_info1->crprClkVertexIndex(); + VertexIndex crpr_vertex2 = clk_info2->crprClkVertexIndex(); + if (crpr_vertex1 < crpr_vertex2) + return -1; + if (crpr_vertex1 > crpr_vertex2) + return 1; + } + + return tagStateCmp(tag1, tag2); +} + +bool +tagMatchNoCrpr(const Tag *tag1, + 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->trIndex() == tag2->trIndex() + && tag1->pathAPIndex() == tag2->pathAPIndex() + && tag1->isClock() == tag2->isClock() + && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() + && tagStateEqual(tag1, tag2)); +} + +bool +tagMatchNoPathAp(const Tag *tag1, + 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->trIndex() == tag2->trIndex() + && tag1->isClock() == tag2->isClock() + && tag1->isSegmentStart() == tag2->isSegmentStart() + && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() + && tagStateEqual(tag1, tag2)); +} + +bool +tagMatchCrpr(const Tag *tag1, + 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->trIndex() == tag2->trIndex() + && tag1->isClock() == tag2->isClock() + && tag1->isSegmentStart() == tag2->isSegmentStart() + && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() + && tagStateEqualCrpr(tag1, tag2)); +} + +//////////////////////////////////////////////////////////////// + +static int +tagStateCmp(const Tag *tag1, + const Tag *tag2) +{ + ExceptionStateSet *states1 = tag1->states(); + ExceptionStateSet *states2 = tag2->states(); + bool states_null1 = (states1 == nullptr || states1->empty()); + bool states_null2 = (states2 == nullptr || states2->empty()); + if (states_null1 + && states_null2) + return 0; + if (states_null1 + && !states_null2) + return -1; + if (!states_null1 + && states_null2) + return 1; + + size_t state_size1 = states1->size(); + size_t state_size2 = states2->size(); + if (state_size1 < state_size2) + return -1; + 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 (state1 < state2) + return -1; + if (state1 > state2) + return 1; + } + return 0; +} + +bool +tagStateEqual(const Tag *tag1, + const Tag *tag2) +{ + return tagStateEqual(tag1->states(), tag2->states()); +} + +static bool +tagStateEqual(ExceptionStateSet *states1, + ExceptionStateSet *states2) +{ + bool states_null1 = (states1 == nullptr || states1->empty()); + bool states_null2 = (states2 == nullptr || states2->empty()); + if (states_null1 && states_null2) + return true; + else if (states_null1 != states_null2) + return false; + + size_t state_size1 = states1->size(); + size_t state_size2 = states2->size(); + if (state_size1 == state_size2) { + 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 (state1 != state2) + return false; + } + return true; + } + else + return false; +} + +// Match false, loop exception states only for crpr min/max paths. +static bool +tagStateEqualCrpr(const Tag *tag1, + 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->isFalse() + || exception1->isLoop()) + break; + else + state1 = nullptr; + } + state2 = nullptr; + while (state_iter2.hasNext()) { + state2 = state_iter2.next(); + ExceptionPath *exception2 = state2->exception(); + if (exception2->isFalse() + || exception2->isLoop()) + break; + else + state2 = nullptr; + } + if (state1 != state2) + return false; + } while (state1 && state2); + return state1 == nullptr + && state2 == nullptr; +} + +//////////////////////////////////////////////////////////////// + +size_t +TagHash::operator()(const Tag *tag) +{ + return tag->hash(); +} + +bool +TagEqual::operator()(const Tag *tag1, + const Tag *tag2) +{ + return tagEqual(tag1, tag2); +} + +TagMatchHash::TagMatchHash(bool match_crpr_clk_pin, + const StaState *sta) : + match_crpr_clk_pin_(match_crpr_clk_pin), + sta_(sta) +{ +} + +size_t +TagMatchHash::operator()(const Tag *tag) const +{ + return tag->matchHash(match_crpr_clk_pin_); +} + +TagMatchEqual::TagMatchEqual(bool match_crpr_clk_pin, + const StaState *sta) : + match_crpr_clk_pin_(match_crpr_clk_pin), + sta_(sta) +{ +} + +bool +TagMatchEqual::operator()(const Tag *tag1, + const Tag *tag2) const +{ + return tagMatch(tag1, tag2, match_crpr_clk_pin_, sta_); +} + +} // namespace diff --git a/search/Tag.hh b/search/Tag.hh new file mode 100644 index 0000000..ea52298 --- /dev/null +++ b/search/Tag.hh @@ -0,0 +1,168 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_TAG_H +#define STA_TAG_H + +#include "DisallowCopyAssign.hh" +#include "Vector.hh" +#include "Set.hh" +#include "SearchClass.hh" +#include "SdcClass.hh" +#include "Transition.hh" +#include "PathRef.hh" + +namespace sta { + +// Tags are used to distinguish multiple paths that hold +// arrival/required times on a vertex. +// +// Each tag corresponds to a different path on the vertex thru a +// set of exceptions. +// +// Clock paths are distinguished from non-clock paths using separate +// tags. This is because clocks pins can also have input arrivals wrt +// other clocks. +// +// When common clock reconvergence pessimism removal is enabled the +// tag ClkInfo includes the last clock driver pin so that distinct +// paths are used for paths from different sources of min/max clock +// arrivals. + +class Tag +{ +public: + Tag(TagIndex index, + int tr_index, + PathAPIndex path_ap_index, + ClkInfo *clk_info, + bool is_clk, + InputDelay *input_delay, + bool is_segment_start, + ExceptionStateSet *states, + bool own_states, + const StaState *sta); + ~Tag(); + const char *asString(const StaState *sta) const; + const char *asString(bool report_index, + const StaState *sta) const; + ClkInfo *clkInfo() const { return clk_info_; } + bool isClock() const { return is_clk_; } + ClockEdge *clkEdge() const; + Clock *clock() const; + const Pin *clkSrc() const; + int trIndex() const { return tr_index_; } + const TransRiseFall *transition() const; + PathAnalysisPt *pathAnalysisPt(const StaState *sta) const; + PathAPIndex pathAPIndex() const { return path_ap_index_; } + TagIndex index() const { return index_; } + ExceptionStateSet *states() const { return states_; } + void setStates(ExceptionStateSet *states); + bool isGenClkSrcPath() const; + Clock *genClkSrcPathClk(const StaState *sta) 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() const { return hash_; } + size_t matchHash(bool match_crpr_clk_pin) const; + +protected: + void findHash(); + +private: + DISALLOW_COPY_AND_ASSIGN(Tag); + + ClkInfo *clk_info_; + InputDelay *input_delay_; + ExceptionStateSet *states_; + size_t hash_; + size_t match_hash_; + bool is_clk_:1; + bool is_filter_:1; + bool is_loop_:1; + bool is_segment_start_:1; + // Indicates that states_ is owned by the tag. + bool own_states_:1; + TagIndex index_:tag_index_bits; + unsigned int tr_index_:TransRiseFall::index_bit_count; + unsigned int path_ap_index_:path_ap_index_bit_count; +}; + +class TagLess +{ +public: + bool operator()(const Tag *tag1, + const Tag *tag2) const; +}; + +class TagIndexLess +{ +public: + bool operator()(const Tag *tag1, + const Tag *tag2) const; +}; + +class TagHash +{ +public: + size_t operator()(const Tag *tag); +}; + +class TagEqual +{ +public: + bool operator()(const Tag *tag1, + const Tag *tag2); +}; + +int +tagCmp(const Tag *tag1, + const Tag *tag2, + bool cmp_tr); + +// Match tag clock edge, clock driver and exception states but not clk info. +bool +tagMatch(const Tag *tag1, + const Tag *tag2, + const StaState *sta); +bool +tagMatch(const Tag *tag1, + const Tag *tag2, + bool match_crpr_clk_pin, + const StaState *sta); +bool +tagStateEqual(const Tag *tag1, + const Tag *tag2); +bool +tagMatchNoCrpr(const Tag *tag1, + const Tag *tag2); +int +tagMatchCmp(const Tag *tag1, + const Tag *tag2, + bool match_clk_clk_pin, + const StaState *sta); + +bool +tagMatchNoPathAp(const Tag *tag1, + const Tag *tag2); +bool +tagMatchCrpr(const Tag *tag1, + const Tag *tag2); + +} // namespace +#endif diff --git a/search/TagGroup.cc b/search/TagGroup.cc new file mode 100644 index 0000000..7135dce --- /dev/null +++ b/search/TagGroup.cc @@ -0,0 +1,383 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Report.hh" +#include "Debug.hh" +#include "Graph.hh" +#include "PathAnalysisPt.hh" +#include "ClkInfo.hh" +#include "Tag.hh" +#include "Corner.hh" +#include "Search.hh" +#include "PathVertexRep.hh" +#include "TagGroup.hh" + +namespace sta { + +TagGroup::TagGroup(TagGroupIndex index, + ArrivalMap *arrival_map, + bool has_clk_tag, + bool has_genclk_src_tag, + bool has_filter_tag, + bool has_loop_tag) : + arrival_map_(arrival_map), + hash_(arrivalMapHash(arrival_map_)), + index_(index), + has_clk_tag_(has_clk_tag), + has_genclk_src_tag_(has_genclk_src_tag), + has_filter_tag_(has_filter_tag), + has_loop_tag_(has_loop_tag), + own_arrival_map_(true) +{ +} + +TagGroup::TagGroup(TagGroupBldr *tag_bldr) : + arrival_map_(tag_bldr->arrivalMap()), + hash_(arrivalMapHash(arrival_map_)), + own_arrival_map_(false) +{ +} + +TagGroup::~TagGroup() +{ + if (own_arrival_map_) + delete arrival_map_; +} + +size_t +TagGroup::arrivalMapHash(ArrivalMap *arrival_map) +{ + size_t hash = 0; + ArrivalMap::Iterator arrival_iter(arrival_map); + while (arrival_iter.hasNext()) { + Tag *tag; + int arrival_index; + arrival_iter.next(tag, arrival_index); + hash += tag->hash(); + } + return hash; +} + +bool +TagGroup::hasTag(Tag *tag) const +{ + return arrival_map_->hasKey(tag); +} + +void +TagGroup::arrivalIndex(Tag *tag, + int &arrival_index, + bool &exists) const +{ + arrival_map_->findKey(tag, arrival_index, exists); +} + +void +TagGroup::requiredIndex(Tag *tag, + int &req_index, + bool &exists) const +{ + arrivalIndex(tag, req_index, exists); + if (exists) + req_index += arrivalCount(); +} + +int +TagGroup::requiredIndex(int arrival_index) const +{ + return arrival_index + arrivalCount(); +} + +void +TagGroup::report(const StaState *sta) const +{ + Report *report = sta->report(); + report->print("Group %u hash = %u\n", index_, hash_); + arrivalMapReport(arrival_map_, sta); +} + +void +TagGroup::reportArrivalMap(const StaState *sta) const +{ + arrivalMapReport(arrival_map_, sta); +} + +void +arrivalMapReport(const ArrivalMap *arrival_map, + const StaState *sta) +{ + Report *report = sta->report(); + ArrivalMap::ConstIterator arrival_iter(arrival_map); + while (arrival_iter.hasNext()) { + Tag *tag; + int arrival_index; + arrival_iter.next(tag, arrival_index); + report->print(" %2u %s\n", + arrival_index, + tag->asString(sta)); + } + report->print("\n"); +} + +//////////////////////////////////////////////////////////////// + +TagGroupBldr::TagGroupBldr(bool match_crpr_clk_pin, + const StaState *sta) : + default_arrival_count_(sta->corners()->count() + * TransRiseFall::index_count + * MinMax::index_count), + arrival_map_(default_arrival_count_, + TagMatchHash(match_crpr_clk_pin, sta), + TagMatchEqual(match_crpr_clk_pin, sta)), + arrivals_(default_arrival_count_), + prev_paths_(default_arrival_count_), + has_clk_tag_(false), + has_genclk_src_tag_(false), + has_filter_tag_(false), + has_loop_tag_(false), + sta_(sta) +{ +} + +bool +TagGroupBldr::empty() +{ + return arrival_map_.empty(); +} + +void +TagGroupBldr::init(Vertex *vertex) +{ + vertex_ = vertex; + arrival_map_.clear(); + arrivals_.clear(); + prev_paths_.clear(); + has_clk_tag_ = false; + has_genclk_src_tag_ = false; + has_filter_tag_ = false; + has_loop_tag_ = false; +} + +void +TagGroupBldr::reportArrivalEntries() const +{ + arrivalMapReport(&arrival_map_, sta_); +} + +void +TagGroupBldr::tagArrival(Tag *tag, + // Return values. + Arrival &arrival, + int &arrival_index, + bool &exists) const +{ + arrival_map_.findKey(tag, arrival_index, exists); + if (exists) + arrival = arrivals_[arrival_index]; +} + +void +TagGroupBldr::tagArrival(Tag *tag, + // Return values. + Arrival &arrival, + bool &exists) const +{ + int arrival_index; + arrival_map_.findKey(tag, arrival_index, exists); + if (exists) + arrival = arrivals_[arrival_index]; +} + +void +TagGroupBldr::tagMatchArrival(Tag *tag, + // Return values. + Tag *&tag_match, + Arrival &arrival, + int &arrival_index) const +{ + // Find matching group tag. + // Match is not necessarily equal to original tag because it + // must only satisfy tagMatch. + bool exists; + arrival_map_.findKey(tag, tag_match, arrival_index, exists); + if (exists) + arrival = arrivals_[arrival_index]; + else { + tag_match = nullptr; + arrival = -1.0; + arrival_index = -1; + } +} + +Arrival +TagGroupBldr::arrival(int arrival_index) const +{ + return arrivals_[arrival_index]; +} + +void +TagGroupBldr::setArrival(Tag *tag, + const Arrival &arrival, + PathVertexRep *prev_path) +{ + Tag *tag_match; + Arrival ignore; + int arrival_index; + // Find matching group tag (not necessarily equal to original tag). + tagMatchArrival(tag, tag_match, ignore, arrival_index); + setMatchArrival(tag, tag_match, arrival, arrival_index, prev_path); +} + +void +TagGroupBldr::setMatchArrival(Tag *tag, + Tag *tag_match, + const Arrival &arrival, + int arrival_index, + PathVertexRep *prev_path) +{ + if (tag_match) { + // If the group_tag exists there has to be an arrival map entry for it. + if (tag_match != tag) { + // Replace tag in arrival map. + arrival_map_.erase(tag_match); + arrival_map_.insert(tag, arrival_index); + } + arrivals_[arrival_index] = arrival; + prev_paths_[arrival_index].init(prev_path); + } + else { + arrival_index = arrivals_.size(); + arrival_map_.insert(tag, arrival_index); + arrivals_.push_back(arrival); + if (prev_path) + prev_paths_.push_back(*prev_path); + else + prev_paths_.push_back(PathVertexRep()); + + if (tag->isClock()) + has_clk_tag_ = true; + if (tag->isGenClkSrcPath()) + has_genclk_src_tag_ = true; + if (tag->isFilter() + || tag->clkInfo()->refsFilter(sta_)) + has_filter_tag_ = true; + if (tag->isLoop()) + has_loop_tag_ = true; + } +} + +void +TagGroupBldr::deleteArrival(Tag *tag) +{ + arrival_map_.erase(tag); +} + +TagGroup * +TagGroupBldr::makeTagGroup(TagGroupIndex index, + const StaState *sta) +{ + return new TagGroup(index, makeArrivalMap(sta), + has_clk_tag_, has_genclk_src_tag_, has_filter_tag_, + has_loop_tag_); + +} + +ArrivalMap * +TagGroupBldr::makeArrivalMap(const StaState *sta) +{ + ArrivalMap *arrival_map = new ArrivalMap(arrival_map_.size(), + TagMatchHash(true, sta), + TagMatchEqual(true, sta)); + int arrival_index = 0; + ArrivalMap::Iterator arrival_iter(arrival_map_); + while (arrival_iter.hasNext()) { + Tag *tag; + int arrival_index1; + arrival_iter.next(tag, arrival_index1); + arrival_map->insert(tag, arrival_index); + arrival_index++; + } + return arrival_map; +} + +void +TagGroupBldr::copyArrivals(TagGroup *tag_group, + Arrival *arrivals, + PathVertexRep *prev_paths) +{ + ArrivalMap::Iterator arrival_iter1(arrival_map_); + while (arrival_iter1.hasNext()) { + Tag *tag1; + int arrival_index1, arrival_index2; + arrival_iter1.next(tag1, arrival_index1); + bool exists2; + tag_group->arrivalIndex(tag1, arrival_index2, exists2); + if (!exists2) + internalError("tag group missing tag"); + arrivals[arrival_index2] = arrivals_[arrival_index1]; + if (prev_paths) { + PathVertexRep *prev_path = &prev_paths_[arrival_index1]; + prev_paths[arrival_index2].init(prev_path); + } + } +} + +//////////////////////////////////////////////////////////////// + +size_t +TagGroupHash::operator()(const TagGroup *group) const +{ + return group->hash(); +} + +static bool +arrivalMapEqual(const ArrivalMap *arrival_map1, + const ArrivalMap *arrival_map2) +{ + int arrival_count1 = arrival_map1->size(); + int arrival_count2 = arrival_map2->size(); + if (arrival_count1 == arrival_count2) { + ArrivalMap::ConstIterator arrival_iter1(arrival_map1); + while (arrival_iter1.hasNext()) { + Tag *tag1, *tag2; + int arrival_index1, arrival_index2; + arrival_iter1.next(tag1, arrival_index1); + bool exists2; + arrival_map2->findKey(tag1, tag2, arrival_index2, exists2); + if (!exists2 + // ArrivalMap equal function is TagMatchEqual, so make sure + // the tag is an exact match. + || tag2 != tag1) + return false; + } + return true; + } + else + return false; +} + +bool +TagGroupEqual::operator()(const TagGroup *tag_group1, + const TagGroup *tag_group2) const +{ + return tag_group1 == tag_group2 + || (tag_group1->hash() == tag_group2->hash() + && arrivalMapEqual(tag_group1->arrivalMap(), + tag_group2->arrivalMap())); +} + +} // namespace diff --git a/search/TagGroup.hh b/search/TagGroup.hh new file mode 100644 index 0000000..c528574 --- /dev/null +++ b/search/TagGroup.hh @@ -0,0 +1,167 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_TAG_GROUP_H +#define STA_TAG_GROUP_H + +#include "DisallowCopyAssign.hh" +#include "Vector.hh" +#include "Map.hh" +#include "Iterator.hh" +#include "MinMax.hh" +#include "Transition.hh" +#include "GraphClass.hh" +#include "SearchClass.hh" +#include "Tag.hh" + +namespace sta { + +class TagGroupBldr; + +typedef Vector PathVertexRepSeq; + +class TagGroup +{ +public: + TagGroup(TagGroupIndex index, + ArrivalMap *arrival_map, + bool has_clk_tag, + bool has_genclk_src_tag, + bool has_filter_tag, + bool has_loop_tag); + // For Search::findTagGroup to probe. + TagGroup(TagGroupBldr *tag_bldr); + ~TagGroup(); + TagGroupIndex index() const { return index_; } + size_t hash() const { return hash_; } + void report(const StaState *sta) const; + void reportArrivalMap(const StaState *sta) const; + bool hasClkTag() const { return has_clk_tag_; } + bool hasGenClkSrcTag() const { return has_genclk_src_tag_; } + bool hasFilterTag() const { return has_filter_tag_; } + bool hasLoopTag() const { return has_loop_tag_; } + bool ownArrivalMap() const { return own_arrival_map_; } + int arrivalCount() const { return arrival_map_->size(); } + void arrivalIndex(Tag *tag, + int &arrival_index, + bool &exists) const; + void requiredIndex(Tag *tag, + int &req_index, + bool &exists) const; + int requiredIndex(int arrival_index) const; + ArrivalMap *arrivalMap() const { return arrival_map_; } + bool hasTag(Tag *tag) const; + +protected: + size_t arrivalMapHash(ArrivalMap *arrival_map); + + // tag -> arrival index + ArrivalMap *arrival_map_; + size_t hash_; + unsigned int index_:tag_group_index_bits; + unsigned int has_clk_tag_:1; + unsigned int has_genclk_src_tag_:1; + unsigned int has_filter_tag_:1; + unsigned int has_loop_tag_:1; + unsigned int own_arrival_map_:1; + +private: + DISALLOW_COPY_AND_ASSIGN(TagGroup); +}; + +class TagGroupHash +{ +public: + size_t operator()(const TagGroup *tag) const; +}; + +class TagGroupEqual +{ +public: + bool operator()(const TagGroup *group1, + const TagGroup *group2) const; +}; + +// Incremental tag group used to build tag group and associated +// arrivals. +class TagGroupBldr +{ +public: + TagGroupBldr(bool match_crpr_clk_pin, + const StaState *sta); + void init(Vertex *vertex); + bool empty(); + void reportArrivalEntries() const; + TagGroup *makeTagGroup(TagGroupIndex index, + const StaState *sta); + bool hasClkTag() const { return has_clk_tag_; } + bool hasGenClkSrcTag() const { return has_genclk_src_tag_; } + bool hasFilterTag() const { return has_filter_tag_; } + bool hasLoopTag() const { return has_loop_tag_; } + void deleteArrival(Tag *tag); + void tagArrival(Tag *tag, + // Return values. + Arrival &arrival, + bool &exists) const; + void tagArrival(Tag *tag, + // Return values. + Arrival &arrival, + int &arrival_index, + bool &exists) const; + void tagMatchArrival(Tag *tag, + // Return values. + Tag *&tag_match, + Arrival &arrival, + int &arrival_index) const; + Arrival arrival(int arrival_index) const; + void setArrival(Tag *tag, + const Arrival &arrival, + PathVertexRep *prev_path); + void setMatchArrival(Tag *tag, + Tag *tag_match, + const Arrival &arrival, + int arrival_index, + PathVertexRep *prev_path); + ArrivalMap *arrivalMap() { return &arrival_map_; } + void copyArrivals(TagGroup *tag_group, + Arrival *arrivals, + PathVertexRep *prev_paths); + +protected: + int tagMatchIndex(); + ArrivalMap *makeArrivalMap(const StaState *sta); + + Vertex *vertex_; + int default_arrival_count_; + ArrivalMap arrival_map_; + ArrivalSeq arrivals_; + PathVertexRepSeq prev_paths_; + bool has_clk_tag_; + bool has_genclk_src_tag_:1; + bool has_filter_tag_; + bool has_loop_tag_; + const StaState *sta_; + +private: + DISALLOW_COPY_AND_ASSIGN(TagGroupBldr); +}; + +void +arrivalMapReport(const ArrivalMap *arrival_map, + const StaState *sta); + +} // namespace +#endif diff --git a/search/VertexVisitor.cc b/search/VertexVisitor.cc new file mode 100644 index 0000000..e82e14b --- /dev/null +++ b/search/VertexVisitor.cc @@ -0,0 +1,42 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Error.hh" +#include "Graph.hh" +#include "VertexVisitor.hh" + +namespace sta { + +VertexPinCollector::VertexPinCollector(PinSet *pins) : + pins_(pins) +{ +} + +VertexVisitor * +VertexPinCollector::copy() +{ + internalError("VertexPinCollector::copy not supported."); + return nullptr; +} + +void +VertexPinCollector::visit(Vertex *vertex) +{ + pins_->insert(vertex->pin()); +} + +} // namespace diff --git a/search/VertexVisitor.hh b/search/VertexVisitor.hh new file mode 100644 index 0000000..267e33a --- /dev/null +++ b/search/VertexVisitor.hh @@ -0,0 +1,55 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_VERTEX_VISITOR_H +#define STA_VERTEX_VISITOR_H + +#include "DisallowCopyAssign.hh" +#include "NetworkClass.hh" +#include "GraphClass.hh" + +namespace sta { + +// Abstract base class for visiting a vertex. +class VertexVisitor +{ +public: + VertexVisitor() {} + virtual ~VertexVisitor() {} + virtual VertexVisitor *copy() = 0; + virtual void visit(Vertex *vertex) = 0; + void operator()(Vertex *vertex) { visit(vertex); } + virtual void levelFinished() {} + +private: + DISALLOW_COPY_AND_ASSIGN(VertexVisitor); +}; + +// Collect visited pins into a PinSet. +class VertexPinCollector : public VertexVisitor +{ +public: + VertexPinCollector(PinSet *pins); + PinSet *pins() const { return pins_; } + void visit(Vertex *vertex); + virtual VertexVisitor *copy(); + +protected: + PinSet *pins_; +}; + +} // namespace +#endif diff --git a/search/VisitPathEnds.cc b/search/VisitPathEnds.cc new file mode 100644 index 0000000..df82185 --- /dev/null +++ b/search/VisitPathEnds.cc @@ -0,0 +1,639 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Debug.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "ExceptionPath.hh" +#include "PortDelay.hh" +#include "Sdc.hh" +#include "TimingArc.hh" +#include "Graph.hh" +#include "ClkInfo.hh" +#include "Tag.hh" +#include "PathVertex.hh" +#include "PathAnalysisPt.hh" +#include "PathEnd.hh" +#include "Search.hh" +#include "GatedClk.hh" +#include "VisitPathEnds.hh" + +namespace sta { + +VisitPathEnds::VisitPathEnds(const StaState *sta) : + StaState(sta) +{ +} + +void +VisitPathEnds::visitPathEnds(Vertex *vertex, + PathEndVisitor *visitor) +{ + visitPathEnds(vertex, nullptr, MinMaxAll::all(), false, visitor); +} + +void +VisitPathEnds::visitPathEnds(Vertex *vertex, + const Corner *corner, + 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(); + debugPrint1(debug_, "search", 2, "find end slack %s\n", + vertex->name(sdc_network_)); + visitor->vertexBegin(vertex); + bool is_constrained = false; + visitClkedPathEnds(pin, vertex, corner, min_max, filtered, visitor, + is_constrained); + if (search_->unconstrainedPaths() + && !is_constrained + && !vertex->isDisabledConstraint()) + visitUnconstrainedPathEnds(pin, vertex, corner, 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) +{ + bool is_segment_start = search_->isSegmentStart(pin); + VertexPathIterator path_iter(vertex, this); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + PathAnalysisPt *path_ap = path->pathAnalysisPt(this); + const MinMax *path_min_max = path_ap->pathMinMax(); + const TransRiseFall *end_tr = 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_tr, path_min_max) + // Ignore segment startpoint paths. + && (!is_segment_start + || !tag->isSegmentStart())) { + // set_output_delay to timing check has precidence. + if (sdc_->hasOutputDelay(pin)) + visitOutputDelayEnd(pin, path, end_tr, path_ap, filtered, visitor, + is_constrained); + else if (vertex->hasChecks()) + visitCheckEnd(pin, vertex, path, end_tr, path_ap, filtered, visitor, + is_constrained); + else if (!sdc_->exceptionToInvalid(pin) + && (!filtered + || search_->matchesFilter(path, nullptr))) { + PathDelay *path_delay = pathDelayTo(path, pin, end_tr, path_min_max); + if (path_delay) { + PathEndPathDelay path_end(path_delay, path, this); + visitor->visit(&path_end); + is_constrained = true; + } + } + if (sdc_->gatedClkChecksEnabled()) + visitGatedClkEnd(pin, vertex, path, end_tr, path_ap, filtered, visitor, + is_constrained); + visitDataCheckEnd(pin, path, end_tr, path_ap, filtered, visitor, + is_constrained); + } + } +} + +void +VisitPathEnds::visitCheckEnd(const Pin *pin, + Vertex *vertex, + Path *path, + const TransRiseFall *end_tr, + const PathAnalysisPt *path_ap, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) +{ + ClockEdge *src_clk_edge = path->clkEdge(this); + Clock *src_clk = path->clock(this); + const MinMax *min_max = path_ap->pathMinMax(); + const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt(); + bool check_clked = false; + 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) { + TimingArcSet *arc_set = edge->timingArcSet(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *check_arc = arc_iter.next(); + TransRiseFall *clk_tr = check_arc->fromTrans()->asRiseFall(); + if (check_arc->toTrans()->asRiseFall() == end_tr + && clk_tr) { + VertexPathIterator tgt_clk_path_iter(tgt_clk_vertex, clk_tr, + tgt_clk_path_ap, this); + while (tgt_clk_path_iter.hasNext()) { + PathVertex *tgt_clk_path = tgt_clk_path_iter.next(); + 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_tr, + tgt_clk_edge, min_max); + // Ignore generated clock source paths. + if (!tgt_clk_info->isGenClkSrcPath() + && !search_->pathPropagatedToClkSrc(tgt_pin, tgt_clk_path)) { + if (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) + && (search_->checkDefaultArrivalPaths() + || src_clk_edge + != sdc_->defaultArrivalClockEdge()) + // False paths and path delays override + // paths. + && (exception == nullptr + || exception->isFilter() + || exception->isGroupPath() + || exception->isMultiCycle())) { + MultiCyclePath *mcp=dynamic_cast(exception); + if (network_->isLatchData(pin) + && check_role == TimingRole::setup()) { + PathEndLatchCheck path_end(path, check_arc, edge, + tgt_clk_path, mcp, nullptr, + this); + visitor->visit(&path_end); + is_constrained = true; + } + else { + PathEndCheck path_end(path, check_arc, edge, + tgt_clk_path, mcp, this); + visitor->visit(&path_end); + is_constrained = true; + } + } + else if (exception + && exception->isPathDelay() + && (src_clk == nullptr + || sdc_->sameClockGroup(src_clk, + tgt_clk))) { + PathDelay *path_delay = dynamic_cast(exception); + if (network_->isLatchData(pin) + && check_role == TimingRole::setup()) { + PathEndLatchCheck path_end(path, check_arc, edge, + tgt_clk_path, nullptr, + path_delay, this); + visitor->visit(&path_end); + } + else { + PathEndPathDelay path_end(path_delay, path, tgt_clk_path, + check_arc, edge, this); + visitor->visit(&path_end); + is_constrained = true; + } + } + } + } + } + } + } + } + } + } + if (!check_clked + && !sdc_->exceptionToInvalid(pin)) + visitCheckEndUnclked(pin, vertex, path, end_tr, path_ap, filtered, + visitor, is_constrained); +} + +void +VisitPathEnds::visitCheckEndUnclked(const Pin *pin, + Vertex *vertex, + Path *path, + const TransRiseFall *end_tr, + const PathAnalysisPt *path_ap, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) +{ + const MinMax *min_max = path_ap->pathMinMax(); + 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) { + TimingArcSet *arc_set = edge->timingArcSet(); + TimingArcSetArcIterator arc_iter(arc_set); + while (arc_iter.hasNext()) { + TimingArc *check_arc = arc_iter.next(); + TransRiseFall *clk_tr = check_arc->fromTrans()->asRiseFall(); + if (check_arc->toTrans()->asRiseFall() == end_tr + && clk_tr + && (!filtered + || search_->matchesFilter(path, nullptr))) { + ExceptionPath *exception = exceptionTo(path, pin, end_tr, + 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 +{ + const TimingRole *check_role = edge->role(); + return check_role->isTimingCheck() + && search_->evalPred()->searchFrom(edge->from(graph_)) + && !edge->isDisabledConstraint() + && !edge->isDisabledCond() + && !sdc_->isDisabledCondDefault(edge) + && !((check_role == TimingRole::recovery() + || check_role == TimingRole::removal()) + && !sdc_->recoveryRemovalChecksEnabled()); +} + +void +VisitPathEnds::visitOutputDelayEnd(const Pin *pin, + Path *path, + const TransRiseFall *end_tr, + const PathAnalysisPt *path_ap, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) +{ + const MinMax *min_max = path_ap->pathMinMax(); + VertexPinOutputDelayIterator delay_iter(pin, sdc_); + while (delay_iter.hasNext()) { + OutputDelay *output_delay = delay_iter.next(); + float margin; + bool exists; + output_delay->delays()->value(end_tr, min_max, margin, exists); + if (exists) { + const Pin *ref_pin = output_delay->refPin(); + 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); + TransRiseFall *ref_tr = output_delay->refTransition(); + VertexPathIterator ref_path_iter(ref_vertex,ref_tr,path_ap,this); + while (ref_path_iter.hasNext()) { + PathVertex *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_tr, + ref_path->clkEdge(this), ref_path, min_max, + visitor, is_constrained); + } + } + else + visitOutputDelayEnd1(output_delay, pin, path, end_tr, + tgt_clk_edge, nullptr, min_max, + visitor, is_constrained); + } + } + } +} + +void +VisitPathEnds::visitOutputDelayEnd1(OutputDelay *output_delay, + const Pin *pin, + Path *path, + const TransRiseFall *end_tr, + const ClockEdge *tgt_clk_edge, + PathVertex *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_tr, tgt_clk_edge, + min_max); + if (exception + && exception->isPathDelay()) { + PathDelay *path_delay = dynamic_cast(exception); + PathEndPathDelay path_end(path_delay, path, output_delay, this); + visitor->visit(&path_end); + is_constrained = true; + } + else if (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())) { + MultiCyclePath *mcp = dynamic_cast(exception); + PathEndOutputDelay path_end(output_delay, path, ref_path, mcp, this); + visitor->visit(&path_end); + is_constrained = true; + } +} + +//////////////////////////////////////////////////////////////// + +// Look for clock gating functions where path is the clock enable. +void +VisitPathEnds::visitGatedClkEnd(const Pin *pin, + Vertex *vertex, + Path *path, + const TransRiseFall *end_tr, + const PathAnalysisPt *path_ap, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) +{ + ClockEdge *src_clk_edge = path->clkEdge(this); + if (src_clk_edge) { + 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); + if (is_gated_clk_enable) { + const PathAnalysisPt *clk_path_ap = path_ap->tgtClkAnalysisPt(); + const MinMax *min_max = path_ap->pathMinMax(); + Vertex *clk_vertex = graph_->pinLoadVertex(clk_pin); + LogicValue active_value = + sdc_->clockGatingActiveValue(clk_pin, pin); + TransRiseFall *clk_tr = + // 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_tr, clk_path_ap, this); + while (clk_path_iter.hasNext()) { + PathVertex *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() + && !search_->pathPropagatedToClkSrc(clk_pin, clk_path) + && !sdc_->clkStopPropagation(pin, clk) + && clk_vertex->hasDownstreamClkPin()) { + TimingRole *check_role = (min_max == MinMax::max()) + ? TimingRole::gatedClockSetup() + : TimingRole::gatedClockHold(); + float margin = clockGatingMargin(clk, clk_pin, + pin, end_tr, min_max); + ExceptionPath *exception = exceptionTo(path, pin, end_tr, + 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; + } + } + } + } + } +} + +// Gated clock setup/hold margin respecting precedence rules. +// Look for margin from highest precedence level to lowest. +float +VisitPathEnds::clockGatingMargin(const Clock *clk, + const Pin *clk_pin, + const Pin *enable_pin, + const TransRiseFall *enable_tr, + const SetupHold *setup_hold) +{ + bool exists; + float margin; + sdc_->clockGatingMarginEnablePin(enable_pin, enable_tr, + setup_hold, exists, margin); + if (exists) + return margin; + Instance *inst = network_->instance(enable_pin); + sdc_->clockGatingMarginInstance(inst, enable_tr, setup_hold, + exists, margin); + if (exists) + return margin; + sdc_->clockGatingMarginClkPin(clk_pin, enable_tr, setup_hold, + exists, margin); + if (exists) + return margin; + sdc_->clockGatingMarginClk(clk, enable_tr, setup_hold, + exists, margin); + if (exists) + return margin; + sdc_->clockGatingMargin(enable_tr, setup_hold, + exists, margin); + if (exists) + return margin; + else + return 0.0; +} + +//////////////////////////////////////////////////////////////// + +void +VisitPathEnds::visitDataCheckEnd(const Pin *pin, + Path *path, + const TransRiseFall *end_tr, + const PathAnalysisPt *path_ap, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) +{ + ClockEdge *src_clk_edge = path->clkEdge(this); + if (src_clk_edge) { + 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_tr : TransRiseFall::range()) { + float margin; + bool margin_exists; + check->margin(from_tr, end_tr, min_max, margin, margin_exists); + if (margin_exists) + visitDataCheckEnd1(check, pin, path, src_clk, end_tr, + min_max, clk_ap, from_pin, from_vertex, + from_tr, filtered, visitor, is_constrained); + } + } + } + } +} + +bool +VisitPathEnds::visitDataCheckEnd1(DataCheck *check, + const Pin *pin, + Path *path, + const Clock *src_clk, + const TransRiseFall *end_tr, + const MinMax *min_max, + const PathAnalysisPt *clk_ap, + const Pin *from_pin, + Vertex *from_vertex, + TransRiseFall *from_tr, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained) +{ + bool found_from_path = false; + VertexPathIterator tgt_clk_path_iter(from_vertex,from_tr,clk_ap,this); + while (tgt_clk_path_iter.hasNext()) { + PathVertex *tgt_clk_path = tgt_clk_path_iter.next(); + const ClockEdge *tgt_clk_edge = tgt_clk_path->clkEdge(this); + const Clock *tgt_clk = tgt_clk_edge ? tgt_clk_edge->clock() : nullptr; + ExceptionPath *exception = exceptionTo(path, pin, end_tr, + tgt_clk_edge, min_max); + // Ignore generated clock source paths. + if (!tgt_clk_path->clkInfo(this)->isGenClkSrcPath() + && !search_->pathPropagatedToClkSrc(from_pin, tgt_clk_path)) { + found_from_path = true; + 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))) { + // No mcp for data checks. + PathEndDataCheck path_end(check, path, tgt_clk_path, nullptr, this); + visitor->visit(&path_end); + is_constrained = true; + } + } + } + return found_from_path; +} + +//////////////////////////////////////////////////////////////// + +void +VisitPathEnds::visitUnconstrainedPathEnds(const Pin *pin, + Vertex *vertex, + const Corner *corner, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor) +{ + VertexPathIterator path_iter(vertex, this); + while (path_iter.hasNext()) { + PathVertex *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() + && !search_->pathPropagatedToClkSrc(pin, path) + && (!filtered + || search_->matchesFilter(path, nullptr)) + && !falsePathTo(path, pin, path->transition(this), + path->minMax(this))) { + PathEndUnconstrained path_end(path); + visitor->visit(&path_end); + } + } +} + +//////////////////////////////////////////////////////////////// + +bool +VisitPathEnds::falsePathTo(Path *path, + const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max) +{ + ExceptionPath *exception = search_->exceptionTo(ExceptionPathType::false_path, path, + pin, tr, nullptr, min_max, + false, false); + return exception != nullptr; +} + +PathDelay * +VisitPathEnds::pathDelayTo(Path *path, + const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max) +{ + ExceptionPath *exception = search_->exceptionTo(ExceptionPathType::path_delay, + path, pin, tr, nullptr, + min_max, false, + // Register clk pins only + // match with -to pin. + network_->isRegClkPin(pin)); + return dynamic_cast(exception); +} + +ExceptionPath * +VisitPathEnds::exceptionTo(const Path *path, + const Pin *pin, + const TransRiseFall *tr, + const ClockEdge *clk_edge, + const MinMax *min_max) const +{ + return search_->exceptionTo(ExceptionPathType::any, path, pin, tr, clk_edge, + min_max, false, false); +} + +} // namespace diff --git a/search/VisitPathEnds.hh b/search/VisitPathEnds.hh new file mode 100644 index 0000000..9a9cb7b --- /dev/null +++ b/search/VisitPathEnds.hh @@ -0,0 +1,153 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_VISIT_PATH_ENDS_H +#define STA_VISIT_PATH_ENDS_H + +#include "SdcClass.hh" +#include "GraphClass.hh" +#include "SearchClass.hh" + +namespace sta { + +class StaState; +class PathEndVisitor; + +class VisitPathEnds : public StaState +{ +public: + VisitPathEnds(const StaState *sta); + // All corners, unfiltered. + void visitPathEnds(Vertex *vertex, + PathEndVisitor *visitor); + // Use corner nullptr to visit PathEnds for all corners. + void visitPathEnds(Vertex *vertex, + const Corner *corner, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor); + bool checkEdgeEnabled(Edge *edge) const; + +protected: + void visitClkedPathEnds(const Pin *pin, + Vertex *vertex, + const Corner *corner, + const MinMaxAll *min_max, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); + virtual void visitCheckEnd(const Pin *pin, + Vertex *vertex, + Path *path, + const TransRiseFall *end_tr, + const PathAnalysisPt *path_ap, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); + void visitCheckEndUnclked(const Pin *pin, + Vertex *vertex, + Path *path, + const TransRiseFall *end_tr, + const PathAnalysisPt *path_ap, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); + void visitOutputDelayEnd(const Pin *pin, + Path *path, + const TransRiseFall *end_tr, + const PathAnalysisPt *path_ap, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); + virtual void visitOutputDelayEnd1(OutputDelay *output_delay, + const Pin *pin, + Path *path, + const TransRiseFall *end_tr, + const ClockEdge *tgt_clk_edge, + PathVertex *ref_path, + const MinMax *min_max, + PathEndVisitor *visitor, + bool &is_constrained); + virtual void visitGatedClkEnd(const Pin *pin, + Vertex *vertex, + Path *path, + const TransRiseFall *end_tr, + const PathAnalysisPt *path_ap, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); + float clockGatingMargin(const Clock *clk, + const Pin *clk_pin, + const Pin *enable_pin, + const TransRiseFall *enable_tr, + const SetupHold *setup_hold); + void visitDataCheckEnd(const Pin *pin, + Path *path, + const TransRiseFall *end_tr, + const PathAnalysisPt *path_ap, + bool filtered, + PathEndVisitor *visitor, + bool &is_constrained); + bool visitDataCheckEnd1(DataCheck *check, + const Pin *pin, + Path *path, + const Clock *src_clk, + const TransRiseFall *end_tr, + const MinMax *min_max, + const PathAnalysisPt *clk_ap, + const Pin *from_pin, + Vertex *from_vertex, + TransRiseFall *from_tr, + 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); + bool falsePathTo(Path *path, + const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max); + PathDelay *pathDelayTo(Path *path, + const Pin *pin, + const TransRiseFall *tr, + const MinMax *min_max); + ExceptionPath *exceptionTo(const Path *path, + const Pin *pin, + const TransRiseFall *tr, + 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 *copy() = 0; + // Begin visiting the path ends for a vertex / path_index. + virtual void vertexBegin(Vertex *) {} + // Visit a path end. path_end is only valid during the call. + virtual void visit(PathEnd *path_end) = 0; + // End visiting the path ends for a vertex / path_index. + virtual void vertexEnd(Vertex *) {} +}; + +} // namespace +#endif diff --git a/search/VisitPathGroupVertices.cc b/search/VisitPathGroupVertices.cc new file mode 100644 index 0000000..fae65a7 --- /dev/null +++ b/search/VisitPathGroupVertices.cc @@ -0,0 +1,321 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Debug.hh" +#include "Graph.hh" +#include "Bfs.hh" +#include "Search.hh" +#include "PathVertex.hh" +#include "PathEnd.hh" +#include "Tag.hh" +#include "VisitPathEnds.hh" +#include "VisitPathGroupVertices.hh" + +namespace sta { + +typedef Set PathVertexSet; +typedef Map VertexPathSetMap; + +static void +vertexPathSetMapInsertPath(VertexPathSetMap *matching_path_map, + Vertex *vertex, + Tag *tag, + int arrival_index, + const StaState *sta); + +// Visit each path end for a vertex and add the worst one in each +// path group to the group. +class VisitPathGroupEnds : public PathEndVisitor +{ +public: + explicit VisitPathGroupEnds(PathGroup *path_group, + VertexVisitor *vertex_visitor, + VertexPathSetMap *matching_path_map, + BfsBkwdIterator *bkwd_iter, + StaState *sta); + virtual PathEndVisitor *copy(); + virtual void visit(PathEnd *path_end); + virtual void vertexBegin(Vertex *vertex); + virtual void vertexEnd(Vertex *vertex); + +private: + DISALLOW_COPY_AND_ASSIGN(VisitPathGroupEnds); + + PathGroup *path_group_; + VertexVisitor *vertex_visitor_; + BfsBkwdIterator *bkwd_iter_; + VertexPathSetMap *matching_path_map_; + bool vertex_matches_; + StaState *sta_; +}; + +class PathGroupPathVisitor : public PathVisitor +{ +public: + PathGroupPathVisitor(VertexVisitor *visitor, + BfsBkwdIterator *bkwd_iter, + VertexPathSetMap *matching_path_map, + const StaState *sta); + virtual ~PathGroupPathVisitor(); + virtual VertexVisitor *copy(); + virtual void visit(Vertex *vertex); + +protected: + // Return false to stop visiting. + virtual bool visitFromToPath(const Pin *from_pin, + Vertex *from_vertex, + const TransRiseFall *from_tr, + Tag *from_tag, + PathVertex *from_path, + Edge *edge, + TimingArc *arc, + ArcDelay arc_delay, + Vertex *to_vertex, + const TransRiseFall *to_tr, + Tag *to_tag, + Arrival &to_arrival, + const MinMax *min_max, + const PathAnalysisPt *path_ap); + void fromMatches(Vertex *from_vertex, + Tag *from_tag, + int from_arrival_index); + +private: + VertexVisitor *visitor_; + BfsBkwdIterator *bkwd_iter_; + VertexPathSetMap *matching_path_map_; + bool vertex_matches_; +}; + +//////////////////////////////////////////////////////////////// + +// Visit the fanin vertices for the path group. +// Vertices in the clock network are NOT visited. +void +visitPathGroupVertices(PathGroup *path_group, + VertexVisitor *visitor, + StaState *sta) +{ + Search *search = sta->search(); + VertexPathSetMap matching_path_map; + // Do not visit clock network. + SearchPredNonReg2 srch_non_reg(sta); + BfsBkwdIterator bkwd_iter(BfsIndex::other, &srch_non_reg, sta); + // Visit the path ends and filter by path_group to seed the backward search. + VisitPathGroupEnds end_visitor(path_group, visitor, &matching_path_map, + &bkwd_iter, sta); + VisitPathEnds visit_path_ends(sta); + VertexSet::Iterator end_iter(search->endpoints()); + while (end_iter.hasNext()) { + Vertex *vertex = end_iter.next(); + visit_path_ends.visitPathEnds(vertex, &end_visitor); + } + + // Search backward from the path ends thru vertices that have arrival tags + // that match path_group end paths. + PathGroupPathVisitor path_visitor(visitor, &bkwd_iter, &matching_path_map, + sta); + bkwd_iter.visit(0, &path_visitor); + + // Cleanup. + VertexPathSetMap::Iterator matching_iter(matching_path_map); + while (matching_iter.hasNext()) { + PathVertexSet *paths = matching_iter.next(); + PathVertexSet::Iterator path_iter(paths); + while (path_iter.hasNext()) { + PathVertex *path = path_iter.next(); + delete path; + } + delete paths; + } +} + +//////////////////////////////////////////////////////////////// + +VisitPathGroupEnds::VisitPathGroupEnds(PathGroup *path_group, + VertexVisitor *vertex_visitor, + VertexPathSetMap *matching_path_map, + BfsBkwdIterator *bkwd_iter, + StaState *sta) : + path_group_(path_group), + vertex_visitor_(vertex_visitor), + bkwd_iter_(bkwd_iter), + matching_path_map_(matching_path_map), + sta_(sta) +{ +} + +PathEndVisitor * +VisitPathGroupEnds::copy() +{ + return new VisitPathGroupEnds(path_group_, vertex_visitor_, + matching_path_map_, bkwd_iter_, sta_); +} + +void +VisitPathGroupEnds::vertexBegin(Vertex *) +{ + vertex_matches_ = false; +} + +void +VisitPathGroupEnds::visit(PathEnd *path_end) +{ + PathGroup *group = sta_->search()->pathGroup(path_end); + if (group == path_group_) { + PathRef path(path_end->pathRef()); + Vertex *vertex = path.vertex(sta_); + + int arrival_index; + bool arrival_exists; + path.arrivalIndex(arrival_index, arrival_exists); + vertexPathSetMapInsertPath(matching_path_map_, vertex, path.tag(sta_), + arrival_index, sta_); + vertex_matches_ = true; + } +} + +static void +vertexPathSetMapInsertPath(VertexPathSetMap *matching_path_map, + Vertex *vertex, + Tag *tag, + int arrival_index, + const StaState *sta) +{ + PathVertexSet *matching_paths = matching_path_map->findKey(vertex); + if (matching_paths == nullptr) { + PathLess path_less(sta); + matching_paths = new PathVertexSet(path_less); + (*matching_path_map)[vertex] = matching_paths; + } + PathVertex *vpath = new PathVertex(vertex, tag, arrival_index); + matching_paths->insert(vpath); +} + +void +VisitPathGroupEnds::vertexEnd(Vertex *vertex) +{ + if (vertex_matches_) { + vertex_visitor_->visit(vertex); + // Seed backward bfs fanin search. + bkwd_iter_->enqueueAdjacentVertices(vertex); + } +} + +//////////////////////////////////////////////////////////////// + +PathGroupPathVisitor::PathGroupPathVisitor(VertexVisitor *visitor, + BfsBkwdIterator *bkwd_iter, + VertexPathSetMap *matching_path_map, + const StaState *sta) : + PathVisitor(sta), + visitor_(visitor), + bkwd_iter_(bkwd_iter), + matching_path_map_(matching_path_map) +{ +} + +PathGroupPathVisitor::~PathGroupPathVisitor() +{ +} + +VertexVisitor * +PathGroupPathVisitor::copy() +{ + return new PathGroupPathVisitor(visitor_, bkwd_iter_, matching_path_map_, + sta_); +} + +void +PathGroupPathVisitor::visit(Vertex *vertex) +{ + vertex_matches_ = false; + visitFanoutPaths(vertex); + if (vertex_matches_) { + const Debug *debug = sta_->debug(); + debugPrint1(debug, "visit_path_group", 1, "visit %s\n", + vertex->name(sta_->network())); + visitor_->visit(vertex); + bkwd_iter_->enqueueAdjacentVertices(vertex); + } +} + +bool +PathGroupPathVisitor::visitFromToPath(const Pin *, + Vertex *from_vertex, + const TransRiseFall *, + Tag *from_tag, + PathVertex *from_path, + Edge *, + TimingArc *, + ArcDelay , + Vertex *to_vertex, + const TransRiseFall *to_tr, + Tag *to_tag, + Arrival &, + const MinMax *, + const PathAnalysisPt *path_ap) +{ + PathVertexSet *matching_paths = matching_path_map_->findKey(to_vertex); + if (matching_paths) { + int arrival_index; + bool arrival_exists; + from_path->arrivalIndex(arrival_index, arrival_exists); + PathVertex to_path(to_vertex, to_tag, sta_); + if (!to_path.isNull()) { + if (matching_paths->hasKey(&to_path)) { + const Debug *debug = sta_->debug(); + debugPrint4(debug, "visit_path_group", 2, "match %s %s -> %s %s\n", + from_vertex->name(sta_->network()), + from_tag->asString(sta_), + to_vertex->name(sta_->network()), + to_tag->asString(sta_)); + fromMatches(from_vertex, from_tag, arrival_index); + } + } + else { + VertexPathIterator to_iter(to_vertex, to_tr, path_ap, sta_); + while (to_iter.hasNext()) { + PathVertex *to_path = to_iter.next(); + if (tagMatchNoCrpr(to_path->tag(sta_), to_tag) + && matching_paths->hasKey(to_path)) { + const Debug *debug = sta_->debug(); + debugPrint4(debug, "visit_path_group", 2, + "match crpr %s %s -> %s %s\n", + from_vertex->name(sta_->network()), + from_tag->asString(sta_), + to_vertex->name(sta_->network()), + to_tag->asString(sta_)); + fromMatches(from_vertex, from_tag, arrival_index); + } + } + } + } + return true; +} + +void +PathGroupPathVisitor::fromMatches(Vertex *from_vertex, + Tag *from_tag, + int from_arrival_index) +{ + vertex_matches_ = true; + vertexPathSetMapInsertPath(matching_path_map_, from_vertex, + from_tag, from_arrival_index, sta_); +} + +} // namespace diff --git a/search/VisitPathGroupVertices.hh b/search/VisitPathGroupVertices.hh new file mode 100644 index 0000000..0d6578d --- /dev/null +++ b/search/VisitPathGroupVertices.hh @@ -0,0 +1,34 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_VISIT_PATH_GROUP_VERTICES_H +#define STA_VISIT_PATH_GROUP_VERTICES_H + +namespace sta { + +class PathGroup; +class VertexVisitor; +class StaState; + +// Visit the fanin vertices for the path group. +// Vertices in the clock network are NOT visited. +void +visitPathGroupVertices(PathGroup *path_group, + VertexVisitor *visitor, + StaState *sta); + +} // namespace +#endif diff --git a/search/WorstSlack.cc b/search/WorstSlack.cc new file mode 100644 index 0000000..7029bcf --- /dev/null +++ b/search/WorstSlack.cc @@ -0,0 +1,346 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Mutex.hh" +#include "Debug.hh" +#include "Report.hh" +#include "Graph.hh" +#include "Corner.hh" +#include "Search.hh" +#include "PathAnalysisPt.hh" +#include "WorstSlack.hh" + +namespace sta { + +using std::min; + +WorstSlacks::WorstSlacks(StaState *sta) : + sta_(sta) +{ + worst_slacks_.resize(sta->corners()->pathAnalysisPtCount()); +} + +void +WorstSlacks::worstSlack(const MinMax *min_max, + // 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(); + Slack worst_slack1; + Vertex *worst_vertex1; + worst_slacks_[path_ap_index].worstSlack(path_ap_index, sta_, + worst_slack1, worst_vertex1); + if (fuzzyLess(worst_slack1, worst_slack)) { + worst_slack = worst_slack1; + worst_vertex = worst_vertex1; + } + } +} + +void +WorstSlacks::worstSlack(const Corner *corner, + 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, sta_, + worst_slack, worst_vertex); +} + +void +WorstSlacks::updateWorstSlacks(Vertex *vertex, + SlackSeq &slacks) +{ + PathAPIndex path_ap_count = sta_->corners()->pathAnalysisPtCount(); + for (PathAPIndex i = 0; i < path_ap_count; i++) + worst_slacks_[i].updateWorstSlack(vertex, slacks, i, sta_); +} + +void +WorstSlacks::worstSlackNotifyBefore(Vertex *vertex) +{ + WorstSlackSeq::Iterator worst_iter(worst_slacks_); + while (worst_iter.hasNext()) { + WorstSlack &worst_slack = worst_iter.next(); + worst_slack.deleteVertexBefore(vertex); + } +} + +//////////////////////////////////////////////////////////////// + +WorstSlack::WorstSlack() : + slack_init_(MinMax::min()->initValue()), + worst_vertex_(nullptr), + worst_slack_(slack_init_), + slack_threshold_(slack_init_), + min_queue_size_(10), + max_queue_size_(20) +{ +} + +WorstSlack::WorstSlack(const WorstSlack &) : + slack_init_(MinMax::min()->initValue()), + worst_vertex_(nullptr), + worst_slack_(slack_init_), + slack_threshold_(slack_init_), + min_queue_size_(10), + max_queue_size_(20) +{ +} + +void +WorstSlack::deleteVertexBefore(Vertex *vertex) +{ + UniqueLock lock(lock_); + if (vertex == worst_vertex_) { + worst_vertex_ = nullptr; + worst_slack_ = slack_init_; + } + queue_.erase(vertex); +} + +void +WorstSlack::worstSlack(PathAPIndex path_ap_index, + const StaState *sta, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex) +{ + findWorstSlack(path_ap_index, sta); + worst_slack = worst_slack_; + worst_vertex = worst_vertex_; +} + +void +WorstSlack::findWorstSlack(PathAPIndex path_ap_index, + const StaState *sta) +{ + if (worst_vertex_ == nullptr) { + if (queue_.empty()) + initQueue(path_ap_index, sta); + else + findWorstInQueue(path_ap_index, sta); + } +} + +void +WorstSlack::initQueue(PathAPIndex path_ap_index, + const StaState *sta) +{ + Search *search = sta->search(); + const Debug *debug = sta->debug(); + debugPrint0(debug, "wns", 3, "init queue\n"); + + queue_.clear(); + worst_vertex_ = nullptr; + worst_slack_ = slack_init_; + slack_threshold_ = slack_init_; + VertexSet::Iterator end_iter(search->endpoints()); + while (end_iter.hasNext()) { + Vertex *vertex = end_iter.next(); + Slack slack = search->wnsSlack(vertex, path_ap_index); + if (!fuzzyEqual(slack, slack_init_)) { + if (fuzzyLess(slack, worst_slack_)) + setWorstSlack(vertex, slack, sta); + if (fuzzyLessEqual(slack, slack_threshold_)) + queue_.insert(vertex); + int queue_size = queue_.size(); + if (queue_size >= max_queue_size_) + sortQueue(path_ap_index, sta); + } + } + debugPrint1(debug, "wns", 3, "threshold %s\n", + delayAsString(slack_threshold_, sta)); +// checkQueue(); +} + +void +WorstSlack::sortQueue(PathAPIndex path_ap_index, + const StaState *sta) +{ + Search *search = sta->search(); + const Debug *debug = sta->debug(); + debugPrint0(debug, "wns", 3, "sort queue\n"); + + VertexSeq vertices; + vertices.reserve(queue_.size()); + VertexSet::Iterator queue_iter(queue_); + while (queue_iter.hasNext()) { + Vertex *vertex = queue_iter.next(); + vertices.push_back(vertex); + } + WnsSlackLess slack_less(path_ap_index, sta); + sort(vertices, slack_less); + + int vertex_count = vertices.size(); + int threshold_index = min(min_queue_size_, vertex_count - 1); + Vertex *threshold_vertex = vertices[threshold_index]; + slack_threshold_ = search->wnsSlack(threshold_vertex, path_ap_index); + debugPrint1(debug, "wns", 3, "threshold %s\n", + delayAsString(slack_threshold_, sta)); + + // Reinsert vertices with slack < threshold. + queue_.clear(); + VertexSeq::Iterator queue_iter2(vertices); + while (queue_iter2.hasNext()) { + Vertex *vertex = queue_iter2.next(); + Slack slack = search->wnsSlack(vertex, path_ap_index); + if (fuzzyGreater(slack, slack_threshold_)) + break; + queue_.insert(vertex); + } + max_queue_size_ = queue_.size() * 2; + Vertex *worst_slack_vertex = vertices[0]; + Slack worst_slack_slack = search->wnsSlack(worst_slack_vertex, path_ap_index); + setWorstSlack(worst_slack_vertex, worst_slack_slack, sta); +} + +void +WorstSlack::findWorstInQueue(PathAPIndex path_ap_index, + const StaState *sta) +{ + Search *search = sta->search(); + const Debug *debug = sta->debug(); + debugPrint0(debug, "wns", 3, "find worst in queue\n"); + + worst_vertex_ = nullptr; + worst_slack_ = slack_init_; + VertexSet::Iterator queue_iter(queue_); + while (queue_iter.hasNext()) { + Vertex *vertex = queue_iter.next(); + Slack slack = search->wnsSlack(vertex, path_ap_index); + if (slack < worst_slack_) + setWorstSlack(vertex, slack, sta); + } +} + +void +WorstSlack::checkQueue(PathAPIndex path_ap_index, + const StaState *sta) +{ + Search *search = sta->search(); + Report *report = sta->report(); + const Network *network = sta->network(); + + VertexSeq ends; + VertexSet::Iterator end_iter(search->endpoints()); + while (end_iter.hasNext()) { + Vertex *end = end_iter.next(); + if (fuzzyLessEqual(search->wnsSlack(end, path_ap_index), + slack_threshold_)) + ends.push_back(end); + } + WnsSlackLess slack_less(path_ap_index, sta); + sort(ends, slack_less); + + VertexSet end_set; + VertexSeq::Iterator end_iter2(ends); + while (end_iter2.hasNext()) { + Vertex *end = end_iter2.next(); + end_set.insert(end); + if (!queue_.hasKey(end) + && fuzzyLessEqual(search->wnsSlack(end, path_ap_index), + slack_threshold_)) + report->print("WorstSlack queue missing %s %s < %s\n", + end->name(network), + delayAsString(search->wnsSlack(end, path_ap_index), sta), + delayAsString(slack_threshold_, sta)); + } + + VertexSet::Iterator queue_iter(queue_); + while (queue_iter.hasNext()) { + Vertex *end = queue_iter.next(); + if (!end_set.hasKey(end)) + report->print("WorstSlack queue extra %s %s > %s\n", + end->name(network), + delayAsString(search->wnsSlack(end, path_ap_index), sta), + delayAsString(slack_threshold_, sta)); + } +} + +void +WorstSlack::updateWorstSlack(Vertex *vertex, + SlackSeq &slacks, + PathAPIndex path_ap_index, + const StaState *sta) +{ + const Debug *debug = sta->debug(); + const Network *network = sta->network(); + Slack slack = slacks[path_ap_index]; + + // Locking is required because ArrivalVisitor is called by multiple + // threads. + UniqueLock lock(lock_); + if (worst_vertex_ + && fuzzyLess(slack, worst_slack_)) + setWorstSlack(vertex, slack, sta); + else if (vertex == worst_vertex_) + // Mark worst slack as unknown (updated by findWorstSlack(). + worst_vertex_ = nullptr; + + if (!fuzzyEqual(slack, slack_init_) + && fuzzyLessEqual(slack, slack_threshold_)) { + debugPrint2(debug, "wns", 3, "insert %s %s\n", + vertex->name(network), + delayAsString(slack, sta)); + queue_.insert(vertex); + } + else { + debugPrint2(debug, "wns", 3, "delete %s %s\n", + vertex->name(network), + delayAsString(slack, sta)); + queue_.erase(vertex); + } + // checkQueue(); +} + +void +WorstSlack::setWorstSlack(Vertex *vertex, + Slack slack, + const StaState *sta) +{ + debugPrint2(sta->debug(), "wns", 3, "%s %s\n", + vertex->name(sta->network()), + delayAsString(slack, sta)); + worst_vertex_ = vertex; + worst_slack_ = slack; +} + +//////////////////////////////////////////////////////////////// + +WnsSlackLess::WnsSlackLess(PathAPIndex path_ap_index, + const StaState *sta) : + path_ap_index_(path_ap_index), + search_(sta->search()) +{ +} + +bool +WnsSlackLess::operator()(Vertex *vertex1, + Vertex *vertex2) +{ + return fuzzyLess(search_->wnsSlack(vertex1, path_ap_index_), + search_->wnsSlack(vertex2, path_ap_index_)); +} + +} // namespace diff --git a/search/WorstSlack.hh b/search/WorstSlack.hh new file mode 100644 index 0000000..e36a3a7 --- /dev/null +++ b/search/WorstSlack.hh @@ -0,0 +1,116 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_WORST_SLACK_H +#define STA_WORST_SLACK_H + +namespace sta { + +#include +#include "MinMax.hh" +#include "Vector.hh" +#include "GraphClass.hh" +#include "SearchClass.hh" + +class StaState; +class WorstSlack; +class WnsSlackLess; + +typedef Vector WorstSlackSeq; + +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); + void updateWorstSlacks(Vertex *vertex, + SlackSeq &slacks); + void worstSlackNotifyBefore(Vertex *vertex); + +protected: + WorstSlackSeq worst_slacks_; + const StaState *sta_; +}; + +class WnsSlackLess +{ +public: + WnsSlackLess(PathAPIndex path_ap_index, + const StaState *sta); + bool operator()(Vertex *vertex1, + Vertex *vertex2); + +private: + PathAPIndex path_ap_index_; + Search *search_; +}; + +class WorstSlack +{ +public: + WorstSlack(); + WorstSlack(const WorstSlack &); + void worstSlack(PathAPIndex path_ap_index, + const StaState *sta, + // Return values. + Slack &worst_slack, + Vertex *&worst_vertex); + void updateWorstSlack(Vertex *vertex, + SlackSeq &slacks, + PathAPIndex path_ap_index, + const StaState *sta); + void deleteVertexBefore(Vertex *vertex); + +protected: + void findWorstSlack(PathAPIndex path_ap_index, + const StaState *sta); + void initQueue(PathAPIndex path_ap_index, + const StaState *sta); + void findWorstInQueue(PathAPIndex path_ap_index, + const StaState *sta); + void setWorstSlack(Vertex *vertex, + Slack slack, + const StaState *sta); + void sortQueue(PathAPIndex path_ap_index, + const StaState *sta); + void checkQueue(PathAPIndex path_ap_index, + const StaState *sta); + + Slack slack_init_; + // Vertex with the worst slack. + // When nullptr the worst slack is unknown but in the queue. + Vertex *worst_vertex_; + 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_; + std::mutex lock_; +}; + +} // namespace +#endif diff --git a/search/WritePathSpice.cc b/search/WritePathSpice.cc new file mode 100644 index 0000000..5b2dccd --- /dev/null +++ b/search/WritePathSpice.cc @@ -0,0 +1,1626 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include +#include "Machine.hh" +#include "Debug.hh" +#include "Error.hh" +#include "Report.hh" +#include "StringUtil.hh" +#include "FuncExpr.hh" +#include "Units.hh" +#include "Sequential.hh" +#include "TableModel.hh" +#include "Liberty.hh" +#include "TimingArc.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 "PathRef.hh" +#include "PathExpanded.hh" +#include "StaState.hh" +#include "Sim.hh" +#include "WritePathSpice.hh" + +namespace sta { + +using std::string; +using std::ofstream; +using std::ifstream; + +typedef Map CellSpicePortNames; +typedef int Stage; +typedef Map ParasiticNodeMap; +typedef Map LibertyPortLogicValues; + +void +streamPrint(ofstream &stream, + const char *fmt, + ...) __attribute__((format (printf, 2, 3))); + +//////////////////////////////////////////////////////////////// + +class WritePathSpice : public StaState +{ +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, + const StaState *sta); + ~WritePathSpice(); + void writeSpice(); + +private: + void writeHeader(); + void writeStageInstances(); + void writeInputSource(); + void writeStepVoltSource(const Pin *pin, + const TransRiseFall *tr, + float slew, + float time, + int &volt_index); + void writeStageSubckts(); + void writeInputStage(Stage stage); + void writeMeasureStmts(); + void writeMeasureStmt(const Pin *pin); + void writeGateStage(Stage stage); + void writeSubcktInst(const Pin *input_pin); + void writeSubcktInstVoltSrcs(Stage stage, + const Pin *input_pin, + int &volt_index, + LibertyPortLogicValues &port_values, + const Clock *clk, + DcalcAPIndex dcalc_ap_index); + void writeStageParasitics(Stage stage); + void writeSubckts(); + void findPathCellnames(// Return values. + StringSet &path_cell_names); + void recordSpicePortNames(const char *cell_name, + StringVector &tokens); + float maxTime(); + const char *nodeName(ParasiticNode *node); + void initNodeMap(const char *net_name); + const char *spiceTrans(const TransRiseFall *tr); + void writeMeasureDelayStmt(Stage stage, + Path *from_path, + Path *to_path); + void writeMeasureSlewStmt(Stage stage, + Path *path); + void gatePortValues(Stage stage, + // Return values. + LibertyPortLogicValues &port_values, + const Clock *&clk, + DcalcAPIndex &dcalc_ap_index); + void regPortValues(Stage stage, + // Return values. + LibertyPortLogicValues &port_values, + const Clock *&clk, + DcalcAPIndex &dcalc_ap_index); + void gatePortValues(const Instance *inst, + FuncExpr *expr, + LibertyPort *input_port, + // Return values. + LibertyPortLogicValues &port_values); + void seqPortValues(Sequential *seq, + const TransRiseFall *tr, + // Return values. + LibertyPortLogicValues &port_values); + void writeInputWaveform(); + void writeClkWaveform(); + void writeWaveformEdge(const TransRiseFall *tr, + float time, + float slew); + void writeClkedStepSource(const Pin *pin, + const TransRiseFall *tr, + const Clock *clk, + DcalcAPIndex dcalc_ap_index, + int &volt_index); + float clkWaveformTImeOffset(const Clock *clk); + float findSlew(Path *path); + float findSlew(Path *path, + const TransRiseFall *tr, + TimingArc *next_arc); + float findSlew(Vertex *vertex, + const TransRiseFall *tr, + TimingArc *next_arc, + DcalcAPIndex dcalc_ap_index); + LibertyPort *onePort(FuncExpr *expr); + void writeVoltageSource(const char *inst_name, + const char *port_name, + float voltage, + int &volt_index); + void writeVoltageSource(LibertyCell *cell, + const char *inst_name, + const char *subckt_port_name, + const char *pg_port_name, + float voltage, + int &volt_index); + float slewAxisMinValue(TimingArc *arc); + float pgPortVoltage(LibertyPgPort *pg_port); + + // Stage "accessors". + // + // stage + // |---------------| + // |\ |\ . + // -------| >---/\/\/----| >--- + // gate |/ drvr load|/ + // input + // + // A path from an input port has no GateInputPath (the input port is the drvr). + // Internally a stage index from stageFirst() to stageLast() + // is turned into an index into path_expanded_. + // + Stage stageFirst(); + Stage stageLast(); + string stageName(Stage stage); + int stageGateInputPathIndex(Stage stage); + int stageDrvrPathIndex(Stage stage); + int stageLoadPathIndex(Stage stage); + PathRef *stageGateInputPath(Stage stage); + PathRef *stageDrvrPath(Stage stage); + PathRef *stageLoadPath(Stage stage); + TimingArc *stageGateArc(Stage stage); + 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); + + 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_; + + ofstream spice_stream_; + PathExpanded path_expanded_; + CellSpicePortNames cell_spice_port_names_; + ParasiticNodeMap node_map_; + int next_node_index_; + const char *net_name_; + float power_voltage_; + float gnd_voltage_; + LibertyLibrary *default_library_; + // Resistance to use to simulate a short circuit between spice nodes. + float short_ckt_resistance_; + // Input clock waveform cycles. + int clk_cycle_count_; +}; + +//////////////////////////////////////////////////////////////// + +class SubcktEndsMissing : public StaException +{ +public: + SubcktEndsMissing(const char *cell_name, + const char *subckt_filename); + const char *what() const throw(); + +protected: + string what_; +}; + +SubcktEndsMissing::SubcktEndsMissing(const char *cell_name, + const char *subckt_filename) +{ + what_ = "Error: spice subckt for cell "; + what_ += cell_name; + what_ += " missing .ends in "; + what_ += subckt_filename; +} + +const char * +SubcktEndsMissing::what() const throw() +{ + return what_.c_str(); +} + +//////////////////////////////////////////////////////////////// + +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, + StaState *sta) +{ + WritePathSpice writer(path, spice_filename, subckt_filename, + lib_subckt_filename, model_filename, + power_name, gnd_name, sta); + 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, + const StaState *sta) : + StaState(sta), + path_(path), + spice_filename_(spice_filename), + subckt_filename_(subckt_filename), + lib_subckt_filename_(lib_subckt_filename), + model_filename_(model_filename), + power_name_(power_name), + gnd_name_(gnd_name), + path_expanded_(sta), + net_name_(nullptr), + default_library_(network_->defaultLibertyLibrary()), + short_ckt_resistance_(.0001), + clk_cycle_count_(3) +{ + bool exists; + default_library_->supplyVoltage(power_name_, power_voltage_, exists); + if (!exists) { + auto dcalc_ap = path_->dcalcAnalysisPt(this); + auto op_cond = dcalc_ap->operatingConditions(); + if (op_cond == nullptr) + op_cond = network_->defaultLibertyLibrary()->defaultOperatingConditions(); + power_voltage_ = op_cond->voltage(); + } + default_library_->supplyVoltage(gnd_name_, gnd_voltage_, exists); + if (!exists) + gnd_voltage_ = 0.0; +} + +WritePathSpice::~WritePathSpice() +{ + stringDelete(net_name_); + cell_spice_port_names_.deleteContents(); +} + +void +WritePathSpice::writeSpice() +{ + spice_stream_.open(spice_filename_); + if (spice_stream_.is_open()) { + path_expanded_.expand(path_, true); + // Find subckt port names as a side-effect of writeSubckts. + writeSubckts(); + writeHeader(); + writeStageInstances(); + writeMeasureStmts(); + writeInputSource(); + writeStageSubckts(); + streamPrint(spice_stream_, ".end\n"); + spice_stream_.close(); + } + else + throw FileNotWritable(spice_filename_); +} + +void +WritePathSpice::writeHeader() +{ + auto min_max = path_->minMax(this); + auto pvt = sdc_->operatingConditions(min_max); + if (pvt == nullptr) + pvt = default_library_->defaultOperatingConditions(); + Path *start_path = path_expanded_.startPath(); + streamPrint(spice_stream_, "* Path from %s %s to %s %s\n", + network_->pathName(start_path->pin(this)), + start_path->transition(this)->asString(), + network_->pathName(path_->pin(this)), + path_->transition(this)->asString()); + auto temp = pvt->temperature(); + streamPrint(spice_stream_, ".temp %.1f\n", temp); + streamPrint(spice_stream_, ".include \"%s\"\n", model_filename_); + streamPrint(spice_stream_, ".include \"%s\"\n", subckt_filename_); + + auto max_time = maxTime(); + auto time_step = max_time / 1e+3; + streamPrint(spice_stream_, ".tran %.3g %.3g\n\n", + time_step, max_time); +} + +float +WritePathSpice::maxTime() +{ + auto input_stage = stageFirst(); + auto input_path = stageDrvrPath(input_stage); + auto tr = input_path->transition(this); + auto next_arc = stageGateArc(input_stage + 1); + auto input_slew = findSlew(input_path, tr, next_arc); + if (input_path->isClock(this)) { + auto clk = input_path->clock(this); + auto period = clk->period(); + float first_edge_offset = period / 10; + auto max_time = period * clk_cycle_count_ + first_edge_offset; + return max_time; + } + else { + auto end_slew = findSlew(path_); + auto max_time = delayAsFloat(input_slew + + path_->arrival(this) + + end_slew * 2) * 1.5; + return max_time; + } +} + +void +WritePathSpice::writeStageInstances() +{ + streamPrint(spice_stream_, "*****************\n"); + streamPrint(spice_stream_, "* Stage instances\n"); + streamPrint(spice_stream_, "*****************\n\n"); + + for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { + auto stage_name = stageName(stage).c_str(); + if (stage == stageFirst()) + streamPrint(spice_stream_, "x%s %s %s %s\n", + stage_name, + stageDrvrPinName(stage), + stageLoadPinName(stage), + stage_name); + else + streamPrint(spice_stream_, "x%s %s %s %s %s\n", + stage_name, + stageGateInputPinName(stage), + stageDrvrPinName(stage), + stageLoadPinName(stage), + stage_name); + } + streamPrint(spice_stream_, "\n"); +} + +float +WritePathSpice::pgPortVoltage(LibertyPgPort *pg_port) +{ + LibertyLibrary *liberty = pg_port->cell()->libertyLibrary(); + float voltage = 0.0; + bool exists; + auto voltage_name = pg_port->voltageName(); + if (voltage_name) { + 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_; + else + report_->error("pg_pin %s/%s voltage %s not found,\n", + pg_port->cell()->name(), + pg_port->name(), + voltage_name); + } + } + else + report_->error("Liberty pg_port %s/%s missing voltage_name attribute,\n", + pg_port->cell()->name(), + pg_port->name()); + return voltage; +} + +void +WritePathSpice::writeInputSource() +{ + streamPrint(spice_stream_, "**************\n"); + streamPrint(spice_stream_, "* Input source\n"); + streamPrint(spice_stream_, "**************\n\n"); + + auto input_stage = stageFirst(); + auto input_path = stageDrvrPath(input_stage); + if (input_path->isClock(this)) + writeClkWaveform(); + else + writeInputWaveform(); + streamPrint(spice_stream_, "\n"); +} + +void +WritePathSpice::writeInputWaveform() +{ + auto input_stage = stageFirst(); + auto input_path = stageDrvrPath(input_stage); + auto tr = input_path->transition(this); + auto next_arc = stageGateArc(input_stage + 1); + auto slew0 = findSlew(input_path, tr, next_arc); + // Arbitrary offset. + auto time0 = slew0; + int volt_index = 1; + auto drvr_pin = stageDrvrPin(input_stage); + writeStepVoltSource(drvr_pin, tr, slew0, time0, volt_index); +} + +void +WritePathSpice::writeStepVoltSource(const Pin *pin, + const TransRiseFall *tr, + float slew, + float time, + int &volt_index) +{ + float volt0, volt1; + if (tr == TransRiseFall::rise()) { + volt0 = gnd_voltage_; + volt1 = power_voltage_; + } + else { + 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); + writeWaveformEdge(tr, time, slew); + streamPrint(spice_stream_, "+%.3e %.3e\n", maxTime(), volt1); + streamPrint(spice_stream_, "+)\n"); + volt_index++; +} + +void +WritePathSpice::writeClkWaveform() +{ + auto input_stage = stageFirst(); + auto input_path = stageDrvrPath(input_stage); + auto next_arc = stageGateArc(input_stage + 1); + auto clk_edge = input_path->clkEdge(this); + auto clk = clk_edge->clock(); + auto period = clk->period(); + float time_offset = clkWaveformTImeOffset(clk); + TransRiseFall *tr0, *tr1; + float volt0; + if (clk_edge->time() < period) { + tr0 = TransRiseFall::rise(); + tr1 = TransRiseFall::fall(); + volt0 = gnd_voltage_; + } + else { + tr0 = TransRiseFall::fall(); + tr1 = TransRiseFall::rise(); + volt0 = power_voltage_; + } + auto slew0 = findSlew(input_path, tr0, next_arc); + auto slew1 = findSlew(input_path, tr1, next_arc); + streamPrint(spice_stream_, "v1 %s 0 pwl(\n", + stageDrvrPinName(input_stage)); + streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0); + for (int cycle = 0; cycle < clk_cycle_count_; cycle++) { + auto time0 = time_offset + cycle * period; + auto time1 = time0 + period / 2.0; + writeWaveformEdge(tr0, time0, slew0); + writeWaveformEdge(tr1, time1, slew1); + } + streamPrint(spice_stream_, "+%.3e %.3e\n", maxTime(), volt0); + streamPrint(spice_stream_, "+)\n"); +} + +float +WritePathSpice::clkWaveformTImeOffset(const Clock *clk) +{ + return clk->period() / 10; +} + +float +WritePathSpice::findSlew(Path *path) +{ + auto vertex = path->vertex(this); + auto dcalc_ap_index = path->dcalcAnalysisPt(this)->index(); + auto tr = path->transition(this); + return findSlew(vertex, tr, nullptr, dcalc_ap_index); +} + +float +WritePathSpice::findSlew(Path *path, + const TransRiseFall *tr, + TimingArc *next_arc) +{ + auto vertex = path->vertex(this); + auto dcalc_ap_index = path->dcalcAnalysisPt(this)->index(); + return findSlew(vertex, tr, next_arc, dcalc_ap_index); +} + +float +WritePathSpice::findSlew(Vertex *vertex, + const TransRiseFall *tr, + TimingArc *next_arc, + DcalcAPIndex dcalc_ap_index) +{ + auto slew = delayAsFloat(graph_->slew(vertex, tr, dcalc_ap_index)); + if (slew == 0.0 && next_arc) + slew = slewAxisMinValue(next_arc); + if (slew == 0.0) + slew = units_->timeUnit()->scale(); + return slew; +} + +// Look up the smallest slew axis value in the timing arc delay table. +float +WritePathSpice::slewAxisMinValue(TimingArc *arc) +{ + GateTableModel *gate_model = dynamic_cast(arc->model()); + if (gate_model) { + const TableModel *model = gate_model->delayModel(); + TableAxis *axis; + TableAxisVariable var; + axis = model->axis1(); + var = axis->variable(); + if (var == TableAxisVariable::input_transition_time + || var == TableAxisVariable::input_net_transition) + return axis->axisValue(0); + + axis = model->axis2(); + var = axis->variable(); + if (var == TableAxisVariable::input_transition_time + || var == TableAxisVariable::input_net_transition) + return axis->axisValue(0); + + axis = model->axis3(); + var = axis->variable(); + if (var == TableAxisVariable::input_transition_time + || var == TableAxisVariable::input_net_transition) + return axis->axisValue(0); + } + return 0.0; +} + +// Write PWL rise/fall edge that crosses threshold at time. +void +WritePathSpice::writeWaveformEdge(const TransRiseFall *tr, + float time, + float slew) +{ + float volt0, volt1; + if (tr == TransRiseFall::rise()) { + volt0 = gnd_voltage_; + volt1 = power_voltage_; + } + else { + volt0 = power_voltage_; + volt1 = gnd_voltage_; + } + auto threshold = default_library_->inputThreshold(tr); + auto lower = default_library_->slewLowerThreshold(tr); + auto upper = default_library_->slewUpperThreshold(tr); + auto dt = slew / (upper - lower); + auto time0 = time - dt * threshold; + auto time1 = time0 + dt; + streamPrint(spice_stream_, "+%.3e %.3e\n", time0, volt0); + streamPrint(spice_stream_, "+%.3e %.3e\n", time1, volt1); +} + +//////////////////////////////////////////////////////////////// + +void +WritePathSpice::writeMeasureStmts() +{ + streamPrint(spice_stream_, "********************\n"); + streamPrint(spice_stream_, "* Measure statements\n"); + streamPrint(spice_stream_, "********************\n\n"); + + for (auto stage = stageFirst(); stage <= stageLast(); stage++) { + auto gate_input_path = stageGateInputPath(stage); + auto drvr_path = stageDrvrPath(stage); + auto load_path = stageLoadPath(stage); + if (gate_input_path) { + // gate input -> gate output + writeMeasureSlewStmt(stage, gate_input_path); + writeMeasureDelayStmt(stage, gate_input_path, drvr_path); + } + writeMeasureSlewStmt(stage, drvr_path); + // gate output | input port -> load + writeMeasureDelayStmt(stage, drvr_path, load_path); + if (stage == stageLast()) + writeMeasureSlewStmt(stage, load_path); + } + streamPrint(spice_stream_, "\n"); +} + +void +WritePathSpice::writeMeasureDelayStmt(Stage stage, + Path *from_path, + Path *to_path) +{ + auto from_pin_name = network_->pathName(from_path->pin(this)); + auto from_tr = from_path->transition(this); + auto from_threshold = power_voltage_ * default_library_->inputThreshold(from_tr); + + auto to_pin_name = network_->pathName(to_path->pin(this)); + auto to_tr = to_path->transition(this); + auto to_threshold = power_voltage_ * default_library_->inputThreshold(to_tr); + streamPrint(spice_stream_, + ".measure tran %s_%s_delay_%s\n", + stageName(stage).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_tr)); + streamPrint(spice_stream_, + "+targ v(%s) val=%.3f %s=last\n", + to_pin_name, + to_threshold, + spiceTrans(to_tr)); +} + +void +WritePathSpice::writeMeasureSlewStmt(Stage stage, + Path *path) +{ + auto pin_name = network_->pathName(path->pin(this)); + auto tr = path->transition(this); + auto spice_tr = spiceTrans(tr); + auto lower = power_voltage_ * default_library_->slewLowerThreshold(tr); + auto upper = power_voltage_ * default_library_->slewUpperThreshold(tr); + float threshold1, threshold2; + if (tr == TransRiseFall::rise()) { + threshold1 = lower; + threshold2 = upper; + } + else { + threshold1 = upper; + threshold2 = lower; + } + streamPrint(spice_stream_, + ".measure tran %s_%s_slew\n", + stageName(stage).c_str(), + pin_name); + streamPrint(spice_stream_, + "+trig v(%s) val=%.3f %s=last\n", + pin_name, + threshold1, + spice_tr); + streamPrint(spice_stream_, + "+targ v(%s) val=%.3f %s=last\n", + pin_name, + threshold2, + spice_tr); +} + +const char * +WritePathSpice::spiceTrans(const TransRiseFall *tr) +{ + if (tr == TransRiseFall::rise()) + return "RISE"; + else + return "FALL"; +} + +void +WritePathSpice::writeStageSubckts() +{ + streamPrint(spice_stream_, "***************\n"); + streamPrint(spice_stream_, "* Stage subckts\n"); + streamPrint(spice_stream_, "***************\n\n"); + + for (auto stage = stageFirst(); stage <= stageLast(); stage++) { + if (stage == stageFirst()) + writeInputStage(stage); + else + writeGateStage(stage); + } +} + +// Input port to first gate input. +void +WritePathSpice::writeInputStage(Stage stage) +{ + // Input arc. + // External driver not handled. + auto drvr_pin_name = stageDrvrPinName(stage); + auto load_pin_name = stageLoadPinName(stage); + streamPrint(spice_stream_, ".subckt %s %s %s\n", + stageName(stage).c_str(), + drvr_pin_name, + load_pin_name); + writeStageParasitics(stage); + streamPrint(spice_stream_, ".ends\n\n"); +} + +// Gate and load parasitics. +void +WritePathSpice::writeGateStage(Stage stage) +{ + auto input_pin = stageGateInputPin(stage); + auto input_pin_name = stageGateInputPinName(stage); + auto drvr_pin = stageDrvrPin(stage); + auto drvr_pin_name = stageDrvrPinName(stage); + auto load_pin = stageLoadPin(stage); + auto load_pin_name = stageLoadPinName(stage); + streamPrint(spice_stream_, ".subckt stage%d %s %s %s\n", + stage, + input_pin_name, + drvr_pin_name, + load_pin_name); + // Driver subckt call. + auto inst = stageInstance(stage); + auto input_port = stageGateInputPort(stage); + auto drvr_port = stageDrvrPort(stage); + streamPrint(spice_stream_, "* Gate %s %s -> %s\n", + network_->pathName(inst), + input_port->name(), + drvr_port->name()); + writeSubcktInst(input_pin); + LibertyPortLogicValues port_values; + DcalcAPIndex dcalc_ap_index; + const Clock *clk; + int volt_index = 1; + gatePortValues(stage, port_values, clk, dcalc_ap_index); + writeSubcktInstVoltSrcs(stage, input_pin, volt_index, + port_values, clk, dcalc_ap_index); + streamPrint(spice_stream_, "\n"); + + port_values.clear(); + auto pin_iter = network_->connectedPinIterator(drvr_pin); + while (pin_iter->hasNext()) { + auto pin = pin_iter->next(); + if (pin != drvr_pin + && pin != load_pin + && network_->direction(pin)->isAnyInput() + && !network_->isHierarchical(pin) + && !network_->isTopLevelPort(pin)) { + streamPrint(spice_stream_, "* Side load %s\n", network_->pathName(pin)); + writeSubcktInst(pin); + writeSubcktInstVoltSrcs(stage, pin, volt_index, port_values, nullptr, 0); + streamPrint(spice_stream_, "\n"); + } + } + delete pin_iter; + + writeStageParasitics(stage); + streamPrint(spice_stream_, ".ends\n\n"); +} + +void +WritePathSpice::writeSubcktInst(const Pin *input_pin) +{ + auto inst = network_->instance(input_pin); + auto inst_name = network_->pathName(inst); + auto cell = network_->libertyCell(inst); + auto cell_name = cell->name(); + auto spice_port_names = cell_spice_port_names_[cell_name]; + streamPrint(spice_stream_, "x%s", inst_name); + for (auto subckt_port_name : *spice_port_names) { + auto subckt_port_cname = subckt_port_name.c_str(); + auto pin = network_->findPin(inst, subckt_port_cname); + auto pg_port = cell->findPgPort(subckt_port_cname); + const char *pin_name; + if (pin) { + pin_name = network_->pathName(pin); + streamPrint(spice_stream_, " %s", pin_name); + } + 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); + } + streamPrint(spice_stream_, " %s\n", cell_name); +} + +// Power/ground and input voltage sources. +void +WritePathSpice::writeSubcktInstVoltSrcs(Stage stage, + const Pin *input_pin, + int &volt_index, + LibertyPortLogicValues &port_values, + const Clock *clk, + DcalcAPIndex dcalc_ap_index) + +{ + auto inst = network_->instance(input_pin); + auto cell = network_->libertyCell(inst); + auto cell_name = cell->name(); + auto spice_port_names = cell_spice_port_names_[cell_name]; + + auto drvr_pin = stageDrvrPin(stage); + auto input_port = network_->libertyPort(input_pin); + auto drvr_port = network_->libertyPort(drvr_pin); + auto input_port_name = input_port->name(); + auto drvr_port_name = drvr_port->name(); + auto inst_name = network_->pathName(inst); + + debugPrint1(debug_, "write_spice", 2, "subckt %s\n", cell->name()); + for (auto subckt_port_sname : *spice_port_names) { + auto subckt_port_name = subckt_port_sname.c_str(); + auto pg_port = cell->findPgPort(subckt_port_name); + debugPrint2(debug_, "write_spice", 2, " port %s%s\n", + subckt_port_name, + pg_port ? " pwr/gnd" : ""); + if (pg_port) + writeVoltageSource(inst_name, subckt_port_name, + pgPortVoltage(pg_port), volt_index); + else if (stringEq(subckt_port_name, power_name_)) + writeVoltageSource(inst_name, subckt_port_name, + power_voltage_, volt_index); + else if (stringEq(subckt_port_name, gnd_name_)) + writeVoltageSource(inst_name, subckt_port_name, + gnd_voltage_, volt_index); + else if (!(stringEq(subckt_port_name, input_port_name) + || stringEq(subckt_port_name, drvr_port_name))) { + // Input voltage to sensitize path from gate input to output. + auto port = cell->findLibertyPort(subckt_port_name); + if (port + && port->direction()->isAnyInput()) { + const Pin *pin = network_->findPin(inst, port); + // Look for tie high/low or propagated constant values. + LogicValue port_value = sim_->logicValue(pin); + if (port_value == LogicValue::unknown) { + bool has_value; + LogicValue value; + port_values.findKey(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_, + volt_index); + break; + case LogicValue::one: + writeVoltageSource(cell, inst_name, subckt_port_name, + port->relatedPowerPin(), + power_voltage_, + volt_index); + break; + case LogicValue::rise: + writeClkedStepSource(pin, TransRiseFall::rise(), clk, + dcalc_ap_index, volt_index); + break; + case LogicValue::fall: + writeClkedStepSource(pin, TransRiseFall::fall(), clk, + dcalc_ap_index, volt_index); + break; + } + } + } + } +} + +// PWL voltage source that rises half way into the first clock cycle. +void +WritePathSpice::writeClkedStepSource(const Pin *pin, + const TransRiseFall *tr, + const Clock *clk, + DcalcAPIndex dcalc_ap_index, + int &volt_index) +{ + auto vertex = graph_->pinLoadVertex(pin); + auto slew = findSlew(vertex, tr, nullptr, dcalc_ap_index); + auto time = clkWaveformTImeOffset(clk) + clk->period() / 2.0; + writeStepVoltSource(pin, tr, slew, time, volt_index); +} + +void +WritePathSpice::writeVoltageSource(const char *inst_name, + const char *port_name, + float voltage, + int &volt_index) +{ + streamPrint(spice_stream_, "v%d %s/%s 0 %.3f\n", + volt_index, + inst_name, port_name, + voltage); + volt_index++; +} + +void +WritePathSpice::writeVoltageSource(LibertyCell *cell, + const char *inst_name, + const char *subckt_port_name, + const char *pg_port_name, + float voltage, + int &volt_index) +{ + if (pg_port_name) { + auto pg_port = cell->findPgPort(pg_port_name); + if (pg_port) + voltage = pgPortVoltage(pg_port); + else + report_->error("%s pg_port %s not found,\n", + cell->name(), + pg_port_name); + + } + writeVoltageSource(inst_name, subckt_port_name, voltage, volt_index); +} + +void +WritePathSpice::gatePortValues(Stage stage, + // Return values. + LibertyPortLogicValues &port_values, + const Clock *&clk, + DcalcAPIndex &dcalc_ap_index) +{ + clk = nullptr; + dcalc_ap_index = 0; + + auto gate_edge = stageGateEdge(stage); + auto drvr_port = stageDrvrPort(stage); + if (gate_edge->role()->genericRole() == TimingRole::regClkToQ()) + regPortValues(stage, port_values, clk, dcalc_ap_index); + else if (drvr_port->function()) { + auto input_pin = stageGateInputPin(stage); + auto input_port = network_->libertyPort(input_pin); + auto inst = network_->instance(input_pin); + gatePortValues(inst, drvr_port->function(), input_port, port_values); + } +} + +void +WritePathSpice::regPortValues(Stage stage, + // Return values. + LibertyPortLogicValues &port_values, + const Clock *&clk, + DcalcAPIndex &dcalc_ap_index) +{ + auto drvr_port = stageDrvrPort(stage); + auto drvr_expr = drvr_port->function(); + if (drvr_expr) { + auto q_port = drvr_expr->port(); + if (q_port) { + // Drvr (register/latch output) function should be a reference + // to an internal port like IQ or IQN. + auto cell = stageLibertyCell(stage); + auto seq = cell->outputPortSequential(q_port); + if (seq) { + auto drvr_path = stageDrvrPath(stage); + auto drvr_tr = drvr_path->transition(this); + seqPortValues(seq, drvr_tr, port_values); + clk = drvr_path->clock(this); + dcalc_ap_index = drvr_path->dcalcAnalysisPt(this)->index(); + } + else + report_->error("no register/latch found for path from %s to %s,\n", + stageGateInputPort(stage)->name(), + stageDrvrPort(stage)->name()); + } + } +} + +// Find the logic values for expression inputs to enable paths input_port. +void +WritePathSpice::gatePortValues(const Instance *inst, + FuncExpr *expr, + LibertyPort *input_port, + // Return values. + LibertyPortLogicValues &port_values) +{ + auto left = expr->left(); + auto right = expr->right(); + switch (expr->op()) { + case FuncExpr::op_port: + break; + case FuncExpr::op_not: + gatePortValues(inst, left, input_port, port_values); + break; + case FuncExpr::op_or: + if (left->hasPort(input_port) + && right->op() == FuncExpr::op_port) + port_values[right->port()] = LogicValue::zero; + else if (left->hasPort(input_port) + && right->op() == FuncExpr::op_not + && right->left()->op() == FuncExpr::op_port) + // input_port + !right_port + port_values[right->left()->port()] = LogicValue::one; + else if (right->hasPort(input_port) + && left->op() == FuncExpr::op_port) + port_values[left->port()] = LogicValue::zero; + else if (right->hasPort(input_port) + && left->op() == FuncExpr::op_not + && left->left()->op() == FuncExpr::op_port) + // input_port + !left_port + port_values[left->left()->port()] = LogicValue::one; + else { + gatePortValues(inst, left, input_port, port_values); + gatePortValues(inst, right, input_port, port_values); + } + break; + case FuncExpr::op_and: + if (left->hasPort(input_port) + && right->op() == FuncExpr::op_port) + port_values[right->port()] = LogicValue::one; + else if (left->hasPort(input_port) + && right->op() == FuncExpr::op_not + && right->left()->op() == FuncExpr::op_port) + // input_port * !right_port + port_values[right->left()->port()] = LogicValue::zero; + else if (right->hasPort(input_port) + && left->op() == FuncExpr::op_port) + port_values[left->port()] = LogicValue::one; + else if (right->hasPort(input_port) + && left->op() == FuncExpr::op_not + && left->left()->op() == FuncExpr::op_port) + // input_port * !left_port + port_values[left->left()->port()] = LogicValue::zero; + else { + gatePortValues(inst, left, input_port, port_values); + gatePortValues(inst, right, input_port, port_values); + } + break; + case FuncExpr::op_xor: + // Need to know timing arc sense to get this right. + if (left->port() == input_port + && right->op() == FuncExpr::op_port) + port_values[right->port()] = LogicValue::zero; + else if (right->port() == input_port + && left->op() == FuncExpr::op_port) + port_values[left->port()] = LogicValue::zero; + else { + gatePortValues(inst, left, input_port, port_values); + gatePortValues(inst, right, input_port, port_values); + } + break; + case FuncExpr::op_one: + case FuncExpr::op_zero: + break; + } +} + +void +WritePathSpice::seqPortValues(Sequential *seq, + const TransRiseFall *tr, + // Return values. + LibertyPortLogicValues &port_values) +{ + auto data = seq->data(); + auto port = onePort(data); + if (port) { + auto sense = data->portTimingSense(port); + switch (sense) { + case TimingSense::positive_unate: + if (tr == TransRiseFall::rise()) + port_values[port] = LogicValue::rise; + else + port_values[port] = LogicValue::fall; + break; + case TimingSense::negative_unate: + if (tr == TransRiseFall::rise()) + port_values[port] = LogicValue::fall; + else + port_values[port] = LogicValue::rise; + break; + case TimingSense::non_unate: + case TimingSense::none: + case TimingSense::unknown: + default: + break; + } + } +} + +// Pick a port, any port... +LibertyPort * +WritePathSpice::onePort(FuncExpr *expr) +{ + auto left = expr->left(); + auto 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; + } +} + +// Sort predicate for ParasiticDevices. +class ParasiticDeviceLess +{ +public: + ParasiticDeviceLess(Parasitics *parasitics) : + parasitics_(parasitics) + { + } + bool operator()(const ParasiticDevice *device1, + const ParasiticDevice *device2) const + { + auto node1 = parasitics_->node1(device1); + auto node2 = parasitics_->node1(device2); + auto name1 = parasitics_->name(node1); + auto name2 = parasitics_->name(node2); + if (stringEq(name1, name2)) { + auto node12 = parasitics_->node2(device1); + auto node22 = parasitics_->node2(device2); + auto name12 = parasitics_->name(node12); + auto name22 = parasitics_->name(node22); + return stringLess(name12, name22); + } + else + return stringLess(name1, name2); + } +private: + Parasitics *parasitics_; +}; + +// Sort predicate for ParasiticDevices. +class ParasiticNodeLess +{ +public: + ParasiticNodeLess(Parasitics *parasitics) : + parasitics_(parasitics) + { + } + bool operator()(const ParasiticNode *node1, + const ParasiticNode *node2) const + { + auto name1 = parasitics_->name(node1); + auto name2 = parasitics_->name(node2); + return stringLess(name1, name2); + } +private: + Parasitics *parasitics_; +}; + +void +WritePathSpice::writeStageParasitics(Stage stage) +{ + auto drvr_path = stageDrvrPath(stage); + auto drvr_pin = stageDrvrPin(stage); + auto dcalc_ap = drvr_path->dcalcAnalysisPt(this); + auto parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + auto parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + Set reachable_pins; + int res_index = 1; + int cap_index = 1; + if (parasitic) { + auto net = network_->net(drvr_pin); + auto net_name = net ? network_->pathName(net) : network_->pathName(drvr_pin); + initNodeMap(net_name); + streamPrint(spice_stream_, "* Net %s\n", net_name); + + // Sort devices for consistent regression results. + Vector devices; + ParasiticDeviceIterator *device_iter1 = parasitics_->deviceIterator(parasitic); + while (device_iter1->hasNext()) { + auto device = device_iter1->next(); + devices.push_back(device); + } + delete device_iter1; + + sort(devices, ParasiticDeviceLess(parasitics_)); + + for (auto device : devices) { + auto resistance = parasitics_->value(device, parasitic_ap); + if (parasitics_->isResistor(device)) { + auto node1 = parasitics_->node1(device); + auto node2 = parasitics_->node2(device); + streamPrint(spice_stream_, "R%d %s %s %.3e\n", + res_index, + nodeName(node1), + nodeName(node2), + resistance); + res_index++; + + auto pin1 = parasitics_->connectionPin(node1); + reachable_pins.insert(pin1); + auto pin2 = parasitics_->connectionPin(node2); + reachable_pins.insert(pin2); + } + else if (parasitics_->isCouplingCap(device)) { + // Ground coupling caps for now. + ParasiticNode *node1 = parasitics_->node1(device); + auto cap = parasitics_->value(device, parasitic_ap); + streamPrint(spice_stream_, "C%d %s 0 %.3e\n", + cap_index, + nodeName(node1), + cap); + cap_index++; + } + } + } + else + streamPrint(spice_stream_, "* No parasitics found for this net.\n"); + + // Add resistors from drvr to load for missing parasitic connections. + auto pin_iter = network_->connectedPinIterator(drvr_pin); + while (pin_iter->hasNext()) { + auto pin = pin_iter->next(); + if (pin != drvr_pin + && network_->isLoad(pin) + && !network_->isHierarchical(pin) + && !reachable_pins.hasKey(pin)) { + streamPrint(spice_stream_, "R%d %s %s %.3e\n", + res_index, + network_->pathName(drvr_pin), + network_->pathName(pin), + short_ckt_resistance_); + res_index++; + } + } + delete pin_iter; + + if (parasitic) { + // Sort node capacitors for consistent regression results. + Vector nodes; + ParasiticNodeIterator *node_iter = parasitics_->nodeIterator(parasitic); + while (node_iter->hasNext()) { + auto node = node_iter->next(); + nodes.push_back(node); + } + + sort(nodes, ParasiticNodeLess(parasitics_)); + + for (auto node : nodes) { + auto cap = parasitics_->nodeGndCap(node, parasitic_ap); + // Spice has a cow over zero value caps. + if (cap > 0.0) { + streamPrint(spice_stream_, "C%d %s 0 %.3e\n", + cap_index, + nodeName(node), + cap); + cap_index++; + } + } + delete node_iter; + } +} + +void +WritePathSpice::initNodeMap(const char *net_name) +{ + stringDelete(net_name_); + node_map_.clear(); + next_node_index_ = 1; + net_name_ = stringCopy(net_name); +} + +const char * +WritePathSpice::nodeName(ParasiticNode *node) +{ + auto pin = parasitics_->connectionPin(node); + if (pin) + return parasitics_->name(node); + else { + int node_index; + bool node_index_exists; + node_map_.findKey(node, node_index, node_index_exists); + if (!node_index_exists) { + node_index = next_node_index_++; + node_map_[node] = node_index; + } + return stringPrintTmp("%s/%d", net_name_, node_index); + } +} + +//////////////////////////////////////////////////////////////// + +// Copy the subckt definition from lib_subckt_filename for +// each cell in path to path_subckt_filename. +void +WritePathSpice::writeSubckts() +{ + StringSet path_cell_names; + findPathCellnames(path_cell_names); + + ifstream lib_subckts_stream(lib_subckt_filename_); + if (lib_subckts_stream.is_open()) { + ofstream subckts_stream(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")) { + auto cell_name = tokens[1].c_str(); + if (path_cell_names.hasKey(cell_name)) { + 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_); + path_cell_names.erase(cell_name); + } + recordSpicePortNames(cell_name, tokens); + } + } + subckts_stream.close(); + lib_subckts_stream.close(); + + if (!path_cell_names.empty()) { + report_->error("The following subkcts are missing from %s\n", + lib_subckt_filename_); + for (auto cell_name : path_cell_names) + report_->printError(" %s\n", cell_name); + } + } + else { + lib_subckts_stream.close(); + throw FileNotWritable(subckt_filename_); + } + } + else + throw FileNotReadable(lib_subckt_filename_); +} + +void +WritePathSpice::findPathCellnames(// Return values. + StringSet &path_cell_names) +{ + for (auto stage = stageFirst(); stage <= stageLast(); stage++) { + auto arc = stageGateArc(stage); + if (arc) { + auto cell = arc->set()->libertyCell(); + if (cell) { + debugPrint1(debug_, "write_spice", 2, "cell %s\n", cell->name()); + path_cell_names.insert(cell->name()); + } + // Include side receivers. + auto drvr_pin = stageDrvrPin(stage); + auto pin_iter = network_->connectedPinIterator(drvr_pin); + while (pin_iter->hasNext()) { + auto pin = pin_iter->next(); + auto port = network_->libertyPort(pin); + if (port) { + auto cell = port->libertyCell(); + path_cell_names.insert(cell->name()); + } + } + delete pin_iter; + } + } +} + +void +WritePathSpice::recordSpicePortNames(const char *cell_name, + StringVector &tokens) +{ + auto cell = network_->findLibertyCell(cell_name); + if (cell) { + auto spice_port_names = new StringVector; + for (size_t i = 2; i < tokens.size(); i++) { + auto port_name = tokens[i].c_str(); + auto port = cell->findLibertyPort(port_name); + auto pg_port = cell->findPgPort(port_name); + if (port == nullptr + && pg_port == nullptr + && !stringEqual(port_name, power_name_) + && !stringEqual(port_name, gnd_name_)) + report_->error("subckt %s port %s has no corresponding liberty port, pg_port and is not power or ground.\n", + cell_name, port_name); + spice_port_names->push_back(port_name); + } + cell_spice_port_names_[cell_name] = spice_port_names; + } +} + +//////////////////////////////////////////////////////////////// + +Stage +WritePathSpice::stageFirst() +{ + return 1; +} + +Stage +WritePathSpice::stageLast() +{ + return (path_expanded_.size() + 1) / 2; +} + +string +WritePathSpice::stageName(Stage stage) +{ + string name; + stringPrint(name, "stage%d", stage); + return name; +} + +int +WritePathSpice::stageGateInputPathIndex(Stage stage) +{ + return stage * 2 - 3; +} + +int +WritePathSpice::stageDrvrPathIndex(Stage stage) +{ + return stage * 2 - 2; +} + +int +WritePathSpice::stageLoadPathIndex(Stage stage) +{ + return stage * 2 - 1; +} + +PathRef * +WritePathSpice::stageGateInputPath(Stage stage) +{ + auto path_index = stageGateInputPathIndex(stage); + return path_expanded_.path(path_index); +} + +PathRef * +WritePathSpice::stageDrvrPath(Stage stage) +{ + auto path_index = stageDrvrPathIndex(stage); + return path_expanded_.path(path_index); +} + +PathRef * +WritePathSpice::stageLoadPath(Stage stage) +{ + auto path_index = stageLoadPathIndex(stage); + return path_expanded_.path(path_index); +} + +TimingArc * +WritePathSpice::stageGateArc(Stage stage) +{ + auto path_index = stageDrvrPathIndex(stage); + if (path_index >= 0) + return path_expanded_.prevArc(path_index); + else + return nullptr; +} + +TimingArc * +WritePathSpice::stageWireArc(Stage stage) +{ + auto path_index = stageLoadPathIndex(stage); + return path_expanded_.prevArc(path_index); +} + +Edge * +WritePathSpice::stageGateEdge(Stage stage) +{ + auto path = stageDrvrPath(stage); + auto arc = stageGateArc(stage); + return path->prevEdge(arc, this); +} + +Edge * +WritePathSpice::stageWireEdge(Stage stage) +{ + auto path = stageLoadPath(stage); + auto arc = stageWireArc(stage); + return path->prevEdge(arc, this); +} + +Pin * +WritePathSpice::stageGateInputPin(Stage stage) +{ + auto path = stageGateInputPath(stage); + return path->pin(this); +} + +LibertyPort * +WritePathSpice::stageGateInputPort(Stage stage) +{ + auto pin = stageGateInputPin(stage); + return network_->libertyPort(pin); +} + +Pin * +WritePathSpice::stageDrvrPin(Stage stage) +{ + auto path = stageDrvrPath(stage); + return path->pin(this); +} + +LibertyPort * +WritePathSpice::stageDrvrPort(Stage stage) +{ + auto pin = stageDrvrPin(stage); + return network_->libertyPort(pin); +} + +Pin * +WritePathSpice::stageLoadPin(Stage stage) +{ + auto path = stageLoadPath(stage); + return path->pin(this); +} + +const char * +WritePathSpice::stageGateInputPinName(Stage stage) +{ + auto pin = stageGateInputPin(stage); + return network_->pathName(pin); +} + +const char * +WritePathSpice::stageDrvrPinName(Stage stage) +{ + auto pin = stageDrvrPin(stage); + return network_->pathName(pin); +} + +const char * +WritePathSpice::stageLoadPinName(Stage stage) +{ + auto pin = stageLoadPin(stage); + return network_->pathName(pin); +} + +Instance * +WritePathSpice::stageInstance(Stage stage) +{ + auto pin = stageDrvrPin(stage); + return network_->instance(pin); +} + +LibertyCell * +WritePathSpice::stageLibertyCell(Stage stage) +{ + auto pin = stageDrvrPin(stage); + return network_->libertyPort(pin)->libertyCell(); +} + +//////////////////////////////////////////////////////////////// + +// 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; + if (vasprintf(&result, fmt, args) == -1) + internalError("out of memory"); + stream << result; + free(result); + va_end(args); +} + +} // namespace diff --git a/search/WritePathSpice.hh b/search/WritePathSpice.hh new file mode 100644 index 0000000..e49cdcb --- /dev/null +++ b/search/WritePathSpice.hh @@ -0,0 +1,39 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_WRITE_PATH_SPICE_H +#define STA_WRITE_PATH_SPICE_H + +namespace sta { + +// 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, + StaState *sta); + +} // namespace +#endif diff --git a/search/WriteSpice.cc b/search/WriteSpice.cc new file mode 100644 index 0000000..2fc8872 --- /dev/null +++ b/search/WriteSpice.cc @@ -0,0 +1,946 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include +#include +#include "Machine.hh" +#include "Debug.hh" +#include "Error.hh" +#include "Report.hh" +#include "StringUtil.hh" +#include "FuncExpr.hh" +#include "Liberty.hh" +#include "TimingArc.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Sdc.hh" +#include "DcalcAnalysisPt.hh" +#include "Parasitics.hh" +#include "PathAnalysisPt.hh" +#include "Path.hh" +#include "PathRef.hh" +#include "PathExpanded.hh" +#include "StaState.hh" +#include "WriteSpice.hh" + +namespace sta { + +using std::string; +using std::ofstream; +using std::ifstream; + +typedef Vector StringVector; +typedef Map CellSpicePortNames; +typedef int Stage; +typedef Map ParasiticNodeMap; + +void +split(const string &text, + const string &delims, + // Return values. + StringVector &tokens); +void +streamPrint(ofstream &stream, + const char *fmt, + ...) __attribute__((format (printf, 2, 3))); +void +stringPrint(string &str, + const char *fmt, + ...) __attribute__((format (printf, 2, 3))); + +//////////////////////////////////////////////////////////////// + +class WriteSpice : public StaState +{ +public: + WriteSpice(Path *path, + const char *spice_filename, + const char *subckts_filename, + const char *lib_subckts_filename, + const char *models_filename, + const StaState *sta); + ~WriteSpice(); + void writeSpice();; + +private: + void writeHeader(); + void writeStageInstances(); + void writeInputSource(); + void writeStageSubckts(); + void writeInputStage(Stage stage); + void writeMeasureStmts(); + void writeGateStage(Stage stage); + void writeStageVoltageSources(LibertyCell *cell, + StringVector *spice_port_names, + const char *inst_name, + LibertyPort *from_port, + LibertyPort *drvr_port); + void writeStageParasitics(Stage stage); + void writeSubckts(); + void findPathCellnames(// Return values. + StringSet &path_cell_names); + void recordSpicePortNames(const char *cell_name, + StringVector &tokens); + float pgPortVoltage(const char *pg_port_name, + LibertyCell *cell); + float pgPortVoltage(LibertyPgPort *pg_port); + float maxTime(); + const char *nodeName(ParasiticNode *node); + void initNodeMap(const char *net_name); + + // Stage "accessors". + // Internally a stage index from stageFirst() to stageLast() + // is turned into an index into path_expanded_. + Stage stageFirst(); + Stage stageLast(); + string stageName(Stage stage); + int stageGateInputPathIndex(Stage stage); + int stageDrvrPathIndex(Stage stage); + int stageLoadPathIndex(Stage stage); + PathRef *stageGateInputPath(Stage stage); + PathRef *stageDrvrPath(Stage stage); + PathRef *stageLoadPath(Stage stage); + TimingArc *stageGateArc(Stage stage); + TimingArc *stageWireArc(Stage stage); + Edge *stageGateEdge(Stage stage); + Edge *stageWireEdge(Stage stage); + Pin *stageInputPin(Stage stage); + Pin *stageDrvrPin(Stage stage); + Pin *stageLoadPin(Stage stage); + const char *stageInputPinName(Stage stage); + const char *stageDrvrPinName(Stage stage); + const char *stageLoadPinName(Stage stage); + + Path *path_; + const char *spice_filename_; + const char *subckts_filename_; + const char *lib_subckts_filename_; + const char *models_filename_; + + ofstream spice_stream_; + PathExpanded path_expanded_; + CellSpicePortNames cell_spice_port_names_; + ParasiticNodeMap node_map_; + int next_node_index_; + const char *net_name_; + + // Resistance to use to simulate a short circuit between spice nodes. + static const float short_ckt_resistance_; +}; + +//////////////////////////////////////////////////////////////// + +class SubcktEndsMissing : public StaException +{ +public: + SubcktEndsMissing(const char *cell_name, + const char *subckt_filename);; + const char *what() const throw(); + +protected: + string what_; +}; + +SubcktEndsMissing::SubcktEndsMissing(const char *cell_name, + const char *subckt_filename) +{ + what_ = "Error: spice subckt for cell "; + what_ += cell_name; + what_ += " missing .ends in "; + what_ += subckt_filename; +} + +const char * +SubcktEndsMissing::what() const throw() +{ + return what_.c_str(); +} + +//////////////////////////////////////////////////////////////// + +void +writeSpice (Path *path, + const char *spice_filename, + const char *subckts_filename, + const char *lib_subckts_filename, + const char *models_filename, + StaState *sta) +{ + WriteSpice writer(path, spice_filename, subckts_filename, + lib_subckts_filename, models_filename, sta); + writer.writeSpice(); +} + +const float WriteSpice::short_ckt_resistance_ = .0001; + +WriteSpice::WriteSpice(Path *path, + const char *spice_filename, + const char *subckts_filename, + const char *lib_subckts_filename, + const char *models_filename, + const StaState *sta) : + StaState(sta), + path_(path), + spice_filename_(spice_filename), + subckts_filename_(subckts_filename), + lib_subckts_filename_(lib_subckts_filename), + models_filename_(models_filename), + path_expanded_(sta), + net_name_(NULL) +{ +} + +WriteSpice::~WriteSpice() +{ + cell_spice_port_names_.deleteContents(); +} + +void +WriteSpice::writeSpice() +{ + spice_stream_.open(spice_filename_); + if (spice_stream_.is_open()) { + path_expanded_.expand(path_, true); + // Find subckt port names as a side-effect of writeSubckts. + writeSubckts(); + writeHeader(); + writeStageInstances(); + writeInputSource(); + writeStageSubckts(); + streamPrint(spice_stream_, ".end\n"); + spice_stream_.close(); + } + else + throw FileNotWritable(spice_filename_); +} + +void +WriteSpice::writeHeader() +{ + const MinMax *min_max = path_->minMax(this); + const Pvt *pvt = sdc_->operatingConditions(min_max); + if (pvt == NULL) + pvt = network_->defaultLibertyLibrary()->defaultOperatingConditions(); + float temp = pvt->temperature(); + streamPrint(spice_stream_, ".temp %.1f\n", temp); + streamPrint(spice_stream_, ".include \"%s\"\n", models_filename_); + streamPrint(spice_stream_, ".include \"%s\"\n", subckts_filename_); + + float max_time = maxTime(); + float time_step = max_time / 1e+3; + streamPrint(spice_stream_, ".tran %.3g %.3g\n\n", + time_step, max_time); +} + +float +WriteSpice::maxTime() +{ + float end_slew = path_->slew(this); + float max_time = (path_->arrival(this) + end_slew * 2) * 1.5; + return max_time; +} + +void +WriteSpice::writeStageInstances() +{ + streamPrint(spice_stream_, "*****************\n"); + streamPrint(spice_stream_, "* Stage instances\n"); + streamPrint(spice_stream_, "*****************\n\n"); + + for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { + const char *stage_name = stageName(stage).c_str(); + if (stage == stageFirst()) + streamPrint(spice_stream_, "x%s %s %s %s\n", + stage_name, + stageDrvrPinName(stage), + stageLoadPinName(stage), + stage_name); + else + streamPrint(spice_stream_, "x%s %s %s %s %s\n", + stage_name, + stageInputPinName(stage), + stageDrvrPinName(stage), + stageLoadPinName(stage), + stage_name); + } + streamPrint(spice_stream_, "\n"); +} + +float +WriteSpice::pgPortVoltage(const char *pg_port_name, + LibertyCell *cell) +{ + auto pg_port = cell->findPgPort(pg_port_name); + return pgPortVoltage(pg_port); +} + +float +WriteSpice::pgPortVoltage(LibertyPgPort *pg_port) +{ + auto cell = pg_port->cell(); + auto voltage_name = pg_port->voltageName(); + auto lib = cell->libertyLibrary(); + float voltage = lib->supplyVoltage(voltage_name); + return voltage; +} + +void +WriteSpice::writeInputSource() +{ + streamPrint(spice_stream_, "**************\n"); + streamPrint(spice_stream_, "* Input source\n"); + streamPrint(spice_stream_, "**************\n\n"); + + Stage input_stage = stageFirst(); + streamPrint(spice_stream_, "v1 %s 0 pwl(\n", + stageDrvrPinName(input_stage)); + auto wire_arc = stageWireArc(input_stage); + auto load_pin = stageLoadPin(input_stage); + auto cell = network_->libertyCell(network_->instance(load_pin)); + auto load_port = network_->libertyPort(load_pin); + const char *pg_gnd_port_name = load_port->relatedGroundPin(); + const char *pg_pwr_port_name = load_port->relatedPowerPin(); + auto gnd_volt = pgPortVoltage(pg_gnd_port_name, cell); + auto pwr_volt = pgPortVoltage(pg_pwr_port_name, cell); + float volt0, volt1; + if (wire_arc->fromTrans()->asRiseFall() == TransRiseFall::rise()) { + volt0 = gnd_volt; + volt1 = pwr_volt; + } + else { + volt0 = pwr_volt; + volt1 = gnd_volt; + } + float time0 = .1e-9; + float time1 = .2e-9; + streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0); + streamPrint(spice_stream_, "+%.3e %.3e\n", time0, volt0); + streamPrint(spice_stream_, "+%.3e %.3e\n", time1, volt1); + streamPrint(spice_stream_, "+%.3e %.3e\n", maxTime(), volt1); + streamPrint(spice_stream_, "+)\n\n"); +} + +void +WriteSpice::writeMeasureStmts() +{ + streamPrint(spice_stream_, "********************\n"); + streamPrint(spice_stream_, "* Measure statements\n"); + streamPrint(spice_stream_, "********************\n\n"); +} + +void +WriteSpice::writeStageSubckts() +{ + streamPrint(spice_stream_, "***************\n"); + streamPrint(spice_stream_, "* Stage subckts\n"); + streamPrint(spice_stream_, "***************\n\n"); + + for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { + if (stage == stageFirst()) + writeInputStage(stage); + else + writeGateStage(stage); + } +} + +// Input port to first gate input. +void +WriteSpice::writeInputStage(Stage stage) +{ + // Input arc. + // External driver not handled. + auto drvr_pin_name = stageDrvrPinName(stage); + auto load_pin_name = stageLoadPinName(stage); + streamPrint(spice_stream_, ".subckt %s %s %s\n", + stageName(stage).c_str(), + drvr_pin_name, + load_pin_name); + writeStageParasitics(stage); + streamPrint(spice_stream_, ".ends\n\n"); +} + +// Gate and load parasitics. +void +WriteSpice::writeGateStage(Stage stage) +{ + auto input_pin = stageInputPin(stage); + auto input_pin_name = stageInputPinName(stage); + auto drvr_pin = stageDrvrPin(stage); + auto drvr_pin_name = stageDrvrPinName(stage); + auto load_pin_name = stageLoadPinName(stage); + streamPrint(spice_stream_, ".subckt stage%d %s %s %s\n", + stage, + input_pin_name, + drvr_pin_name, + load_pin_name); + Instance *inst = network_->instance(input_pin); + const char *inst_name = network_->pathName(inst); + LibertyCell *cell = network_->libertyCell(inst); + const char *cell_name = cell->name(); + auto spice_port_names = cell_spice_port_names_[cell_name]; + + // Instance subckt call. + streamPrint(spice_stream_, "x%s", inst_name); + StringVector::Iterator port_iter(spice_port_names); + while (port_iter.hasNext()) { + const char *subckt_port_name = port_iter.next().c_str(); + auto pin = network_->findPin(inst, subckt_port_name); + auto pg_port = cell->findPgPort(subckt_port_name); + const char *pin_name; + if (pin) { + pin_name = network_->pathName(pin); + streamPrint(spice_stream_, " %s", pin_name); + } + else if (pg_port) + streamPrint(spice_stream_, " %s/%s", inst_name, subckt_port_name); + } + streamPrint(spice_stream_, " %s\n", cell_name); + + writeStageVoltageSources(cell, spice_port_names, + inst_name, + network_->libertyPort(input_pin), + network_->libertyPort(drvr_pin)); + writeStageParasitics(stage); + streamPrint(spice_stream_, ".ends\n\n"); +} + +typedef Map LibertyPortLogicValues; + +// Find the logic values for expression inputs to enable paths from_port. +void +sensitizationValues(FuncExpr *expr, + LibertyPort *from_port, + // Return values. + LibertyPortLogicValues &port_values) +{ + switch (expr->op()) { + case FuncExpr::op_port: { + break; + } + case FuncExpr::op_not: { + sensitizationValues(expr->left(), from_port, port_values); + break; + } + case FuncExpr::op_or: { + FuncExpr *left = expr->left(); + FuncExpr *right = expr->right(); + if (left->port() == from_port + && right->op() == FuncExpr::op_port) + port_values[right->port()] = logic_zero; + else if (right->port() == from_port + && left->op() == FuncExpr::op_port) + port_values[left->port()] = logic_zero; + break; + } + case FuncExpr::op_and: { + FuncExpr *left = expr->left(); + FuncExpr *right = expr->right(); + if (left->port() == from_port + && right->op() == FuncExpr::op_port) + port_values[right->port()] = logic_one; + else if (right->port() == from_port + && left->op() == FuncExpr::op_port) + port_values[left->port()] = logic_one; + break; + } + case FuncExpr::op_xor: { + // Need to know timing arc sense to get this right. + FuncExpr *left = expr->left(); + FuncExpr *right = expr->right(); + if (left->port() == from_port + && right->op() == FuncExpr::op_port) + port_values[right->port()] = logic_zero; + else if (right->port() == from_port + && left->op() == FuncExpr::op_port) + port_values[left->port()] = logic_zero; + break; + } + case FuncExpr::op_one: + case FuncExpr::op_zero: + break; + } +} + +// Power/ground and input voltage sources. +void +WriteSpice::writeStageVoltageSources(LibertyCell *cell, + StringVector *spice_port_names, + const char *inst_name, + LibertyPort *from_port, + LibertyPort *drvr_port) +{ + auto from_port_name = from_port->name(); + auto drvr_port_name = drvr_port->name(); + LibertyLibrary *lib = cell->libertyLibrary(); + LibertyPortLogicValues port_values; + sensitizationValues(drvr_port->function(), from_port, port_values); + int volt_source = 1; + debugPrint1(debug_, "write_spice", 2, "subckt %s\n", cell->name()); + StringVector::Iterator port_iter(spice_port_names); + while (port_iter.hasNext()) { + auto subckt_port_name = port_iter.next().c_str(); + auto pg_port = cell->findPgPort(subckt_port_name); + debugPrint2(debug_, "write_spice", 2, " port %s%s\n", + subckt_port_name, + pg_port ? " pwr/gnd" : ""); + if (pg_port) { + auto voltage = pgPortVoltage(pg_port); + streamPrint(spice_stream_, "v%d %s/%s 0 %.3f\n", + volt_source, + inst_name, subckt_port_name, + voltage); + volt_source++; + } else if (!(stringEq(subckt_port_name, from_port_name) + || stringEq(subckt_port_name, drvr_port_name))) { + // Input voltage to sensitize path from gate input to output. + LibertyPort *port = cell->findLibertyPort(subckt_port_name); + if (port) { + const char *pg_port_name = NULL; + bool port_has_value; + LogicValue port_value; + port_values.findKey(port, port_value, port_has_value); + if (port_has_value) { + switch (port_value) { + case logic_zero: + pg_port_name = port->relatedGroundPin(); + break; + case logic_one: + pg_port_name = port->relatedPowerPin(); + break; + default: + break; + } + if (pg_port_name) { + auto pg_port = cell->findPgPort(pg_port_name); + if (pg_port) { + auto voltage_name = pg_port->voltageName(); + if (voltage_name) { + float voltage = lib->supplyVoltage(voltage_name); + streamPrint(spice_stream_, "v%d %s/%s 0 %.3f\n", + volt_source, + inst_name, subckt_port_name, + voltage); + volt_source++; + } + else + report_->error("port %s %s voltage %s not found,\n", + subckt_port_name, + pg_port_name, + voltage_name); + } + else + report_->error("port %s %s not found,\n", + subckt_port_name, + pg_port_name); + } + } + } + } + } +} + +typedef Set ParasiticDeviceSet; +typedef Set ParasiticNodeSet; + +void +findParasiticDevicesNodes(ParasiticNode *node, + Parasitics *parasitics, + // Return values. + ParasiticNodeSet &nodes, + ParasiticDeviceSet &devices) +{ + nodes.insert(node); + auto device_iter = parasitics->deviceIterator(node); + while (device_iter->hasNext()) { + auto device = device_iter->next(); + if (!devices.hasKey(device)) { + devices.insert(device); + auto other_node = parasitics->otherNode(device, node); + findParasiticDevicesNodes(other_node, parasitics, nodes, devices); + } + } + delete device_iter; +} + +void +WriteSpice::writeStageParasitics(Stage stage) +{ + auto drvr_path = stageDrvrPath(stage); + auto drvr_pin = stageDrvrPin(stage); + auto load_pin = stageLoadPin(stage); + auto dcalc_ap = drvr_path->dcalcAnalysisPt(this); + auto parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + auto parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + int resistor_index = 1; + int cap_index = 1; + if (parasitic) { + Net *net = network_->net(drvr_pin); + auto net_name = + net ? network_->pathName(net) : network_->pathName(drvr_pin); + initNodeMap(net_name); + streamPrint(spice_stream_, "* Net %s\n", net_name); + auto node = parasitics_->findNode(parasitic, drvr_pin); + ParasiticNodeSet nodes; + ParasiticDeviceSet devices; + findParasiticDevicesNodes(node, parasitics_, nodes, devices); + ParasiticDeviceSet::Iterator device_iter(devices); + while (device_iter.hasNext()) { + auto device = device_iter.next(); + auto resistance = parasitics_->value(device, parasitic_ap); + if (parasitics_->isResistor(device)) { + ParasiticNode *node1, *node2; + parasitics_->resistorNodes(device, node1, node2); + streamPrint(spice_stream_, "R%d %s %s %.3e\n", + resistor_index, + nodeName(node1), + nodeName(node2), + resistance); + resistor_index++; + } + else if (parasitics_->isCouplingCap(device)) { + } + } + ParasiticNodeSet::Iterator node_iter(nodes); + while (node_iter.hasNext()) { + auto node = node_iter.next(); + auto cap = parasitics_->nodeGndCap(node, parasitic_ap); + streamPrint(spice_stream_, "C%d %s 0 %.3e\n", + cap_index, + nodeName(node), + cap); + cap_index++; + } + } + else + streamPrint(spice_stream_, "R1 %s %s %.3e\n", + network_->pathName(drvr_pin), + network_->pathName(load_pin), + short_ckt_resistance_); +} + +void +WriteSpice::initNodeMap(const char *net_name) +{ + stringDelete(net_name_); + node_map_.clear(); + next_node_index_ = 1; + net_name_ = stringCopy(net_name); +} + +const char * +WriteSpice::nodeName(ParasiticNode *node) +{ + auto pin = parasitics_->connectionPin(node); + if (pin) + return parasitics_->name(node); + else { + int node_index; + bool node_index_exists; + node_map_.findKey(node, node_index, node_index_exists); + if (!node_index_exists) { + node_index = next_node_index_++; + node_map_[node] = node_index; + } + return stringPrintTmp(strlen(net_name_) + 10, "%s/%d", + net_name_, node_index); + } +} + +//////////////////////////////////////////////////////////////// + +// Copy the subckt definition from lib_subckts_filename for +// each cell in path to path_subckts_filename. +void +WriteSpice::writeSubckts() +{ + StringSet path_cell_names; + findPathCellnames(path_cell_names); + + ifstream lib_subckts_stream(lib_subckts_filename_); + if (lib_subckts_stream.is_open()) { + ofstream subckts_stream(subckts_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 (path_cell_names.hasKey(cell_name)) { + subckts_stream << line << "\n"; + bool found_ends = false; + while (getline(lib_subckts_stream, line)) { + subckts_stream << line << "\n"; + if (stringEqual(line.c_str(), ".ends")) { + subckts_stream << "\n"; + found_ends = true; + break; + } + } + if (!found_ends) + throw SubcktEndsMissing(cell_name, lib_subckts_filename_); + path_cell_names.eraseKey(cell_name); + } + recordSpicePortNames(cell_name, tokens); + } + } + subckts_stream.close(); + lib_subckts_stream.close(); + + if (!path_cell_names.empty()) { + StringSet::Iterator cell_iter(path_cell_names); + report_->error("The following subkcts are missing from %s\n", + lib_subckts_filename_); + while (cell_iter.hasNext()) { + const char *cell_name = cell_iter.next(); + report_->printError(" %s\n", cell_name); + } + } + } + else { + lib_subckts_stream.close(); + throw FileNotWritable(subckts_filename_); + } + } + else + throw FileNotReadable(lib_subckts_filename_); +} + +void +WriteSpice::findPathCellnames(// Return values. + StringSet &path_cell_names) +{ + for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { + auto arc = stageGateArc(stage); + if (arc) { + LibertyCell *cell = arc->set()->libertyCell(); + if (cell) { + debugPrint1(debug_, "write_spice", 2, "cell %s\n", cell->name()); + path_cell_names.insert(cell->name()); + } + } + } +} + +void +WriteSpice::recordSpicePortNames(const char *cell_name, + StringVector &tokens) +{ + auto cell = network_->findLibertyCell(cell_name); + auto spice_port_names = new StringVector; + for (int i = 2; i < tokens.size(); i++) { + const char *port_name = tokens[i].c_str(); + auto port = cell->findLibertyPort(port_name); + auto pg_port = cell->findPgPort(port_name); + if (port == NULL && pg_port == NULL) + report_->error("subckt %s port %s has no corresponding liberty port or pg_port.\n", + cell_name, port_name); + spice_port_names->push_back(port_name); + } + cell_spice_port_names_[cell_name] = spice_port_names; +} + +//////////////////////////////////////////////////////////////// + +Stage +WriteSpice::stageFirst() +{ + return 1; +} + +Stage +WriteSpice::stageLast() +{ + return (path_expanded_.size() + 1) / 2; +} + +string +WriteSpice::stageName(Stage stage) +{ + string name; + stringPrint(name, "stage%d", stage); + return name; +} + +int +WriteSpice::stageGateInputPathIndex(Stage stage) +{ + return stage * 2 - 3; +} + +int +WriteSpice::stageDrvrPathIndex(Stage stage) +{ + return stage * 2 - 2; +} + +int +WriteSpice::stageLoadPathIndex(Stage stage) +{ + return stage * 2 - 1; +} + +PathRef * +WriteSpice::stageGateInputPath(Stage stage) +{ + int path_index = stageGateInputPathIndex(stage); + return path_expanded_.path(path_index); +} + +PathRef * +WriteSpice::stageDrvrPath(Stage stage) +{ + int path_index = stageDrvrPathIndex(stage); + return path_expanded_.path(path_index); +} + +PathRef * +WriteSpice::stageLoadPath(Stage stage) +{ + int path_index = stageLoadPathIndex(stage); + return path_expanded_.path(path_index); +} + +TimingArc * +WriteSpice::stageGateArc(Stage stage) +{ + int path_index = stageDrvrPathIndex(stage); + if (path_index >= 0) + return path_expanded_.prevArc(path_index); + else + return NULL; +} + +TimingArc * +WriteSpice::stageWireArc(Stage stage) +{ + int path_index = stageLoadPathIndex(stage); + return path_expanded_.prevArc(path_index); +} + +Edge * +WriteSpice::stageGateEdge(Stage stage) +{ + PathRef *path = stageGateInputPath(stage); + TimingArc *arc = stageGateArc(stage); + return path->prevEdge(arc, this); +} + +Edge * +WriteSpice::stageWireEdge(Stage stage) +{ + PathRef *path = stageLoadPath(stage); + TimingArc *arc = stageWireArc(stage); + return path->prevEdge(arc, this); +} + +Pin * +WriteSpice::stageInputPin(Stage stage) +{ + PathRef *path = stageGateInputPath(stage); + return path->pin(this); +} + +Pin * +WriteSpice::stageDrvrPin(Stage stage) +{ + PathRef *path = stageDrvrPath(stage); + return path->pin(this); +} + +Pin * +WriteSpice::stageLoadPin(Stage stage) +{ + PathRef *path = stageLoadPath(stage); + return path->pin(this); +} + +const char * +WriteSpice::stageInputPinName(Stage stage) +{ + const Pin *pin = stageInputPin(stage); + return network_->pathName(pin); +} + +const char * +WriteSpice::stageDrvrPinName(Stage stage) +{ + Pin *pin = stageDrvrPin(stage); + return network_->pathName(pin); +} + +const char * +WriteSpice::stageLoadPinName(Stage stage) +{ + const Pin *pin = stageLoadPin(stage); + return network_->pathName(pin); +} + +//////////////////////////////////////////////////////////////// + +void +split(const string &text, + const string &delims, + // Return values. + StringVector &tokens) +{ + auto start = text.find_first_not_of(delims); + auto end = text.find_first_of(delims, start); + while (end != 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) + tokens.push_back(text.substr(start)); +} + +// 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; + vasprintf(&result, fmt, args); + stream << result; + free(result); + va_end(args); +} + +// print for c++ strings. +void +stringPrint(string &str, + const char *fmt, + ...) +{ + va_list args; + va_start(args, fmt); + char *result; + vasprintf(&result, fmt, args); + str = result; + free(result); + va_end(args); +} + +} // namespace diff --git a/search/WriteSpice.hh b/search/WriteSpice.hh new file mode 100644 index 0000000..dcc4cb2 --- /dev/null +++ b/search/WriteSpice.hh @@ -0,0 +1,37 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_WRITE_SPICE_H +#define STA_WRITE_SPICE_H + +namespace sta { + +// Write a spice deck for path. +// Throws FileNotReadable, FileNotWritable, SubcktEndsMissing +void +writeSpice(Path *path, + // Spice file written for path. + const char *spice_filename, + // Subckts used by path included in spice file. + const char *subckts_filename, + // File of all cell spice subckt definitions. + const char *lib_subckts_filename, + // Device model file included in spice file. + const char *models_filename, + StaState *sta); + +} // namespace +#endif diff --git a/tcl/Cmds.tcl b/tcl/Cmds.tcl new file mode 100644 index 0000000..b80eb49 --- /dev/null +++ b/tcl/Cmds.tcl @@ -0,0 +1,2032 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +################################################################ +# +# Command helper functions. +# +################################################################ + +namespace eval sta { + +proc report_clock1 { clk } { + global sta_report_default_digits + + if { [$clk waveform_valid] } { + set digits $sta_report_default_digits + puts -nonewline [format "%-20s" [get_name $clk]] + puts -nonewline [format "%10s" [format_time [$clk period] $digits]] + puts -nonewline " " + set waveform [$clk waveform] + if { $waveform == {} } { + puts -nonewline " " + } else { + foreach edge $waveform { + puts -nonewline [format "%10s" [format_time $edge $digits]] + } + } + if {[$clk is_generated]} { + puts -nonewline " (generated)" + } + puts "" + } +} + +proc_redirect read_parasitics { + variable native + + if { $native } { + sta_warn "The read_parasitics command is deprecated. Use read_spef." + } + eval [concat read_spef $args] +} + +proc check_setup_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args keys {} flags {-verbose} 0 + # When nothing is everything. + if { $cmd_args == {} } { + set unconstrained_endpoints 1 + set multiple_clock 1 + set no_clock 1 + set no_input_delay 1 + set no_output_delay 1 + set loops 1 + set generated_clocks 1 + } else { + parse_key_args $cmd cmd_args keys {} \ + flags {-no_input_delay -no_output_delay -multiple_clock -no_clock \ + -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)] + set no_clock [info exists flags(-no_clock)] + set unconstrained_endpoints [info exists flags(-unconstrained_endpoints)] + set loops [info exists flags(-loops)] + set generated_clocks [info exists flags(-generated_clocks)] + } + 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] + foreach error $errors { + # First line is the error msg. + puts [lindex $error 0] + if { $verbose } { + foreach obj [lrange $error 1 end] { + puts " $obj" + } + } + } + # return value + expr [llength $errors] == 0 +} + +proc delete_objects_from_list_cmd { list objects } { + set list0 [lindex $list 0] + set list_is_object [is_object $list0] + set list_type [object_type $list0] + foreach obj $objects { + # If the list is a collection of tcl objects (returned by get_*), + # convert the obj to be removed from a name to an object of the same + # type. + if {$list_is_object && ![is_object $obj]} { + if {$list_type == "Clock"} { + 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] + } elseif {$list_type == "Pin"} { + set obj [find_pin $obj] + } elseif {$list_type == "Instance"} { + set obj [find_instance $obj] + } elseif {$list_type == "Net"} { + set obj [find_net $obj] + } elseif {$list_type == "LibertyLibrary"} { + set obj [find_liberty $obj] + } elseif {$list_type == "LibertyCell"} { + set obj [find_liberty_cell $obj] + } elseif {$list_type == "LibertyPort"} { + set obj [get_lib_pins $obj] + } else { + sta_error "unsupported object type $list_type." + } + } + set index [lsearch $list $obj] + if { $index != -1 } { + set list [lreplace $list $index $index] + } + } + return $list +} + +################################################################ + +proc get_timing_edges_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args \ + keys {-from -to -of_objects -filter} flags {} + check_argc_eq0 $cmd $cmd_args + + set arcs {} + if { [info exists keys(-of_objects)] } { + if { [info exists keys(-from)] \ + || [info exists keys(-from)] } { + sta_error "-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)] } { + set arcs [get_timing_arcs_from_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)] } { + set arcs [get_timing_arcs_to $keys(-to)] + } else { + cmd_usage_error $cmd + } + if [info exists keys(-filter)] { + set arcs [filter_timing_arcs1 $keys(-filter) $arcs] + } + return $arcs +} + +proc get_timing_arcs_objects { object_arg } { + parse_libcell_inst_arg $object_arg libcells insts + if { $insts != {} } { + set edges {} + foreach inst $insts { + lappend edges [instance_edges $inst] + } + return $edges + } elseif { $libcells != {} } { + set arc_sets {} + foreach libcell $libcells { + lappend arc_sets [libcell_timing_arc_sets $libcell] + } + return $arc_sets + } +} + +proc instance_edges { inst } { + set edges {} + set pin_iter [$inst pin_iterator] + while {[$pin_iter has_next]} { + set pin [$pin_iter next] + 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 + } + } + $edge_iter finish + } + } + $pin_iter finish + return $edges +} + +proc libcell_timing_arc_sets { libcell } { + set arc_sets {} + set arc_iter [$libcell timing_arc_set_iterator] + while { [$arc_iter has_next] } { + lappend arc_sets [$arc_iter next] + } + $arc_iter finish + return $arc_sets +} + +proc get_timing_arcs_from_to { from_pin_arg to_pin_arg } { + set edges {} + set from_pin [get_port_pin_error "from" $from_pin_arg] + set to_pin [get_port_pin_error "to" $to_pin_arg] + foreach from_vertex [$from_pin vertices] { + 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 + } + } + $edge_iter finish + } + } + return $edges +} + +proc get_timing_arcs_from { from_pin_arg } { + set from_pin [get_port_pin_error "from" $from_pin_arg] + set edges {} + foreach from_vertex [$from_pin vertices] { + set edge_iter [$from_vertex out_edge_iterator] + while {[$edge_iter has_next]} { + set edge [$edge_iter next] + lappend edges $edge + } + $edge_iter finish + } + return $edges +} + +proc get_timing_arcs_to { to_pin_arg } { + set to_pin [get_port_pin_error "to" $to_pin_arg] + set edges {} + foreach to_vertex [$to_pin vertices] { + set edge_iter [$to_vertex in_edge_iterator] + while {[$edge_iter has_next]} { + set edge [$edge_iter next] + lappend edges $edge + } + $edge_iter finish + } + return $edges +} + +proc filter_timing_arcs1 { filter objects } { + variable filter_regexp1 + variable filter_or_regexp + variable filter_and_regexp + set filtered_objects {} + # Ignore sub-exprs in filter_regexp1 for expr2 match var. + if { [regexp $filter_or_regexp $filter ignore expr1 \ + ignore ignore ignore expr2] } { + regexp $filter_regexp1 $expr1 ignore attr_name op arg + set filtered_objects1 [filter_timing_arcs $attr_name $op $arg $objects] + regexp $filter_regexp1 $expr2 ignore attr_name op arg + set filtered_objects2 [filter_timing_arcs $attr_name $op $arg $objects] + set filtered_objects [concat $filtered_objects1 $filtered_objects2] + } elseif { [regexp $filter_and_regexp $filter ignore expr1 \ + ignore ignore ignore expr2] } { + regexp $filter_regexp1 $expr1 ignore attr_name op arg + set filtered_objects [filter_timing_arcs $attr_name $op $arg $objects] + regexp $filter_regexp1 $expr2 ignore attr_name op arg + set filtered_objects [filter_timing_arcs $attr_name $op \ + $arg $filtered_objects] + } elseif { [regexp $filter_regexp1 $filter ignore attr_name op arg] } { + set filtered_objects [filter_timing_arcs $attr_name $op $arg $objects] + } else { + sta_error "unsupported -filter expression." + } + return $filtered_objects +} + +################################################################ + +proc set_assigned_delay_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args keys {-corner -from -to} \ + flags {-cell -net -rise -fall -max -min} + check_argc_eq1 $cmd $cmd_args + set corner [parse_corner keys] + set min_max [parse_min_max_all_check_flags flags] + set to_tr [parse_rise_fall_flags flags] + + if [info exists keys(-from)] { + set from_pins [get_port_pins_error "from_pins" $keys(-from)] + } else { + sta_error "$cmd missing -from argument." + } + if [info exists keys(-to)] { + set to_pins [get_port_pins_error "to_pins" $keys(-to)] + } else { + sta_error "$cmd missing -to argument." + } + + set delay [lindex $cmd_args 0] + if {![string is double $delay]} { + sta_error "$cmd delay is not a float." + } + set delay [time_ui_sta $delay] + + if {[info exists flags(-cell)] && [info exists flags(-net)]} { + sta_error "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 "$cmd pin [get_full_name $pin] is not attached to instance [get_full_name $inst]." + } + } + foreach pin $to_pins { + if {[$pin instance] != $inst} { + sta_error "$cmd pin [get_full_name $pin] is not attached to instance [get_full_name $inst]" + } + } + } + } elseif {![info exists flags(-net)]} { + sta_error "$cmd -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_tr $corner $min_max $delay + if { [llength $from_vertices] == 2 } { + set_assigned_delay1 [lindex $from_vertices 1] \ + $to_pins $to_tr $corner $min_max $delay + } + } +} + +proc set_assigned_delay1 { from_vertex to_pins to_tr corner min_max delay } { + foreach to_pin $to_pins { + set to_vertices [$to_pin vertices] + set_assigned_delay2 $from_vertex [lindex $to_vertices 0] \ + $to_tr $corner $min_max $delay + if { [llength $to_vertices] == 2 } { + # Bidirect driver. + set_assigned_delay2 $from_vertex [lindex $to_vertices 1] \ + $to_tr $corner $min_max $delay + } + } +} + +proc set_assigned_delay2 {from_vertex to_vertex to_tr corner min_max delay} { + 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]] } { + set arc_iter [$edge timing_arc_iterator] + while {[$arc_iter has_next]} { + set arc [$arc_iter next] + if { $to_tr == "rise_fall" \ + || $to_tr eq [$arc to_trans_name] } { + set_arc_delay $edge $arc $corner $min_max $delay + } + } + $arc_iter finish + } + } + $edge_iter finish +} + +################################################################ + +# -worst is ignored. +proc set_assigned_check_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args \ + keys {-from -to -corner -clock -cond} \ + flags {-setup -hold -recovery -removal -rise -fall -max -min -worst} + check_argc_eq1 $cmd $cmd_args + + if { [info exists keys(-from)] } { + set from_pins [get_port_pins_error "from_pins" $keys(-from)] + } else { + sta_error "$cmd missing -from argument." + } + set from_tr "rise_fall" + if { [info exists keys(-clock)] } { + set clk_arg $keys(-clock) + if { $clk_arg eq "rise" \ + || $clk_arg eq "fall" } { + set from_tr $clk_arg + } else { + sta_error "$cmd -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 "$cmd missing -to argument." + } + set to_tr [parse_rise_fall_flags flags] + set corner [parse_corner keys] + set min_max [parse_min_max_all_check_flags flags] + + if { [info exists flags(-setup)] } { + set role "setup" + } elseif { [info exists flags(-hold)] } { + set role "hold" + } elseif { [info exists flags(-recovery)] } { + set role "recovery" + } elseif { [info exists flags(-removal)] } { + set role "removal" + } else { + sta_error "$cmd missing -setup|-hold|-recovery|-removal check type.." + } + set cond "" + if { [info exists key(-cond)] } { + set cond $key(-cond) + } + set check_value [lindex $cmd_args 0] + if { ![string is double $check_value] } { + sta_error "$cmd 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_tr \ + $to_pins $to_tr $role $corner $min_max $cond $check_value + if { [llength $from_vertices] == 2 } { + set_assigned_check1 [lindex $from_vertices 1] $from_tr \ + $to_pins $to_tr $role $corner $min_max $cond $check_value + } + } +} + +proc set_assigned_check1 { from_vertex from_tr to_pins to_tr \ + role corner min_max cond check_value } { + foreach to_pin $to_pins { + set to_vertices [$to_pin vertices] + set_assigned_check2 $from_vertex $from_tr [lindex $to_vertices 0] \ + $to_tr $role $corner $min_max $cond $check_value + if { [llength $to_vertices] == 2 } { + # Bidirect driver. + set_assigned_check2 $from_vertex $from_tr \ + [lindex $to_vertices 1] $to_tr $role $corner $min_max \ + $cond $check_value + } + } +} + +proc set_assigned_check2 { from_vertex from_tr to_vertex to_tr \ + role corner min_max cond check_value } { + set edge_iter [$from_vertex out_edge_iterator] + while {[$edge_iter has_next]} { + set edge [$edge_iter next] + if { [$edge to] == $to_vertex } { + set arc_iter [$edge timing_arc_iterator] + while {[$arc_iter has_next]} { + set arc [$arc_iter next] + if { ($from_tr eq "rise_fall" \ + || $from_tr eq [$arc from_trans_name]) \ + && ($to_tr eq "rise_fall" \ + || $to_tr eq [$arc to_trans_name]) \ + && [$arc role] eq $role \ + && ($cond eq "" || [$arc sdf_cond] eq $cond) } { + set_arc_delay $edge $arc $corner $min_max $check_value + } + } + $arc_iter finish + } + } + $edge_iter finish +} + +################################################################ + +proc set_disable_inferred_clock_gating_cmd { objects } { + parse_inst_port_pin_arg $objects insts pins + foreach inst $insts { + disable_clock_gating_check_inst $inst + } + foreach pin $pins { + disable_clock_gating_check_pin $pin + } +} + +################################################################ + +proc unset_clk_groups_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args \ + keys {-name} \ + flags {-logically_exclusive -physically_exclusive -asynchronous -all} + + set all [info exists flags(-all)] + set names {} + if {[info exists keys(-name)]} { + set names $keys(-name) + } + + if { $all && $names != {} } { + sta_error "the -all and -name options are mutually exclusive." + } + if { !$all && $names == {} } { + sta_error "either -all or -name options must be specified." + } + + set logically_exclusive [info exists flags(-logically_exclusive)] + set physically_exclusive [info exists flags(-physically_exclusive)] + set asynchronous [info exists flags(-asynchronous)] + + if { ($logically_exclusive+$physically_exclusive+$asynchronous) == 0 } { + sta_error "one of -logically_exclusive, -physically_exclusive or -asynchronous is required." + } + if { ($logically_exclusive+$physically_exclusive+$asynchronous) > 1 } { + sta_error "the keywords -logically_exclusive, -physically_exclusive and -asynchronous are mutually exclusive." + } + + if { $all } { + if { $logically_exclusive } { + unset_clock_groups_logically_exclusive "NULL" + } elseif { $physically_exclusive } { + unset_clock_groups_physically_exclusive "NULL" + } elseif { $asynchronous } { + unset_clock_groups_asynchronous "NULL" + } + } else { + foreach name $names { + if { $logically_exclusive } { + unset_clock_groups_logically_exclusive $name + } elseif { $physically_exclusive } { + unset_clock_groups_physically_exclusive $name + } elseif { $asynchronous } { + unset_clock_groups_asynchronous $name + } + } + } +} + +################################################################ + +proc unset_clk_latency_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args keys {-clock} flags {-source} + check_argc_eq1 $cmd $cmd_args + set objects [lindex $cmd_args 0] + parse_clk_port_pin_arg $objects clks pins + set pin_clk "NULL" + if { [info exists keys(-clock)] } { + set pin_clk [get_clock_warn "clock" $keys(-clock)] + if { $clks != {} } { + sta_warn "-clock ignored for clock objects." + } + } + + if {[info exists flags(-source)]} { + # Source latency. + foreach clk $clks { + unset_clock_insertion_cmd $clk "NULL" + } + foreach pin $pins { + # Source only allowed on clocks and clock pins. + if { ![is_clock_pin $pin] } { + sta_error "-source '[$pin path_name]' is not a clock pin." + } + unset_clock_insertion_cmd $pin_clk $pin + } + } else { + # Latency. + foreach clk $clks { + unset_clock_latency_cmd $clk "NULL" + } + foreach pin $pins { + unset_clock_latency_cmd $pin_clk $pin + } + } +} + +################################################################ + +proc unset_clk_uncertainty_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args \ + keys {-from -rise_from -fall_from -to -rise_to -fall_to} \ + flags {-rise -fall -setup -hold} + + set min_max "min_max" + if { [info exists flags(-setup)] && ![info exists flags(-hold)] } { + set min_max "max" + } + if { [info exists flags(-hold)] && ![info exists flags(-setup)] } { + set min_max "min" + } + + if { [info exists keys(-from)] } { + set from_key "-from" + set from_tr "rise_fall" + } elseif { [info exists keys(-rise_from)] } { + set from_key "-rise_from" + set from_tr "rise" + } elseif { [info exists keys(-fall_from)] } { + set from_key "-fall_from" + set from_tr "fall" + } else { + set from_key "none" + } + + if { [info exists keys(-to)] } { + set to_key "-to" + set to_tr "rise_fall" + } elseif { [info exists keys(-rise_to)] } { + set to_key "-rise_to" + set to_tr "rise" + } elseif { [info exists keys(-fall_to)] } { + set to_key "-fall_to" + set to_tr "fall" + } else { + set to_key "none" + } + + if { $from_key != "none" && $to_key == "none" \ + || $from_key == "none" && $to_key != "none" } { + sta_error "-from/-to must be used together." + } elseif { $from_key != "none" && $to_key != "none" } { + # Inter-clock uncertainty. + check_argc_eq0 "unset_clock_uncertainty" $cmd_args + + # -from/-to can be lists. + set from_clks [get_clocks_warn "from_clocks" $keys($from_key)] + set to_clks [get_clocks_warn "to_clocks" $keys($to_key)] + + foreach from_clk $from_clks { + foreach to_clk $to_clks { + unset_inter_clock_uncertainty $from_clk $from_tr \ + $to_clk $to_tr $min_max + } + } + } else { + # Single clock uncertainty. + check_argc_eq1 $cmd $cmd_args + if { [info exists keys(-rise)] \ + || [info exists keys(-fall)] } { + sta_error "-rise, -fall options not allowed for single clock uncertainty." + } + set objects [lindex $cmd_args 0] + parse_clk_port_pin_arg $objects clks pins + + foreach clk $clks { + unset_clock_uncertainty_clk $clk $min_max + } + foreach pin $pins { + unset_clock_uncertainty_pin $pin $min_max + } + } +} + +################################################################ + +proc unset_data_checks_cmd { cmd cmd_args } { + parse_key_args cmd cmd_args \ + keys {-from -rise_from -fall_from -to -rise_to -fall_to -clock} \ + flags {-setup -hold} + check_argc_eq0 $cmd $cmd_args + + set from_tr "rise_fall" + set to_tr "rise_fall" + set clk "NULL" + set setup_hold "max" + if [info exists keys(-from)] { + set from [get_port_pin_error "from_pin" $keys(-from)] + } elseif [info exists keys(-rise_from)] { + set from [get_port_pin_error "from_pin" $keys(-rise_from)] + set from_tr "rise" + } elseif [info exists keys(-fall_from)] { + set from [get_port_pin_error "from_pin" $keys(-fall_from)] + set from_tr "fall" + } else { + sta_error "missing -from, -rise_from or -fall_from argument." + } + + if [info exists keys(-to)] { + set to [get_port_pin_error "to_pin" $keys(-to)] + } elseif [info exists keys(-rise_to)] { + set to [get_port_pin_error "to_pin" $keys(-rise_to)] + set to_tr "rise" + } elseif [info exists keys(-fall_to)] { + set to [get_port_pin_error "to_pin" $keys(-fall_to)] + set to_tr "fall" + } else { + sta_error "missing -to, -rise_to or -fall_to argument." + } + + if [info exists keys(-clock)] { + set clk [get_clock_warn "clock" $keys(-clock)] + } + + if { [info exists flags(-setup)] && ![info exists flags(-hold)] } { + set setup_hold "setup" + } elseif { [info exists flags(-hold)] && ![info exists flags(-setup)] } { + set setup_hold "hold" + } else { + set setup_hold "setup_hold" + } + + unset_data_check_cmd $from $from_tr $to $to_tr $clk $setup_hold +} + +################################################################ + +proc unset_disable_inferred_clock_gating_cmd { objects } { + parse_inst_port_pin_arg $objects insts pins + foreach inst $insts { + unset_disable_clock_gating_check_inst $inst + } + foreach pin $pins { + unset_disable_clock_gating_check_pin $pin + } +} + +################################################################ + +proc remove_gclk_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args keys {} flags {-all} + if { [info exists flags(-all)] } { + check_argc_eq0 $cmd $cmd_args + set clks [all_clocks] + } else { + check_argc_eq1 $cmd $cmd_args + set clks [get_clocks_warn "clocks" [lindex $cmd_args 0]] + } + foreach clk $clks { + if { [$clk is_generated] } { + remove_clock_cmd $clk + } + } +} + +################################################################ + +proc unset_disable_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args keys {-from -to} flags {} + check_argc_eq1 $cmd $cmd_args + + set from "" + if { [info exists keys(-from)] } { + set from $keys(-from) + } + set to "" + if { [info exists keys(-to)] } { + set to $keys(-to) + } + parse_libcell_libport_inst_port_pin_edge_timing_arc_set_arg $cmd_args \ + libcells libports insts ports pins edges timing_arc_sets + + if { ([info exists keys(-from)] || [info exists keys(-to)]) \ + && ($libports != {} || $pins != {} || $ports != {}) } { + sta_warn "-from/-to keywords ignored for lib_pin, port and pin arguments." + } + + foreach libcell $libcells { + unset_disable_timing_cell $libcell $from $to + } + foreach libport $libports { + unset_disable_lib_port $libport + } + foreach inst $insts { + unset_disable_timing_instance $inst $from $to + } + foreach pin $pins { + unset_disable_pin $pin + } + foreach port $ports { + unset_disable_port $port + } + foreach edge $edges { + unset_disable_edge $edge + } + foreach timing_arc_set $timing_arc_sets { + unset_disable_timing_arc_set $timing_arc_set + } +} + +proc unset_disable_timing_cell { cell from to } { + set from_ports [parse_disable_cell_ports $cell $from] + set to_ports [parse_disable_cell_ports $cell $to] + if { $from_ports == "NULL" && $to_ports == "NULL" } { + unset_disable_cell $cell "NULL" "NULL" + } elseif { $from_ports == "NULL" } { + foreach to_port $to_ports { + unset_disable_cell $cell "NULL" $to_port + } + } elseif { $to_ports == "NULL" } { + foreach from_port $from_ports { + unset_disable_cell $cell $from_port "NULL" + } + } else { + foreach from_port $from_ports { + foreach to_port $to_ports { + unset_disable_cell $cell $from_port $to_port + } + } + } +} + +proc unset_disable_timing_instance { inst from to } { + set from_ports [parse_disable_inst_ports $inst $from] + set to_ports [parse_disable_inst_ports $inst $to] + if { ![$inst is_leaf] } { + sta_error "-from/-to hierarchical instance not supported." + } + if { $from_ports == "NULL" && $to_ports == "NULL" } { + unset_disable_instance $inst "NULL" "NULL" + } elseif { $from_ports == "NULL" } { + foreach to_port $to_ports { + unset_disable_instance $inst "NULL" $to_port + } + } elseif { $to_ports == "NULL" } { + foreach from_port $from_ports { + unset_disable_instance $inst $from_port "NULL" + } + } else { + foreach from_port $from_ports { + foreach to_port $to_ports { + unset_disable_instance $inst $from_port $to_port + } + } + } +} + +################################################################ + +proc unset_path_exceptions_cmd { cmd cmd_args } { + parse_key_args $cmd cmd_args \ + keys {-from -rise_from -fall_from -to -rise_to -fall_to} \ + flags {-setup -hold -rise -fall} 0 + + set min_max "min_max" + if { [info exists flags(-setup)] && ![info exists flags(-hold)] } { + set min_max "max" + } + if { [info exists flags(-hold)] && ![info exists flags(-setup)] } { + set min_max "min" + } + + set arg_error 0 + set from [parse_from_arg keys arg_error] + set thrus [parse_thrus_arg cmd_args arg_error] + set to [parse_to_arg keys flags arg_error] + if { $arg_error } { + delete_from_thrus_to $from $thrus $to + sta_error "$cmd command failed." + return 0 + } + + check_for_key_args $cmd cmd_args + if { $cmd_args != {} } { + delete_from_thrus_to $from $thrus $to + sta_error "positional arguments not supported." + } + if { ($from == "NULL" && $thrus == "" && $to == "NULL") } { + delete_from_thrus_to $from $thrus $to + sta_error "-from, -through or -to required." + } + + reset_path_cmd $from $thrus $to $min_max + delete_from_thrus_to $from $thrus $to +} + +################################################################ + +proc unset_port_delay { cmd swig_cmd cmd_args } { + parse_key_args $cmd cmd_args \ + keys {-clock -reference_pin} \ + flags {-rise -fall -max -min -clock_fall } + check_argc_eq2 $cmd $cmd_args + + set delay_arg [lindex $cmd_args 0] + set delay [time_ui_sta $delay_arg] + set pins [get_port_pins_error "pins" [lindex $cmd_args 1]] + + set clk "NULL" + if [info exists keys(-clock)] { + set clk [get_clock_warn "clock" $keys(-clock)] + } + + if [info exists flags(-clock_fall)] { + set clk_tr "fall" + } else { + set clk_tr "rise" + } + + set tr [parse_rise_fall_flags flags] + set min_max [parse_min_max_all_flags flags] + + foreach pin $pins { + $swig_cmd $pin $tr $clk $clk_tr $min_max + } +} + +################################################################# +# +# Argument parsing functions. +# +################################################################ + +# Parse multiple object type args. +# For object name args the search order is as follows: +# clk +# liberty_cell +# liberty_port +# cell +# inst +# port +# pin +# 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 } { + if { $clks_var != {} } { + upvar 1 $clks_var clks + } + if { $libcells_var != {} } { + upvar 1 $libcells_var libcells + } + if { $libports_var != {} } { + upvar 1 $libports_var libports + } + if { $cells_var != {} } { + upvar 1 $cells_var cells + } + if { $insts_var != {} } { + upvar 1 $insts_var insts + } + if { $ports_var != {} } { + upvar 1 $ports_var ports + } + if { $pins_var != {} } { + upvar 1 $pins_var pins + } + if { $nets_var != {} } { + upvar 1 $nets_var nets + } + if { $edges_var != {} } { + upvar 1 $edges_var edges + } + if { $timing_arc_sets_var != {} } { + upvar 1 $timing_arc_sets_var timing_arc_sets + } + + # Copy backslashes that will be removed by foreach. + set objects [string map {\\ \\\\} $objects] + foreach obj $objects { + if { [llength $obj] > 1 } { + # List arg. Recursive call without initing objects. + get_object_args $obj clks libcells libports cells insts \ + ports pins nets edges timing_arc_sets + } elseif { [is_object $obj] } { + # Explicit object arg. + set object_type [object_type $obj] + if { $pins_var != {} && $object_type == "Pin" } { + lappend pins $obj + } elseif { $insts_var != {} && $object_type == "Instance" } { + lappend insts $obj + } elseif { $nets_var != {} && $object_type == "Net" } { + lappend nets $obj + } elseif { $ports_var != {} && $object_type == "Port" } { + lappend ports $obj + } elseif { $edges_var != {} && $object_type == "Edge" } { + lappend edges $obj + } elseif { $clks_var != {} && $object_type == "Clock" } { + lappend clks $obj + } elseif { $libcells_var != {} && $object_type == "LibertyCell" } { + lappend libcells $obj + } elseif { $libports_var != {} && $object_type == "LibertyPort" } { + lappend libports $obj + } elseif { $cells_var != {} && $object_type == "Cell" } { + lappend cells $obj + } elseif { $timing_arc_sets_var != {} \ + && $object_type == "TimingArcSet" } { + lappend timing_arc_sets $obj + } else { + sta_error "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] + } + if { $matches != {} } { + set clks [concat $clks $matches] + } else { + + if { $libcells_var != {} } { + set matches [get_lib_cells -quiet $obj] + } + if { $matches != {} } { + set libcells [concat $libcells $matches] + } else { + + if { $libports_var != {} } { + set matches [get_lib_pins -quiet $obj] + } + if { $matches != {} } { + set libports [concat $libports $matches] + } else { + + if { $cells_var != {} } { + set matches [find_cells_matching $obj 0 0] + } + if { $matches != {} } { + set cells [concat $cells $matches] + } else { + + if { $insts_var != {} } { + set matches [get_cells -quiet $obj] + } + if { $matches != {} } { + set insts [concat $insts $matches] + } else { + if { $ports_var != {} } { + set matches [get_ports -quiet $obj] + } + if { $matches != {} } { + set ports [concat $ports $matches] + } else { + if { $pins_var != {} } { + set matches [get_pins -quiet $obj] + } + if { $matches != {} } { + set pins [concat $pins $matches] + } else { + if { $nets_var != {} } { + set matches [get_nets -quiet $obj] + } + if { $matches != {} } { + set nets [concat $nets $matches] + } else { + sta_warn "object '$obj' not found." + } + } + } + } + } + } + } + } + } + } +} + +proc parse_clk_cell_port_args { objects clks_var cells_var ports_var } { + upvar 1 $clks_var clks + upvar 1 $cells_var cells + upvar 1 $ports_var ports + set clks {} + set cells {} + set ports {} + get_object_args $objects clks {} {} cells {} ports {} {} {} {} +} + +proc parse_clk_cell_port_pin_args { objects clks_var cells_var ports_var \ + pins_arg } { + upvar 1 $clks_var clks + upvar 1 $cells_var cells + upvar 1 $ports_var ports + upvar 1 $pins_arg pins + set clks {} + set cells {} + set ports {} + set pins {} + get_object_args $objects clks {} {} cells {} ports pins {} {} {} +} + +proc parse_clk_inst_pin_arg { objects clks_var insts_var pins_var } { + upvar 1 $clks_var clks + upvar 1 $insts_var insts + upvar 1 $pins_var pins + set clks {} + set insts {} + set pins {} + get_object_args $objects clks {} {} {} insts {} pins {} {} {} +} + +proc parse_clk_inst_port_pin_arg { objects clks_var insts_var pins_var } { + upvar 1 $clks_var clks + upvar 1 $insts_var insts + upvar 1 $pins_var pins + set clks {} + set insts {} + set pins {} + set ports {} + get_object_args $objects clks {} {} {} insts ports pins {} {} {} + foreach port $ports { + lappend pins [[top_instance] find_pin [get_name $port]] + } +} + +proc parse_clk_port_pin_arg { objects clks_var pins_var } { + upvar 1 $clks_var clks + upvar 1 $pins_var pins + set clks {} + set pins {} + set ports {} + get_object_args $objects clks {} {} {} {} ports pins {} {} {} + foreach port $ports { + lappend pins [[top_instance] find_pin [get_name $port]] + } +} + +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 } { + upvar 1 $libcells_var libcells + upvar 1 $libports_var libports + upvar 1 $insts_var insts + upvar 1 $ports_var ports + upvar 1 $pins_var pins + upvar 1 $edges_var edges + upvar 1 $timing_arc_sets_var timing_arc_sets + + set libcells {} + set libports {} + set insts {} + set ports {} + set pins {} + set edges {} + set timing_arc_sets {} + get_object_args $objects {} libcells libports {} insts ports pins {} \ + edges timing_arc_sets +} + +proc parse_libcell_inst_arg { objects libcells_var insts_var } { + upvar 1 $libcells_var libcells + upvar 1 $insts_var insts + set libcells {} + set insts {} + get_object_args $objects {} libcells {} {} insts {} {} {} {} {} +} + +proc parse_libcell_inst_net_arg { objects libcells_var insts_var nets_var } { + upvar 1 $libcells_var libcells + upvar 1 $insts_var insts + upvar 1 $nets_var nets + set libcells {} + set insts {} + set nets {} + get_object_args $objects {} libcells {} {} insts {} {} nets {} {} +} + +proc parse_cell_port_args { objects cells_var ports_var } { + upvar 1 $cells_var cells + upvar 1 $ports_var ports + set cells {} + set ports {} + get_object_args $objects {} {} {} cells {} ports {} {} {} {} +} + +proc parse_cell_port_pin_args { objects cells_var ports_var pins_var } { + upvar 1 $cells_var cells + upvar 1 $ports_var ports + upvar 1 $pins_var pins + set cells {} + set ports {} + set pins {} + get_object_args $objects {} {} {} cells {} ports pins {} {} {} +} + +proc parse_inst_port_pin_arg { objects insts_var pins_var } { + upvar 1 $insts_var insts + upvar 1 $pins_var pins + set insts {} + set pins {} + set ports {} + get_object_args $objects {} {} {} {} insts ports pins {} {} {} + foreach port $ports { + lappend pins [[top_instance] find_pin [get_name $port]] + } +} + +proc parse_inst_pin_arg { objects insts_var pins_var } { + upvar 1 $insts_var insts + upvar 1 $pins_var pins + set insts {} + set pins {} + get_object_args $objects {} {} {} {} insts {} pins {} {} {} +} + +proc parse_inst_port_pin_net_arg { objects insts_var pins_var nets_var } { + upvar 1 $insts_var insts + upvar 1 $pins_var pins + upvar 1 $nets_var nets + set insts {} + set ports {} + set pins {} + set nets {} + get_object_args $objects {} {} {} {} insts ports pins nets {} {} + foreach port $ports { + lappend pins [[top_instance] find_pin [get_name $port]] + } +} + +proc parse_inst_net_arg { objects insts_var nets_var } { + upvar 1 $insts_var insts + upvar 1 $nets_var nets + set insts {} + set nets {} + get_object_args $objects {} {} {} {} insts {} {} nets {} {} +} + +proc parse_port_pin_net_arg { objects pins_var nets_var } { + upvar 1 $pins_var pins + upvar 1 $nets_var nets + set ports {} + set pins {} + set nets {} + get_object_args $objects {} {} {} {} {} ports pins nets {} {} + + foreach port $ports { + lappend pins [[top_instance] find_pin [get_name $port]] + } +} + +proc parse_port_net_args { objects ports_var nets_var } { + upvar 1 $ports_var ports + upvar 1 $nets_var nets + set ports {} + set nets {} + get_object_args $objects {} {} {} {} {} ports {} nets {} {} +} + +proc parse_pin_net_args { objects pins_var nets_var } { + upvar 1 $pins_var pins + upvar 1 $nets_var nets + set pins {} + set nets {} + get_object_args $objects {} {} {} {} {} {} pins nets {} {} +} + +proc get_ports_or_pins { pattern } { + set matches [find_port_pins_matching $pattern 0 0] + if { $matches != {} } { + return $matches + } else { + return [find_pins_matching $pattern 0 0] + } +} + +################################################################ + +# If -corner keyword is missing: +# one corner: return default +# multiple corners: error +proc parse_corner { keys_var } { + upvar 1 $keys_var keys + + if { [info exists keys(-corner)] } { + set corner_name $keys(-corner) + set corner [find_corner $corner_name] + if { $corner == "NULL" } { + sta_error "$corner_name is not the name of process corner." + } else { + return $corner + } + } elseif { [multi_corner] } { + sta_error "-corner keyword required with multi-corner analysis." + } else { + return [cmd_corner] + } +} + +# -corner keyword is required. +# Assumes caller checks for existence of -corner keyword arg. +proc parse_corner_required { keys_var } { + upvar 1 $keys_var keys + + if { [info exists keys(-corner)] } { + set corner_name $keys(-corner) + set corner [find_corner $corner_name] + if { $corner == "NULL" } { + sta_error "$corner_name is not the name of process corner." + } else { + return $corner + } + } else { + sta_error "missing -corner arg." + } +} + +proc parse_corner_or_default { keys_var } { + upvar 1 $keys_var keys + + if { [info exists keys(-corner)] } { + set corner_name $keys(-corner) + set corner [find_corner $corner_name] + if { $corner == "NULL" } { + sta_error "$corner_name is not the name of process corner." + } else { + return $corner + } + } else { + return [cmd_corner] + } +} + +proc parse_corner_or_all { keys_var } { + upvar 1 $keys_var keys + + if { [info exists keys(-corner)] } { + set corner_name $keys(-corner) + set corner [find_corner $corner_name] + if { $corner == "NULL" } { + sta_error "$corner_name is not the name of process corner." + } else { + return $corner + } + } else { + return "NULL" + } +} + +################################################################ + +proc parse_rise_fall_flags { flags_var } { + upvar 1 $flags_var flags + if { [info exists flags(-rise)] && ![info exists flags(-fall)] } { + return "rise" + } elseif { [info exists flags(-fall)] && ![info exists flags(-rise)] } { + return "fall" + } else { + return "rise_fall" + } +} + +proc parse_min_max_flags { flags_var } { + upvar 1 $flags_var flags + if { [info exists flags(-min)] && [info exists flags(-max)] } { + sta_error "both -min and -max specified." + } elseif { [info exists flags(-min)] && ![info exists flags(-max)] } { + return "min" + } elseif { [info exists flags(-max)] && ![info exists flags(-min)] } { + return "max" + } else { + # Default. + return "max" + } +} + +proc parse_min_max_all_flags { flags_var } { + upvar 1 $flags_var flags + if { [info exists flags(-min)] && [info exists flags(-max)] } { + sta_error "both -min and -max specified." + } elseif { [info exists flags(-min)] && ![info exists flags(-max)] } { + return "min" + } elseif { [info exists flags(-max)] && ![info exists flags(-min)] } { + return "max" + } else { + return "all" + } +} + +# parse_min_max_all_flags and require analysis type to be min/max. +proc parse_min_max_all_check_flags { flags_var } { + upvar 1 $flags_var flags + if { [info exists flags(-min)] && [info exists flags(-max)] } { + return "all" + } elseif { [info exists flags(-min)] && ![info exists flags(-max)] } { + return "min" + } elseif { [info exists flags(-max)] && ![info exists flags(-min)] } { + return "max" + } else { + return "all" + } +} + +proc parse_early_late_flags { flags_var } { + upvar 1 $flags_var flags + if { [info exists flags(-early)] && [info exists flags(-late)] } { + sta_error "only one of -early and -late can be specified." + } elseif { [info exists flags(-early)] } { + return "min" + } elseif { [info exists flags(-late)] } { + return "max" + } else { + sta_error "-early or -late must be specified." + } +} + +proc parse_early_late_all_flags { flags_var } { + upvar 1 $flags_var flags + if { [info exists flags(-early)] && [info exists flags(-late)] } { + sta_error "both -early and -late specified." + } elseif { [info exists flags(-early)] && ![info exists flags(-late)] } { + return "min" + } elseif { [info exists flags(-late)] && ![info exists flags(-early)] } { + return "max" + } else { + return "all" + } +} + +################################################################ + +proc get_liberty_error { arg_name arg } { + set lib "NULL" + if {[llength $arg] > 1} { + sta_error "$arg_name must be a single library." + } elseif { [is_object $arg] } { + set object_type [object_type $arg] + if { $object_type == "LibertyLibrary" } { + set lib $arg + } else { + sta_error "$arg_name type '$object_type' is not a library." + } + } else { + set lib [find_liberty $arg] + if { $lib == "NULL" } { + sta_error "library '$arg' not found." + } + } + return $lib +} + +proc get_lib_cell_warn { arg_name arg } { + return [get_lib_cell_arg $arg_name $arg sta_warn] +} + +proc get_lib_cell_error { arg_name arg } { + return [get_lib_cell_arg $arg_name $arg sta_error] +} + +proc get_lib_cell_arg { arg_name arg error_proc } { + set lib_cell "NULL" + if { [llength $arg] > 1 } { + sta_error "$arg_name must be a single lib cell." + } elseif { [is_object $arg] } { + set object_type [object_type $arg] + if { $object_type == "LibertyCell" } { + set lib_cell $arg + } else { + $error_proc "$arg_name type '$object_type' is not a liberty cell." + } + # Parse library_name/cell_name. + } elseif {[regexp [cell_regexp] $arg ignore lib_name cell_name]} { + set library [find_liberty $lib_name] + if { $library != "NULL" } { + set lib_cell [$library find_liberty_cell $cell_name] + if { $lib_cell == "NULL" } { + $error_proc "liberty cell '$arg' not found." + } + } else { + $error_proc "library '$lib_name' not found." + } + } else { + set lib_cell [find_liberty_cell $arg] + if { $lib_cell == "NULL" } { + $error_proc "liberty cell '$arg' not found." + } + } + return $lib_cell +} + +proc get_instance_error { arg_name arg } { + set inst "NULL" + if {[llength $arg] > 1} { + sta_error "$arg_name must be a single instance." + } elseif { [is_object $arg] } { + set object_type [object_type $arg] + if { $object_type == "Instance" } { + set inst $arg + } else { + sta_error "$arg_name type '$object_type' is not an instance." + } + } else { + set inst [find_instance $arg] + if { $inst == "NULL" } { + sta_error "instance '$arg' not found." + } + } + return $inst +} + +proc get_instances_error { arg_name arglist } { + set insts {} + # Copy backslashes that will be removed by foreach. + set arglist [string map {\\ \\\\} $arglist] + foreach arg $arglist { + if {[llength $arg] > 1} { + # Embedded list. + set insts [concat $insts [get_instances_error $arg_name $arg]] + } elseif { [is_object $arg] } { + set object_type [object_type $arg] + if { $object_type == "Instance" } { + lappend insts $arg + } else { + sta_error "$arg_name type '$object_type' is not an instance." + } + } elseif { $arg != {} } { + set arg_insts [get_cells -quiet $arg] + if { $arg_insts != {} } { + set insts [concat $insts $arg_insts] + } else { + sta_error "instance '$arg' not found." + } + } + } + return $insts +} + +proc get_port_pin_warn { arg_name arg } { + return [get_port_pin_arg $arg_name $arg "warn"] +} + +proc get_port_pin_error { arg_name arg } { + return [get_port_pin_arg $arg_name $arg "error"] +} + +proc get_port_pin_arg { arg_name arg warn_error } { + set pin "NULL" + if {[llength $arg] > 1} { + sta_warn_error $warn_error "$arg_name must be a single port or pin." + } elseif { [is_object $arg] } { + set object_type [object_type $arg] + if { $object_type == "Pin" } { + set pin $arg + } elseif { $object_type == "Port" } { + # Explicit port arg - convert to pin. + set pin [find_pin [get_name $arg]] + } else { + sta_warn_error $warn_error "$arg_name type '$object_type' is not a pin or port." + } + } else { + set top_instance [top_instance] + set top_cell [$top_instance cell] + set port [$top_cell find_port $arg] + if { $port == "NULL" } { + set pin [find_pin $arg] + } else { + set pin [$top_instance find_pin [get_name $port]] + } + if { $pin == "NULL" } { + sta_warn_error $warn_error "pin $arg not found." + } + } + return $pin +} + +proc get_port_pins_error { arg_name arglist } { + set pins {} + # Copy backslashes that will be removed by foreach. + set arglilst [string map {\\ \\\\} $arglist] + foreach arg $arglist { + if {[llength $arg] > 1} { + # Embedded list. + set pins [concat $pins [get_port_pins_error $arg_name $arg]] + } elseif { [is_object $arg] } { + set object_type [object_type $arg] + if { $object_type == "Pin" } { + lappend pins $arg + } elseif { $object_type == "Port" } { + # Convert port to pin. + lappend pins [find_pin [get_name $arg]] + } else { + sta_error "$arg_name type '$object_type' is not a pin or port." + } + } elseif { $arg != {} } { + set arg_pins [get_ports_or_pins $arg] + if { $arg_pins != {} } { + set pins [concat $pins $arg_pins] + } else { + sta_error "pin '$arg' not found." + } + } + } + return $pins +} + +proc get_ports_error { arg_name arglist } { + set ports {} + # Copy backslashes that will be removed by foreach. + set arglist [string map {\\ \\\\} $arglist] + foreach arg $arglist { + if {[llength $arg] > 1} { + # Embedded list. + set ports [concat $ports [get_ports_error $arg_name $arg]] + } elseif { [is_object $arg] } { + set object_type [object_type $arg] + if { $object_type == "Port" } { + lappend ports $arg + } else { + sta_error "$arg_name type '$object_type' is not a port." + } + } elseif { $arg != {} } { + set arg_ports [get_ports $arg] + if { $arg_ports != {} } { + set ports [concat $ports $arg_ports] + } + } + } + return $ports +} + +proc get_pin_error { arg_name arg } { + return [get_pin_arg $arg_name $arg "error"] +} + +proc get_pin_warn { arg_name arg } { + return [get_pin_arg $arg_name $arg "warn"] +} + +proc get_pin_arg { arg_name arg warn_error } { + set pin "NULL" + if {[llength $arg] > 1} { + sta_warn_error $warn_error "$arg_name must be a single pin." + } elseif { [is_object $arg] } { + set object_type [object_type $arg] + if { $object_type == "Pin" } { + set pin $arg + } else { + sta_warn_error $warn_error "$arg_name type '$object_type' is not a pin." + } + } else { + set pin [find_pin $arg] + if { $pin == "NULL" } { + sta_warn_error $warn_error "$arg_name pin $arg not found." + } + } + return $pin +} + +proc get_clock_warn { arg_name arg } { + return [get_clock_arg $arg_name $arg sta_warn] +} + +proc get_clock_error { arg_name arg } { + return [get_clock_arg $arg_name $arg sta_error] +} + +proc get_clock_arg { arg_name arg error_proc } { + set clk "NULL" + if {[llength $arg] > 1} { + $error_proc "$arg_name arg must be a single clock, not a list." + } elseif { [is_object $arg] } { + set object_type [object_type $arg] + if { $object_type == "Clock" } { + set clk $arg + } else { + $error_proc "$arg_name arg value is a $object_type, not a clock." + } + } elseif { $arg != {} } { + set clk [find_clock $arg] + if { $clk == "NULL" } { + $error_proc "$arg_name arg '$arg' clock not found." + } + } + return $clk +} + +proc get_clocks_warn { arg_name arglist } { + set clks {} + # Copy backslashes that will be removed by foreach. + set arglist [string map {\\ \\\\} $arglist] + foreach arg $arglist { + if {[llength $arg] > 1} { + # Embedded list. + set clks [concat $clks [get_clocks_warn $arg_name $arg]] + } elseif { [is_object $arg] } { + set object_type [object_type $arg] + if { $object_type == "Clock" } { + lappend clks $arg + } else { + sta_warn "unsupported object type $object_type." + } + } elseif { $arg != {} } { + set arg_clocks [get_clocks $arg] + if { $arg_clocks != {} } { + set clks [concat $clks $arg_clocks] + } + } + } + return $clks +} + +proc get_net_warn { arg_name arg } { + set net "NULL" + if {[llength $arg] > 1} { + sta_warn "$arg_name must be a single net." + } elseif { [is_object $arg] } { + set object_type [object_type $arg] + if { $object_type == "Net" } { + set net $arg + } else { + sta_warn "$arg_name '$object_type' is not a net." + } + } else { + set net [find_net $arg] + if { $net == "NULL" } { + sta_warn "$arg_name '$arg' not found." + } + } + return $net +} + +proc get_nets_error { arg_name arglist } { + return [get_nets_arg $arg_name $arglist "error"] +} + +proc get_nets_warn { arg_name arglist } { + return [get_nets_arg $arg_name $arglist "warn"] +} + +proc get_nets_arg { arg_name arglist warn_error } { + set nets {} + # Copy backslashes that will be removed by foreach. + set arglist [string map {\\ \\\\} $arglist] + foreach arg $arglist { + if {[llength $arg] > 1} { + # Embedded list. + set nets [concat $nets [get_nets_arg $arg_name $arg $warn_error]] + } elseif { [is_object $arg] } { + set object_type [object_type $arg] + if { $object_type == "Net" } { + lappend nets $arg + } else { + sta_warn_error $warn_error "unsupported object type $object_type." + } + } elseif { $arg != {} } { + set arg_nets [get_nets -quiet $arg] + if { $arg_nets != {} } { + set nets [concat $nets $arg_nets] + } + } + } + return $nets +} + +################################################################ + +proc cell_regexp {} { + global hierarchy_separator + if { $hierarchy_separator == "." } { + set lib_regexp {[a-zA-Z0-9_]+} + } else { + set lib_regexp {[a-zA-Z0-9_\.]+} + } + set cell_regexp {[a-zA-Z0-9_]+} + return "^(${lib_regexp})${hierarchy_separator}(${cell_regexp})$" +} + +proc cell_wild_regexp { divider } { + if { $divider == "." } { + set lib_regexp {[a-zA-Z0-9_*+?^$\{\}]+} + } else { + set lib_regexp {[a-zA-Z0-9_.*+?^$\{\}]+} + } + set cell_wild_regexp {[a-zA-Z0-9_.*+?^$\{\}]+} + return "^(${lib_regexp})${divider}(${cell_wild_regexp})$" +} + +proc port_regexp {} { + global hierarchy_separator + if { $hierarchy_separator == "." } { + set lib_regexp {[a-zA-Z0-9_]+} + } else { + set lib_regexp {[a-zA-Z0-9_\.]+} + } + set id_regexp {[a-zA-Z0-9_]+(?:\[[0-9]+\])?} + return "^(${lib_regexp})${hierarchy_separator}(${id_regexp})${hierarchy_separator}(${id_regexp})$" +} + +proc port_wild_regexp { divider } { + if { $divider == "." } { + set lib_regexp {[a-zA-Z0-9_]+} + } else { + set lib_regexp {[a-zA-Z0-9_\.]+} + } + set cell_regexp {[a-zA-Z0-9_]+} + set wild_regexp {[a-zA-Z0-9_.*+?^$\{\}]+} + return "^(${lib_regexp})${divider}(${wild_regexp})${divider}(${wild_regexp})$" +} + +proc path_regexp {} { + global hierarchy_separator + set id_regexp {[a-zA-Z0-9_]+(?:\[[0-9]+\])?} + set prefix_regexp "${id_regexp}(?:${hierarchy_separator}${id_regexp})*" + return "^(${prefix_regexp})${hierarchy_separator}(${id_regexp})$" +} + +proc get_property_cmd { cmd type_key cmd_args } { + parse_key_args $cmd cmd_args keys $type_key flags {-quiet} + set quiet [info exists flags(-quiet)] + check_argc_eq2 $cmd $cmd_args + set object [lindex $cmd_args 0] + if { $object == "" } { + sta_error "$cmd object is null." + } elseif { ![is_object $object] } { + if [info exists keys($type_key)] { + set object_type $keys($type_key) + } else { + sta_error "$cmd $type_key must be specified with object name argument." + } + set object [get_property_object_type $object_type $object $quiet] + } + set prop [lindex $cmd_args 1] + return [get_object_property $object $prop] +} + +proc get_object_property { object prop } { + if { [is_object $object] } { + set object_type [object_type $object] + if { $object_type == "Instance" } { + return [instance_property $object $prop] + } elseif { $object_type == "Pin" } { + return [pin_property $object $prop] + } elseif { $object_type == "Net" } { + return [net_property $object $prop] + } elseif { $object_type == "Clock" } { + return [clock_property $object $prop] + } elseif { $object_type == "Port" } { + return [port_property $object $prop] + } elseif { $object_type == "LibertyPort" } { + return [liberty_port_property $object $prop] + } elseif { $object_type == "LibertyCell" } { + return [liberty_cell_property $object $prop] + } elseif { $object_type == "Cell" } { + return [cell_property $object $prop] + } elseif { $object_type == "Library" } { + return [library_property $object $prop] + } elseif { $object_type == "LibertyLibrary" } { + return [liberty_library_property $object $prop] + } elseif { $object_type == "Edge" } { + return [edge_property $object $prop] + } elseif { $object_type == "PathEnd" } { + return [path_end_property $object $prop] + } elseif { $object_type == "PathRef" } { + return [path_ref_property $object $prop] + } elseif { $object_type == "TimingArcSet" } { + return [timing_arc_set_property $object $prop] + } else { + sta_error "get_property unsupported object type $object_type." + } + } else { + sta_error "get_property $object is not an object." + } +} + +proc get_property_object_type { object_type object_name quiet } { + set object "NULL" + if { $object_type == "cell" } { + set object [get_cells -quiet $object_name] + } elseif { $object_type == "pin" } { + set object [get_pins -quiet $object_name] + } elseif { $object_type == "net" } { + set object [get_nets -quiet $object_name] + } elseif { $object_type == "port" } { + set object [get_ports -quiet $object_name] + } elseif { $object_type == "clock" } { + set object [get_clocks -quiet $object_name] + } elseif { $object_type == "lib_cell" } { + set object [get_lib_cells -quiet $object_name] + } elseif { $object_type == "lib_pin" } { + set object [get_lib_pins -quiet $object_name] + } elseif { $object_type == "lib" } { + set object [get_libs -quiet $object_name] + } else { + sta_error "$object_type not supported." + } + if { $object == "NULL" && !$quiet } { + sta_error "$object_type '$object_name' not found." + } + return [lindex $object 0] +} + +proc get_object_type { obj } { + set object_type [object_type $obj] + if { $object_type == "Clock" } { + return "clock" + } elseif { $object_type == "LibertyCell" } { + return "libcell" + } elseif { $object_type == "LibertyPort" } { + return "libpin" + } elseif { $object_type == "Cell" } { + return "design" + } elseif { $object_type == "Instance" } { + return "cell" + } elseif { $object_type == "Port" } { + return "port" + } elseif { $object_type == "Pin" } { + return "pin" + } elseif { $object_type == "Net" } { + return "net" + } elseif { $object_type == "Edge" } { + return "timing_arc" + } elseif { $object_type == "TimingArcSet" } { + return "timing_arc" + } else { + return "?" + } +} + +proc get_name { object } { + return [get_object_property $object "name"] +} + +proc get_full_name { object } { + return [get_object_property $object "full_name"] +} + +proc sort_by_name { objects } { + return [lsort -command name_cmp $objects] +} + +proc name_cmp { obj1 obj2 } { + return [string compare [get_name $obj1] [get_name $obj2]] +} + +proc sort_by_full_name { objects } { + return [lsort -command full_name_cmp $objects] +} + +proc full_name_cmp { obj1 obj2 } { + return [string compare [get_full_name $obj1] [get_full_name $obj2]] +} + +################################################################ + +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} + +proc write_path_spice { args } { + parse_key_args "write_path_spice" args \ + keys {-spice_directory -lib_subckt_file -model_file \ + -power -ground -path_args} \ + flags {} + + if { [info exists keys(-spice_directory)] } { + set spice_dir [file_expand_tilde $keys(-spice_directory)] + if { ![file exists $spice_dir] } { + sta_error "Directory $spice_dir not found.\n" + } + if { ![file isdirectory $spice_dir] } { + sta_error "$spice_dir is not a directory.\n" + } + if { ![file writable $spice_dir] } { + sta_error "Cannot write in $spice_dir.\n" + } + } else { + sta_error "No -spice_directory specified.\n" + } + + if { [info exists keys(-lib_subckt_file)] } { + set lib_subckt_file [file_expand_tilde $keys(-lib_subckt_file)] + if { ![file readable $lib_subckt_file] } { + sta_error "-lib_subckt_file $lib_subckt_file is not readable.\n" + } + } else { + sta_error "No -lib_subckt_file specified.\n" + } + + if { [info exists keys(-model_file)] } { + set model_file [file_expand_tilde $keys(-model_file)] + if { ![file readable $model_file] } { + sta_error "-model_file $model_file is not readable.\n" + } + } else { + sta_error "No -model_file specified.\n" + } + + if { [info exists keys(-power)] } { + set power $keys(-power) + } else { + sta_error "No -power specified.\n" + } + + if { [info exists keys(-ground)] } { + set ground $keys(-ground) + } else { + sta_error "No -ground specified.\n" + } + + if { ![info exists keys(-path_args)] } { + sta_error "No -path_args specified.\n" + } + set path_args $keys(-path_args) + set path_ends [eval [concat find_timing_paths $path_args]] + if { $path_ends == {} } { + sta_error "No paths found for -path_args $path_args.\n" + } else { + 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 + incr path_index + } + } +} + +proc file_expand_tilde { filename } { + global env + + if { [string range $filename 0 1] == "~/" } { + return [file join $env(HOME) [string range $filename 2 end]] + } else { + return $filename + } +} + +# sta namespace end. +} diff --git a/tcl/Graph.tcl b/tcl/Graph.tcl new file mode 100644 index 0000000..8aa1558 --- /dev/null +++ b/tcl/Graph.tcl @@ -0,0 +1,402 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +# Graph utilities + +namespace eval sta { + +define_cmd_args "report_edges" {[-from from_pin] [-to to_pin]} + +proc report_edges { args } { + parse_key_args "report_edges" args keys {-from -to} flags {} + check_argc_eq0 "report_edges" $args + + 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 + } + } + } 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 + } + } 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 + } + } +} + +proc report_edges_between_ { from_vertex to_vertex } { + 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 + } else { + report_edge_ $edge vertex_port_name vertex_port_name + } + } + } + $iter finish +} + +proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } { + # First report edges internal to the device. + set device_header 0 + set iter [$vertex $iter_proc] + while {[$iter has_next]} { + 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 + } + report_edge_ $edge vertex_port_name vertex_port_name + } + } + $iter finish + + # Then wires. + set iter [$vertex $iter_proc] + while {[$iter has_next]} { + set edge [$iter next] + if { [$edge role] == "wire" } { + report_edge_ $edge $wire_from_name_proc $wire_to_name_proc + } + } + $iter finish +} + +proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } { + global sta_report_default_digits + + puts -nonewline "[$vertex_from_name_proc [$edge from]] -> [$vertex_to_name_proc [$edge to]] [$edge role]" + set latch_enable [$edge latch_d_to_q_en] + if { $latch_enable != "" } { + puts " enable $latch_enable" + } else { + puts "" + } + + set disables [edge_disable_reason $edge] + if { $disables != "" } { + puts " Disabled by $disables" + } + + set cond [$edge cond] + if { $cond != "" } { + puts " Condition: $cond" + } + + set mode_name [$edge mode_name] + if { $mode_name != "" } { + puts " Mode: $mode_name [$edge mode_value]" + } + + set iter [$edge timing_arc_iterator] + while {[$iter has_next]} { + set arc [$iter next] + set delays [$edge arc_delay_strings $arc $sta_report_default_digits] + set delays_fmt [format_delays $delays] + set disable_reason "" + if { [timing_arc_disabled $edge $arc] } { + set disable_reason " disabled" + } + puts " [$arc from_trans] -> [$arc to_trans] $delays_fmt$disable_reason" + } + $iter finish +} + +# 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 "" + foreach value $values { + if { $result != "" } { + append result ":" + } + append result $value + } + return $result +} + +proc edge_disable_reason { edge } { + set disables "" + if [$edge is_disabled_constraint] { + append disables "constraint" + } + if [$edge is_disabled_constant] { + if { $disables != "" } { append disables ", " } + append disables "constant" + } + if [$edge is_disabled_cond_default] { + if { $disables != "" } { append disables ", " } + append disables "cond_default" + } + if [$edge is_disabled_loop] { + if { $disables != "" } { append disables ", " } + append disables "loop" + } + if [$edge is_disabled_bidirect_inst_path] { + 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 "timing_enable_preset_clear_arcs" + } + return $disables +} + +################################################################ + +define_cmd_args "report_constant" {pin|instance|net} + +proc report_constant { obj } { + parse_inst_port_pin_net_arg $obj insts pins nets + foreach pin $pins { + report_pin_constant $pin + } + foreach inst $insts { + set pin_iter [$inst pin_iterator] + while {[$pin_iter has_next]} { + set pin [$pin_iter next] + report_pin_constant $pin + } + $pin_iter finish + } + foreach net $nets { + set pin_iter [$net pin_iterator] + while {[$pin_iter has_next]} { + set pin [$pin_iter next] + report_pin_constant $pin + } + $pin_iter finish + } +} + +proc report_pin_constant { pin } { + set sim_value [pin_sim_logic_value $pin] + puts -nonewline "[pin_property $pin lib_pin_name] $sim_value" + set case_value [pin_case_logic_value $pin] + if { $case_value != "X" } { + puts -nonewline " case=$case_value" + } + set logic_value [pin_logic_value $pin] + if { $logic_value != "X" } { + puts -nonewline " logic=$logic_value" + } + puts "" +} + +################################################################ + +proc report_disabled_edges {} { + foreach edge [disabled_edges_sorted] { + if { [$edge role] == "wire" } { + set from_pin_name [get_full_name [[$edge from] pin]] + set to_pin_name [get_full_name [[$edge to] pin]] + puts -nonewline "$from_pin_name $to_pin_name" + } else { + set from_pin [[$edge from] pin] + set to_pin [[$edge to] pin] + set inst_name [get_full_name [$from_pin instance]] + set from_port_name [get_name [$from_pin port]] + set to_port_name [get_name [$to_pin port]] + puts -nonewline "$inst_name $from_port_name $to_port_name" + set cond [$edge cond] + if { $cond != "" } { + puts -nonewline " when: $cond" + } + } + set reason [edge_disable_reason_verbose $edge] + puts " $reason" + } +} + +proc edge_disable_reason_verbose { edge } { + set disables "" + if { [$edge is_disabled_constraint] } { + append disables "constraint" + } + if { [$edge is_disabled_constant] } { + if { $disables != "" } { append disables ", " } + append disables "constant" + set sense [$edge sim_timing_sense] + if { $sense == "positive_unate" || $sense == "negative_unate" } { + append disables " $sense" + } + set const_pins [$edge disabled_constant_pins] + foreach pin [sort_by_full_name $const_pins] { + set port_name [pin_property $pin lib_pin_name] + set value [pin_sim_logic_value $pin] + append disables " $port_name=$value" + } + } + if { [$edge is_disabled_cond_default] } { + if { $disables != "" } { append disables ", " } + append disables "cond_default" + } + if { [$edge is_disabled_loop] } { + if { $disables != "" } { append disables ", " } + append disables "loop" + } + if { [$edge is_disabled_preset_clear] } { + if { $disables != "" } { append disables ", " } + append disables "timing_enable_preset_clear_arcs" + } + return $disables +} + +################################################################ + +define_cmd_args "report_slews" { pin } + +proc report_slews { pin } { + global sta_report_default_digits + + set pin1 [get_port_pin_error "pin" $pin] + foreach vertex [$pin1 vertices] { + puts "[vertex_path_name $vertex] [rise_short_name] [format_times [$vertex slews rise] $sta_report_default_digits] [fall_short_name] [format_times [$vertex slews fall] $sta_report_default_digits]" + } +} + +proc vertex_path_name { vertex } { + set pin [$vertex pin] + set pin_name [get_full_name $pin] + return [vertex_name_ $vertex $pin $pin_name] +} + +proc vertex_port_name { vertex } { + set pin [$vertex pin] + set pin_name [pin_property $pin lib_pin_name] + return [vertex_name_ $vertex $pin $pin_name] +} + +# Append driver/load for bidirect pin vertices. +proc vertex_name_ { vertex pin pin_name } { + if { [pin_direction $pin] == "bidirect" } { + if [$vertex is_bidirect_driver] { + return "$pin_name driver" + } else { + return "$pin_name load " + } + } else { + return $pin_name + } +} + +# Return a list of hierarchical pins crossed by a graph edge. +proc hier_pins_crossed_by_edge { edge } { + set from_pins [hier_pins_above [[$edge from] pin]] + set to_pins [hier_pins_above [[$edge to] pin]] + foreach p $to_pins { puts [$p path_name] } + while { [llength $from_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] + } + return [concat $from_pins $to_pins] +} + +proc hier_pins_above { pin } { + set pins_above {} + set inst [$pin instance] + set net [$pin net] + set parent [$inst parent] + while { $parent != "NULL" } { + set found 0 + set parent_pin_iter [$parent pin_iterator] + 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 + } + } + $parent_pin_iter finish + if { !$found} { + break + } + set parent [$parent parent] + } + return $pins_above +} + +proc report_level { pin } { + set pin1 [get_port_pin_error "pin" $pin] + foreach vertex [$pin1 vertices] { + if { $vertex != "NULL" } { + puts "[vertex_path_name $vertex] level = [$vertex level]" + } + } +} + +# Show how many pins are at each level from an input. +proc report_level_distribution {} { + set max_level 0 + set iter [vertex_iterator] + while {[$iter has_next]} { + set vertex [$iter next] + set level [$vertex level] + if { $level > $max_level } { + set max_level $level + } + if [info exists count($level)] { + incr count($level) + } else { + set count($level) 1 + } + } + $iter finish + + puts "level pin count" + for { set level 0 } { $level < $max_level } { incr level } { + puts " $level $count($level)" + } +} + +# sta namespace end +} diff --git a/tcl/Liberty.tcl b/tcl/Liberty.tcl new file mode 100644 index 0000000..18fa191 --- /dev/null +++ b/tcl/Liberty.tcl @@ -0,0 +1,37 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +# Liberty commands. + +namespace eval sta { + +define_cmd_args "read_liberty" \ + {[-corner corner_name] [-min] [-max] [-no_latch_infer] filename} + +proc_redirect read_liberty { + parse_key_args "read_liberty" args keys {-corner} \ + flags {-min -max -no_latch_infer} + check_argc_eq1 "read_liberty" $args + + set filename [file nativename $args] + set corner [parse_corner keys] + set min_max [parse_min_max_all_flags flags] + set infer_latches [expr ![info exists flags(-no_latch_infer)]] + read_liberty_cmd $filename $corner $min_max $infer_latches +} + +# sta namespace end +} diff --git a/tcl/Link.tcl b/tcl/Link.tcl new file mode 100644 index 0000000..29aeed6 --- /dev/null +++ b/tcl/Link.tcl @@ -0,0 +1,38 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +# Network commands. + +namespace eval sta { + +define_cmd_args "link_design" {[top_cell_name]} + +proc link_design { {top_cell_name ""} } { + variable current_design_name + + if { $top_cell_name == "" } { + if { $current_design_name == "" } { + sta_error "missing top_cell_name argument and no current_design." + return 0 + } else { + set top_cell_name $current_design_name + } + } + link_design_cmd $top_cell_name +} + +# sta namespace end +} diff --git a/tcl/Makefile.am b/tcl/Makefile.am new file mode 100644 index 0000000..9f45ffe --- /dev/null +++ b/tcl/Makefile.am @@ -0,0 +1,29 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +TCL_SRCS = \ + NetworkEdit.i \ + StaException.i \ + StaTcl.i + +EXTRA_DIST = \ + $(TCL_INIT_FILES) $(TCL_SRCS) + +libs: + +xtags: + etags -a -o ../TAGS --lang=none --regex='/proc[ \t]+\([^ \t]+\)/\1/' \ + $(TCL_INIT_FILES) $(TCL_SRCS) diff --git a/tcl/Network.tcl b/tcl/Network.tcl new file mode 100644 index 0000000..750b4ff --- /dev/null +++ b/tcl/Network.tcl @@ -0,0 +1,530 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +# Network reporting commands. + +namespace eval sta { + +proc set_cmd_namespace { namespc } { + if { $namespc == "sdc" || $namespc == "sta" } { + set_cmd_namespace_cmd $namespc + } else { + sta_error "unknown namespace $namespc." + } +} + +################################################################ + +define_cmd_args "report_cell" \ + {[-connections] [-verbose] instance_path [> filename] [>> filename]} + +# Alias for report_instance. +proc_redirect report_cell { + eval report_instance $args +} + +################################################################ + +define_cmd_args "report_instance" \ + {[-connections] [-verbose] instance_path [> filename] [>> filename]} + +proc_redirect report_instance { + parse_key_args "report_instance" args keys {} flags {-connections -verbose} + check_argc_eq1 "report_instance" $args + + set connections [info exists flags(-connections)] + set verbose [info exists flags(-verbose)] + set instance_path [lindex $args 0] + set instance [find_instance $instance_path] + if { $instance != "NULL" } { + report_instance1 $instance $connections $verbose + } else { + sta_error "instance $instance_path not found." + } +} + +proc report_instance1 { instance connections verbose } { + if { $instance == [top_instance] } { + set inst_name "top" + } else { + set inst_name [get_full_name $instance] + } + puts "Instance $inst_name" + set cell [instance_property $instance "cell"] + puts " Cell: [get_name $cell]" + puts " Library: [get_name [$cell library]]" + puts " Path cells: [instance_cell_path $instance]" + if { $connections } { + report_instance_pins $instance $verbose + } + if { $verbose } { + report_instance_children_ $instance + } +} + +proc report_instance_pins { instance verbose } { + report_instance_pins1 $instance $verbose \ + " Input pins:" 0 {"input" "bidirect"} + report_instance_pins1 $instance $verbose \ + " Output pins:" 0 {"output" "bidirect"} + report_instance_pins1 $instance $verbose \ + " Other pins:" 1 {"internal" "power" "ground" "unknown"} +} + +proc report_instance_pins1 {instance verbose header header_optional dirs} { + set header_shown 0 + if { !$header_optional } { + puts $header + set header_shown 1 + } + set iter [$instance pin_iterator] + while {[$iter has_next]} { + set pin [$iter next] + set dir [pin_direction $pin] + if { [lsearch $dirs $dir] != -1 } { + if { !$header_shown } { + puts $header + set header_shown 1 + } + report_instance_pin $pin $verbose + } + } + $iter finish +} + +proc report_instance_pin { pin verbose } { + puts -nonewline " [$pin port_name] [pin_direction $pin]" + set net [$pin net] + if { $net == "NULL" } { + puts " (unconnected)" + } else { + puts " [get_full_name [$net highest_connected_net]]" + if { $verbose } { + set pins [net_connected_pins_sorted $net] + foreach pin $pins { + if [$pin is_load] { + if [$pin is_top_level_port] { + puts " [get_full_name $pin] [pin_direction $pin] port" + } else { + puts " [get_full_name $pin] [pin_direction $pin]" + } + } + } + } + } +} + +# Concatenate the cell names of the instance parents. +proc instance_cell_path { instance } { + set cell_path "[get_name [instance_property $instance "cell"]]" + set parent [$instance parent] + set top_instance [top_instance] + while { $parent != "NULL" && $parent != $top_instance } { + set cell_path "[get_name [$parent cell]]/$cell_path" + set parent [$parent parent] + } + return $cell_path +} + +proc report_instance_children_ { instance } { + set children [instance_sorted_children $instance] + if { $children != {} } { + puts " Children:" + foreach child $children { + puts " [get_name $child] ([instance_property $child ref_name])" + } + } +} + +proc instance_sorted_children { instance } { + set children {} + set iter [$instance child_iterator] + while {[$iter has_next]} { + lappend children [$iter next] + } + $iter finish + return [sort_by_full_name $children] +} + +################################################################ + +define_cmd_args "report_lib_cell" {cell_name [> filename] [>> filename]} + +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] + if { $cell != "NULL" } { + report_lib_cell_ $cell + } +} + +proc report_lib_cell_ { cell } { + global sta_report_default_digits + + set lib [$cell liberty_library] + puts "Cell [get_name $cell]" + puts "Library [get_name $lib]" + set filename [liberty_cell_property $cell "filename"] + if { $filename != "" } { + puts "File $filename" + } + set iter [$cell liberty_port_iterator] + while {[$iter has_next]} { + set port [$iter next] + if { [$port is_bus] } { + puts -nonewline " [$port bus_name] [liberty_port_direction $port]" + } else { + puts -nonewline " [get_name $port] [liberty_port_direction $port]" + } + set enable [$port tristate_enable] + if { $enable != "" } { + puts -nonewline " enable=$enable" + } + set func [$port function] + if { $func != "" } { + puts -nonewline " function=$func" + } + puts [port_capacitance_str $port $sta_report_default_digits] + } + $iter finish +} + +proc report_cell_ { cell } { + set lib [$cell library] + puts "Cell [get_name $cell]" + puts "Library [get_name $lib]" + set filename [liberty_cell_property $cell "filename"] + if { $filename != "" } { + puts "File $filename" + } + set iter [$cell port_iterator] + while {[$iter has_next]} { + set port [$iter next] + if { [$port is_bus] } { + puts " [$port bus_name] [port_direction $port]" + } else { + puts " [get_name $port] [port_direction $port]" + } + } + $iter finish +} + +define_cmd_args "report_pin" {pin_path [> filename] [>> filename]} + +proc_redirect report_pin { + check_argc_eq1 "report_pin" $args + set pin_path [lindex $args 0] + set pin [get_pin_warn "pin" $pin_path] + if { $pin != "NULL" } { + report_pin_ $pin + } +} + +################################################################ + +define_cmd_args "report_net" \ + {[-connections] [-verbose] [-digits digits] [-hier_pins]\ + 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 -significant_digits} \ + flags {-connections -verbose -hier_pins} + check_argc_eq1 "report_net" $args + + set corner [parse_corner keys] + set digits $sta_report_default_digits + if { [info exists keys(-digits)] } { + set digits $keys(-digits) + } + if { [info exists keys(-significant_digits)] } { + set digits $keys(-significant_digits) + } + + set connections [info exists flags(-connections)] + set verbose [info exists flags(-verbose)] + set hier_pins [info exists flags(-hier_pins)] + set net_path [lindex $args 0] + set net [find_net $net_path] + if { $net != "NULL" } { + report_net1 $net $connections $verbose $hier_pins $corner $digits + } else { + set pin [find_pin $net_path] + if { $pin != "NULL" } { + set net [$pin net] + if { $net != "NULL" } { + report_net1 $net $connections $verbose $hier_pins $corner $digits + } else { + sta_error "net $net_path not found." + } + } else { + sta_error "net $net_path not found." + } + } +} + +proc report_net_ { net } { + global sta_report_default_digits + report_net1 $net 1 1 1 $corner $sta_report_default_digits +} + +proc report_net1 { net connections verbose hier_pins corner digits } { + puts "Net [get_full_name $net]" + if {$connections} { + set pins [net_connected_pins_sorted $net] + if {$verbose} { + report_net_caps $net $pins $corner $digits + } + puts "Driver pins" + report_net_pins $pins "is_driver" $verbose $corner $digits + puts "" + puts "Load pins" + report_net_pins $pins "is_load" $verbose $corner $digits + if {$hier_pins} { + puts "" + puts "Hierarchical pins" + report_net_pins $pins "is_hierarchical" $verbose $corner $digits + } + report_net_other_pins $pins $verbose $corner $digits + } +} + +proc net_connected_pins_sorted { net } { + set pins {} + set iter [$net connected_pin_iterator] + while {[$iter has_next]} { + set pin [$iter next] + lappend pins $pin + } + $iter finish + set pins [sort_by_full_name $pins] + 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 + + set pin_count 0 + set driver_count 0 + set load_count 0 + foreach pin $pins { + if {![$pin is_hierarchical]} { + incr pin_count + } + if [$pin is_driver] { + incr driver_count + } elseif [$pin is_load] { + incr load_count + } + } + puts " Number of drivers: $driver_count" + puts " Number of loads: $load_count" + puts " Number of pins: $pin_count" + puts "" +} + +proc report_net_cap { net caption cap_msg corner digits } { + set cap_r_min [$net $cap_msg "rise" $corner "min"] + set cap_r_max [$net $cap_msg "rise" $corner "max"] + set cap_f_min [$net $cap_msg "fall" $corner "min"] + set cap_f_max [$net $cap_msg "fall" $corner "max"] + puts " $caption capacitance: [capacitances_str $cap_r_min $cap_r_max $cap_f_min $cap_f_max $digits]" +} + +proc report_net_pins { pins pin_pred verbose corner digits } { + foreach pin $pins { + if {[$pin $pin_pred]} { + report_net_pin $pin $verbose $corner $digits + } + } +} + +proc report_net_other_pins { pins verbose corner digits } { + set header 0 + foreach pin $pins { + if { !([$pin is_driver] || [$pin is_load] || [$pin is_hierarchical]) } { + if { !$header } { + puts "" + puts "Other pins" + set header 1 + } + report_net_pin $pin $verbose $corner $digits + } + } +} + +proc report_net_pin { pin verbose corner digits } { + if [$pin is_leaf] { + set cell_name [get_name [[$pin instance] cell]] + puts -nonewline " [get_full_name $pin] [pin_direction $pin] ($cell_name)" + if { $verbose } { + set liberty_port [$pin liberty_port] + if { $liberty_port != "NULL" } { + puts -nonewline [port_capacitance_str $liberty_port $digits] + } + } + puts "" + } elseif [$pin is_top_level_port] { + puts -nonewline " [get_full_name $pin] [pin_direction $pin] port" + if { $verbose } { + set port [$pin port] + set cap_r_min [port_ext_wire_cap $port "rise" "min"] + set cap_r_max [port_ext_wire_cap $port "rise" "max"] + set cap_f_min [port_ext_wire_cap $port "fall" "min"] + set cap_f_max [port_ext_wire_cap $port "fall" "max"] + if { $cap_r_min > 0 || $cap_r_max > 0 || $cap_f_min > 0 || $cap_r_max > 0 } { + puts -nonewline " wire [capacitances_str $cap_r_min $cap_r_max $cap_f_min $cap_f_max $digits]" + } + + set port [$pin port] + set cap_r_min [port_ext_pin_cap $port "rise" "min"] + set cap_r_max [port_ext_pin_cap $port "rise" "max"] + set cap_f_min [port_ext_pin_cap $port "fall" "min"] + set cap_f_max [port_ext_pin_cap $port "fall" "max"] + if { $cap_r_min > 0 || $cap_r_max > 0 || $cap_f_min > 0 || $cap_r_max > 0} { + puts -nonewline " pin [capacitances_str $cap_r_min $cap_r_max $cap_f_min $cap_f_max $digits]" + } + } + puts "" + } elseif [$pin is_hierarchical] { + puts " [get_full_name $pin] [pin_direction $pin]" + } +} + +################################################################ + +proc report_pin_ { pin } { + global sta_report_default_digits + + puts -nonewline "Pin [get_full_name $pin] [pin_direction_desc $pin]" + + set liberty_port [$pin liberty_port] + if { $liberty_port != "NULL" } { + puts -nonewline [port_capacitance_str $liberty_port $sta_report_default_digits] + } + + if { [$pin is_top_level_port] } { + set term [$pin term] + if { $term == "NULL" } { + set net "NULL" + } else { + set net [$term net] + } + } else { + set net [$pin net] + } + if { $net == "NULL" } { + puts " (unconnected)" + } else { + puts " [get_full_name [$net highest_connected_net]]" + } +} + +proc pin_direction_desc { pin } { + if [$pin is_hierarchical] { + return "hierarchical [pin_direction $pin]" + } elseif [$pin is_top_level_port] { + return "[pin_direction $pin] port" + } else { + return [pin_direction $pin] + } +} + +# Do not preceed this field by a space in the caller. +proc port_capacitance_str { liberty_port digits } { + set cap_r_min [$liberty_port capacitance "rise" "min"] + set cap_r_max [$liberty_port capacitance "rise" "max"] + set cap_f_min [$liberty_port capacitance "fall" "min"] + set cap_f_max [$liberty_port capacitance "fall" "max"] + if { $cap_r_min > 0 || $cap_r_max > 0 || $cap_f_min > 0 || $cap_r_max > 0 } { + return " [capacitances_str $cap_r_min $cap_r_max $cap_f_min $cap_f_max $digits]" + } else { + return "" + } +} + +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 } { + # 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 } { + # Mins equal maxes. + return "r [format_capacitance $cap_r_min $digits] f [format_capacitance $cap_f_min $digits]" + } else { + return "r [format_capacitance $cap_r_min $digits]:[format_capacitance $cap_r_max $digits] f [format_capacitance $cap_f_min $digits]:[format_capacitance $cap_f_max $digits]" + } +} + +################################################################ +# +# Debugging functions +# +################################################################ + +proc_redirect report_network { + report_hierarchy [top_instance] +} + +proc report_hierarchy { inst } { + report_instance1 $inst 1 1 + foreach child [instance_sorted_children $inst] { + report_hierarchy $child + } +} + +proc port_direction_any_input { dir } { + return [expr { $dir == "input" || $dir == "bidirect" } ] +} + +proc port_direction_any_output { dir } { + return [expr { $dir == "output" \ + || $dir == "bidirect" \ + || $dir == "tristate" } ] +} + +# collect instance pins into list +proc instance_pins { instance } { + set pins {} + set iter [$instance pin_iterator] + while {[$iter has_next]} { + lappend pins [$iter next] + } + $iter finish + return $pins +} + +# collect ports into a list +proc cell_ports { cell } { + set ports {} + set iter [$cell port_iterator] + while {[$iter has_next]} { + lappend ports [$iter next] + } + $iter finish + return $ports +} + +# sta namespace end +} diff --git a/tcl/NetworkEdit.i b/tcl/NetworkEdit.i new file mode 100644 index 0000000..0fe381a --- /dev/null +++ b/tcl/NetworkEdit.i @@ -0,0 +1,96 @@ +%module NetworkEdit + +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +using sta::Cell; +using sta::Instance; +using sta::Net; +using sta::Port; +using sta::Pin; +using sta::NetworkEdit; +using sta::cmdEditNetwork; + +%} + +//////////////////////////////////////////////////////////////// +// +// SWIG type definitions. +// +//////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////// +// +// C++ functions visible as TCL functions. +// +//////////////////////////////////////////////////////////////// + +%inline %{ + +Instance * +make_instance_cmd(const char *name, + LibertyCell *cell, + Instance *parent) +{ + return Sta::sta()->makeInstance(name, cell, parent); +} + +void +delete_instance_cmd(Instance *inst) +{ + Sta::sta()->deleteInstance(inst); +} + +void +replace_cell_cmd(Instance *inst, + LibertyCell *to_cell) +{ + Sta::sta()->replaceCell(inst, to_cell); +} + +Net * +make_net_cmd(const char *name, + Instance *parent) +{ + Net *net = cmdEditNetwork()->makeNet(name, parent); + // Sta notification unnecessary. + return net; +} + +void +delete_net_cmd(Net *net) +{ + Sta::sta()->deleteNet(net); +} + +void +connect_pin_cmd(Instance *inst, + Port *port, + Net *net) +{ + Sta::sta()->connectPin(inst, port, net); +} + +void +disconnect_pin_cmd(Pin *pin) +{ + Sta::sta()->disconnectPin(pin); +} + +%} // inline diff --git a/tcl/NetworkEdit.tcl b/tcl/NetworkEdit.tcl new file mode 100644 index 0000000..1445345 --- /dev/null +++ b/tcl/NetworkEdit.tcl @@ -0,0 +1,267 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +# Network editing commands. + +namespace eval sta { + +proc connect_pin { net pin } { + set insts_port [parse_connect_pin $pin] + if { $insts_port == 0 } { + return 0 + } + set net [get_net_warn "net" $net] + if { $net == "NULL" } { + return 0 + } + lassign $insts_port inst port + connect_pin_cmd $inst $port $net + return 1 +} + +proc parse_connect_pin { arg } { + set path_regexp [path_regexp] + set insts_port {} + if { [is_object $arg] } { + set object_type [object_type $arg] + if { $object_type == "Pin" } { + set pin $arg + set inst [$pin instance] + set port [$pin port] + } elseif { $object_type == "Port" } { + # Explicit port arg - convert to pin. + set pin [find_pin [get_name $arg]] + set inst [$pin instance] + set port [$pin port] + } else { + sta_error "unsupported object type $object_type." + } + } else { + if {[regexp $path_regexp $arg ignore path_name port_name]} { + set inst [find_instance $path_name] + if { $inst == "NULL" } { + return 0 + } + } else { + set inst [top_instance] + set port_name $arg + } + set cell [$inst cell] + set port [$cell find_port $port_name] + if { $port == "NULL" } { + return 0 + } + set pin [$inst find_pin $port_name] + } + + # Make sure the pin is not currently connected to a net. + if { $pin != "NULL" \ + && ![$pin is_hierarchical] \ + && [$pin net] != "NULL" } { + return 0 + } + return [list $inst $port] +} + +proc connect_pins { net pins } { + sta_warn "connect_pins is deprecated. Use connect_pin." + # Visit the pins to make sure command will succeed. + set insts_ports [parse_connect_pins $pins] + if { $insts_ports == 0 } { + return 0 + } + set net [get_net_warn "net" $net] + if { $net == "NULL" } { + return 0 + } + foreach {inst port} $insts_ports { + connect_pin_cmd $inst $port $net + } + return 1 +} + +proc parse_connect_pins { arg } { + set path_regexp [path_regexp] + set inst_ports {} + # Copy backslashes that will be removed by foreach. + set arg [string map {\\ \\\\} $arg] + foreach obj $arg { + set inst_port [parse_connect_pin $obj] + if { $inst_port == 0 } { + return 0 + } + set inst_ports [concat $inst_ports $inst_port] + } + return $inst_ports +} + +################################################################ + +proc delete_instance { instance } { + if { [is_object $instance] } { + set object_type [object_type $instance] + if { $object_type == "Instance" } { + set inst $obj + } else { + sta_error "unsupported object type $object_type." + } + } else { + set inst [find_instance $instance] + } + if { $inst != "NULL" } { + delete_instance_cmd $inst + } +} + +################################################################ + +proc delete_net { net } { + if { [is_object $net] } { + set object_type [object_type $net] + if { $object_type != "Net" } { + sta_error "unsupported object type $object_type." + } + } else { + set net [find_net $net] + } + if { $net != "NULL" } { + delete_net_cmd $net + } +} + +################################################################ + +proc disconnect_pin { net pin } { + set net [get_net_warn "net" $net] + if { $net == "NULL" } { + return 0 + } + if { $pin == "-all" } { + set iter [$net connected_pin_iterator] + while {[$iter has_next]} { + set pin [$iter next] + disconnect_pin_cmd $pin + } + $iter finish + return 1 + } else { + set pin1 [get_port_pin_warn "pin" $pin] + if { $pin1 == "NULL" } { + return 0 + } else { + disconnect_pin_cmd $pin1 + return 1 + } + } +} + +proc disconnect_pins { net pins } { + sta_warn "disconnect_pins is deprecated. Use disconnect_pin." + foreach pin $pins { + disconnect_pin $net $pins + } +} + +################################################################ + +proc make_instance { inst_path lib_cell } { + set lib_cell [get_lib_cell_warn "lib_cell" $lib_cell] + if { $lib_cell != "NULL" } { + set path_regexp [path_regexp] + 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] + } + } else { + set inst_name $inst_path + set parent [top_instance] + } + return [make_instance_cmd $inst_name $lib_cell $parent] + } else { + return 0 + } +} + +################################################################ + +proc make_net { net_path } { + # Copy backslashes that will be removed by foreach. + set net_path [string map {\\ \\\\} $net_path] + set path_regexp [path_regexp] + if {[regexp $path_regexp $net_path ignore path_name net_name]} { + set parent [find_instance $path_name] + if { $parent == "NULL" } { + return 0 + } + } else { + set parent [top_instance] + set net_name $net_path + } + return [make_net_cmd $net_name $parent] +} + +################################################################ + +proc replace_cell { instance lib_cell } { + set cell [get_lib_cell_warn "lib_cell" $lib_cell] + if { $cell != "NULL" } { + set inst [get_instance_error "instance" $instance] + set inst_cell [$inst liberty_cell] + if { $inst_cell == "NULL" \ + || ![equiv_cell_ports $inst_cell $cell] } { + return 0 + } + replace_cell_cmd $inst $cell + return 1 + } else { + return 0 + } +} + +################################################################ + +proc insert_buffer { buffer_name buffer_cell net load_pins buffer_out_net_name } { + set buffer_cell [sta::get_lib_cell_warn "buffer_cell" $buffer_cell] + set net [sta::get_net_warn "net" $net] + + if { [get_cells -quiet $buffer_name] != "" } { + sta_error "instance $buffer_name already exists." + } + if { [get_nets -quiet $buffer_out_net_name] != "" } { + sta_error "net $buffer_out_net_name already exists." + } + # Copy backslashes that will be removed by foreach. + set load_pins1 [string map {\\ \\\\} $load_pins] + set load_pins {} + foreach pin $load_pins1 { + set pin1 [get_port_pin_warn "pin" $pin] + if { $pin1 != "NULL" } { + lappend load_pins $pin1 + } + } + if { $buffer_cell != "NULL" \ + && $net != "NULL" } { + insert_buffer_cmd $buffer_name $buffer_cell $net $load_pins $buffer_out_net_name + } +} + +# sta namespace end. +} diff --git a/tcl/Power.tcl b/tcl/Power.tcl new file mode 100644 index 0000000..765c852 --- /dev/null +++ b/tcl/Power.tcl @@ -0,0 +1,248 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +################################################################ +# +# Power commands. +# +################################################################ + +namespace eval sta { + +define_cmd_args "report_power" \ + { [-instances instances]\ + [-corner corner_name]\ + [-digits digits]\ + [> filename] [>> filename] } + +proc_redirect report_power { + global sta_report_default_digits + + parse_key_args "report_power" args keys {-instances -corner -digits} flags {} 1 + + if { [info exists keys(-digits)] } { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } + set corner [parse_corner keys] + + if { [info exists keys(-instances)] } { + set insts [get_instances_error "-instances" $keys(-instances)] + report_power_insts $insts $corner $digits + } else { + report_power_design $corner $digits + } +} + +proc report_power_design { corner digits } { + set power_result [design_power $corner] + set totals [lrange $power_result 0 3] + set sequential [lrange $power_result 4 7] + set combinational [lrange $power_result 8 11] + set macro [lrange $power_result 12 15] + set pad [lrange $power_result 16 end] + lassign $totals design_internal design_switching design_leakage design_total + + set field_width [max [expr $digits + 6] 10] + report_power_title5 "Group" "Internal" "Switching" "Leakage" "Total" $field_width + report_power_title5 " " "Power" "Power" "Power" "Power" $field_width + report_title_dashes5 $field_width + report_power_row "Sequential" $sequential $design_total $field_width $digits + report_power_row "Combinational" $combinational $design_total $field_width $digits + report_power_row "Macro" $macro $design_total $field_width $digits + report_power_row "Pad" $pad $design_total $field_width $digits + report_title_dashes5 $field_width + report_power_row "Total" $power_result $design_total $field_width $digits + + puts -nonewline [format "%-20s" ""] + report_power_col_percent $design_internal $design_total $field_width + report_power_col_percent $design_switching $design_total $field_width + report_power_col_percent $design_leakage $design_total $field_width + puts "" +} + +proc max { x y } { + if { $x >= $y } { + return $x + } else { + return $y + } +} + +proc report_power_title5 { title1 title2 title3 title4 title5 field_width } { + puts -nonewline [format "%-20s" $title1] + report_power_title4 $title2 $title3 $title4 $title5 $field_width +} + +proc report_power_title4 { title1 title2 title3 title4 field_width } { + puts -nonewline [format " %${field_width}s" $title1] + puts -nonewline [format " %${field_width}s" $title2] + puts -nonewline [format " %${field_width}s" $title3] + puts [format " %${field_width}s" $title4] +} + +proc report_title_dashes5 { field_width } { + set count [expr 20 + ($field_width + 1) * 4] + report_title_dashes $count +} + +proc report_title_dashes4 { field_width } { + set count [expr ($field_width + 1) * 4] + report_title_dashes $count +} + +proc report_title_dashes { count } { + for {set i 0} {$i < $count} {incr i} { + puts -nonewline "-" + } + puts "" +} + +proc report_power_row { type row_result design_total field_width digits } { + lassign $row_result internal switching leakage total + if { $design_total == 0.0 } { + set percent 0.0 + } else { + set percent [expr $total / $design_total * 100] + } + puts -nonewline [format "%-20s" $type] + report_power_col $internal $field_width $digits + report_power_col $switching $field_width $digits + report_power_col $leakage $field_width $digits + report_power_col $total $field_width $digits + puts [format " %5.1f%%" $percent] +} + +proc report_power_col { pwr field_width digits } { + puts -nonewline [format " %$field_width.${digits}e" $pwr] +} + +proc report_power_col_percent { col_total total field_width } { + if { $total == 0.0 } { + set percent 0.0 + } else { + set percent [expr $col_total / $total * 100] + } + puts -nonewline [format "%$field_width.1f%%" $percent] +} + +proc report_power_line { type pwr digits } { + puts [format "%-16s %.${digits}e" $type $pwr] +} + +proc report_power_insts { insts corner digits } { + set inst_pwrs {} + foreach inst $insts { + set power_result [instance_power $inst $corner] + lappend inst_pwrs [list $inst $power_result] + } + set inst_pwrs [lsort -command inst_pwr_cmp $inst_pwrs] + + set field_width [max [expr $digits + 6] 10] + + report_power_title4 "Internal" "Switching" "Leakage" "Total" $field_width + report_power_title4 "Power" "Power" "Power" "Power" $field_width + report_title_dashes4 $field_width + + foreach inst_pwr $inst_pwrs { + set inst [lindex $inst_pwr 0] + set power [lindex $inst_pwr 1] + report_power_inst $inst $power $field_width $digits + } +} + +proc inst_pwr_cmp { inst_pwr1 inst_pwr2 } { + set pwr1 [lindex $inst_pwr1 1] + set pwr2 [lindex $inst_pwr2 1] + lassign $pwr1 internal1 switching1 leakage1 total1 + lassign $pwr2 internal2 switching2 leakage2 total2 + if { $total1 < $total2 } { + return 1 + } elseif { $total1 == $total2 } { + return 0 + } else { + return -1 + } +} + +proc report_power_inst { inst power_result field_width digits } { + lassign $power_result internal switching leakage total + report_power_col $internal $field_width $digits + report_power_col $switching $field_width $digits + report_power_col $leakage $field_width $digits + report_power_col $total $field_width $digits + puts " [get_full_name $inst]" +} + +################################################################ + +define_cmd_args "set_power_activity" { [-global]\ + [-input]\ + [-input_ports ports]\ + [-pins pins]\ + [-activiity activity]\ + [-duty duty] } + +proc set_power_activity { args } { + parse_key_args "set_power_activity" args \ + keys {-input_ports -pins -activity -duty} \ + flags {-global -input} 1 + + set activity 0.0 + if { [info exists keys(-activity)] } { + set activity $keys(-activity) + check_float "activity" $activity + if { $activity < 0.0 } { + sta_warn "activity should be 0.0 to 1.0 or 2.0" + } + } + set duty 0.5 + if { [info exists keys(-duty)] } { + set duty $keys(-duty) + check_float "duty" $duty + if { $duty < 0.0 || $duty > 1.0 } { + sta_warn "duty should be 0.0 to 1.0" + } + } + + if { [info exists flags(-global)] } { + set_power_global_activity $activity $duty + } + if { [info exists flags(-input)] } { + set_power_input_activity $activity $duty + } + if { [info exists keys(-input_ports)] } { + set ports [get_ports_error "input_ports" $keys(-input_ports)] + foreach port $ports { + if { [get_property $port "direction"] == "input" } { + set_power_input_port_activity $port $activity $duty + } + } + } + if { [info exists keys(-pins)] } { + set ports [get_pins_error "pins" $keys(-pins)] + foreach pin $pins { + if { [get_property $pin "direction"] == "input" } { + set_power_pin_activity $pin $activity $duty + } + } + } +} + +# sta namespace end. +} diff --git a/tcl/Sdc.tcl b/tcl/Sdc.tcl new file mode 100644 index 0000000..44e6249 --- /dev/null +++ b/tcl/Sdc.tcl @@ -0,0 +1,3200 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +################################################################ +# +# SDC commands. +# Alphabetical within groups as in the SDC spec. +# +# Argument parsing and checking is done in TCL before calling a +# SWIG interface function. +# +################################################################ + +namespace eval sta { + +define_cmd_args "read_sdc" {[-echo] filename} + +proc_redirect read_sdc { + parse_key_args "read_sdc" args keys {} flags {-echo} + + check_argc_eq1 "read_sdc" $args + set echo [info exists flags(-echo)] + set filename [lindex $args 0] + source_ $filename $echo 0 +} + +################################################################ + +set ::sta_continue_on_error 1 + +define_cmd_args "source" \ + {[-echo] [-verbose] filename [> filename] [>> filename]} + +# Override source to support -echo and return codes. +proc_redirect source { + parse_key_args "source" args keys {-encoding} flags {-echo -verbose} + if { [llength $args] != 1 } { + cmd_usage_error "source" + } + set echo [info exists flags(-echo)] + set verbose [info exists flags(-verbose)] + set filename [lindex $args 0] + source_ $filename $echo $verbose +} + +proc source_ { filename echo verbose } { + global sta_continue_on_error + variable sdc_file + variable sdc_line + if [catch {open $filename r} stream] { + sta_error "cannot open '$filename'." + } else { + # Save file and line in recursive call to source. + if { [info exists sdc_file] } { + set sdc_file_save $sdc_file + set sdc_line_save $sdc_line + } + set sdc_file $filename + set sdc_line 1 + set cmd "" + set errors 0 + while {![eof $stream]} { + gets $stream line + if { $line != "" } { + if {$echo} { + puts $line + } + } + append cmd $line "\n" + if { [string index $line end] != "\\" \ + && [info complete $cmd] } { + set error {} + switch [catch {uplevel \#0 $cmd} result] { + 0 { if { $verbose && $result != "" } { puts $result } } + 1 { set error $result } + 2 { set error {invoked "return" outside of a proc.} } + 3 { set error {invoked "break" outside of a loop.} } + 4 { set error {invoked "continue" outside of a loop.} } + } + set cmd "" + if { $error != {} } { + if { [string first "Error" $error] == 0 } { + puts $error + } else { + puts "Error: [file tail $sdc_file], $sdc_line $error" + } + set errors 1 + if { !$sta_continue_on_error } { + break + } + } + } + incr sdc_line + } + close $stream + if { $cmd != {} } { + sta_error "incomplete command at end of file." + } + if { [info exists sdc_file_save] } { + set sdc_file $sdc_file_save + set sdc_line $sdc_line_save + } else { + unset sdc_file + unset sdc_line + } + return $errors + } +} + +################################################################ + +define_cmd_args "write_sdc" \ + {[-no_timestamp] [-digits digits] filename} + +proc write_sdc { args } { + parse_key_args "write_sdc" args keys {-digits -significant_digits} \ + flags {-compatible -no_timestamp} + check_argc_eq1 "write_sdc" $args + + set digits 4 + if { [info exists keys(-digits)] } { + set digits $keys(-digits) + } + if { [info exists keys(-significant_digits)] } { + set digits $keys(-significant_digits) + } + check_positive_integer "-digits" $digits + + set filename [file nativename [lindex $args 0]] + set no_timestamp [info exists flags(-no_timestamp)] + set compatible [info exists flags(-native)] + write_sdc_cmd $filename $compatible $no_timestamp $digits +} + +################################################################ +# +# General Purpose Commands +# +################################################################ + +define_cmd_args "current_instance" {[instance]} + +proc current_instance { {inst ""} } { + if { $inst == "" } { + set current_instance [top_instance] + } else { + set current_instance [get_instance_error "instance" $inst] + } + set cell [get_name [$current_instance cell]] + puts "Current instance is $cell." + # Current instance state variable must be part of the sta state so + # the tcl interpreter can be shared by multiple sdc files. + set_current_instance $current_instance +} + +################################################################ + +define_cmd_args "set_hierarchy_separator" { seperator } + +set ::hierarchy_separator "/" + +proc set_hierarchy_separator { separator } { + global hierarchy_separator + check_path_divider $separator + set_path_divider $separator + set hierarchy_separator $separator +} + +proc check_path_divider { divider } { + set sdc_dividers "/@^#.|" + if { !([string length $divider] == 1 + && [string first $divider $sdc_dividers] != -1)} { + sta_error "hierarchy separator must be one of '$sdc_dividers'." + } +} + +################################################################ + +define_cmd_args "set_units" \ + {[-capacitance cap_unit] [-resistance res_unit] [-time time_unit]\ + [-voltage voltage_unit] [-current current_unit] [-power power_unit]\ + [-distance distance_unit]} + +# Note that the set_units command does NOT actually set the units. +# It merely checks that the current units are the same as the +# units in the set_units command. Blame SNPS for this brain damage. +proc set_units { args } { + parse_key_args "set_units" args \ + keys {-capacitance -resistance -time -voltage -current -power -distance} \ + flags {} + check_argc_eq0 "set_units" $args + check_unit "capacitance" -capacitance "f" keys + check_unit "time" -time "s" keys + check_unit "voltage" -voltage "v" keys + check_unit "current" -current "A" keys + check_unit "resistance" -resistance "ohm" keys + check_unit "distance" -distance "m" keys +} + +proc check_unit { unit key unit_name key_var } { + upvar 1 $key_var keys + if { [info exists keys($key)] } { + set value $keys($key) + if { [string equal -nocase $value $unit_name] } { + check_unit_scale $unit 1.0 + } else { + set prefix [string index $value 0] + set suffix [string range $value 1 end] + if { [string equal -nocase $suffix $unit_name] } { + set scale [unit_prefix_scale $unit $prefix] + check_unit_scale $unit $scale + } else { + sta_error "unknown unit $unit '$suffix'." + } + } + } +} + +proc unit_prefix_scale { unit prefix } { + if { [string equal $prefix "M"] } { + return 1E+6 + } elseif { [string equal $prefix "k"] } { + return 1E+3 + } elseif { [string equal $prefix "m"] } { + return 1E-3 + } elseif { [string equal $prefix "u"] } { + return 1E-6 + } elseif { [string equal $prefix "n"] } { + return 1E-9 + } elseif { [string equal $prefix "p"] } { + return 1E-12 + } elseif { [string equal $prefix "f"] } { + return 1E-15 + } else { + sta_error "unknown $unit prefix '$prefix'." + } +} + +proc check_unit_scale { unit scale } { + set unit_scale [unit_scale $unit] + if { ![fuzzy_equal $scale $unit_scale] } { + sta_warn "$unit scale [format %.0e $scale] does not match library scale [format %.0e $unit_scale]." + } +} + +################################################################ + +define_cmd_args "set_cmd_units" \ + {[-capacitance cap_unit] [-resistance res_unit] [-time time_unit]\ + [-voltage voltage_unit] [-current current_unit] [-power power_unit]\ + [-distance distance_unit]} + +proc set_cmd_units { args } { + parse_key_args "set_cmd_units" args \ + keys {-capacitance -resistance -time -voltage -current -power \ + -distance -digits -suffix} \ + flags {} + + check_argc_eq0 "set_cmd_units" $args + set_unit_values "capacitance" -capacitance "f" keys + set_unit_values "time" -time "s" keys + set_unit_values "voltage" -voltage "v" keys + set_unit_values "current" -current "A" keys + set_unit_values "resistance" -resistance "ohm" keys + set_unit_values "distance" -distance "m" keys +} + +proc set_unit_values { unit key unit_name key_var } { + upvar 1 $key_var keys + if { [info exists keys($key)] } { + set value $keys($key) + if { [string equal -nocase $value $unit_name] } { + set_cmd_unit_scale $unit 1.0 + } else { + set prefix [string index $value 0] + set suffix [string range $value 1 end] + if { [string equal -nocase $suffix $unit_name] } { + set scale [unit_prefix_scale $unit $prefix] + set_cmd_unit_scale $unit $scale + } else { + sta_error "unknown $unit unit '$suffix'." + } + } + if [info exists keys(-digits)] { + set_cmd_unit_digits $unit $keys(-digits) + } + if [info exists keys(-suffix)] { + set_cmd_unit_suffix $unit $keys(-suffix) + } + } +} + +################################################################ +# +# Object Access Commands +# +################################################################ + +define_cmd_args "all_clocks" {} + +proc all_clocks { } { + set clks {} + set clk_iter [clock_iterator] + while {[$clk_iter has_next]} { + set clk [$clk_iter next] + lappend clks $clk + } + $clk_iter finish + return $clks +} + +################################################################ + +define_cmd_args "all_inputs" {} + +proc all_inputs { } { + return [all_ports_for_direction "input"] +} + +################################################################ + +define_cmd_args "all_outputs" {} + +proc all_outputs { } { + return [all_ports_for_direction "output"] +} + +proc all_ports_for_direction { direction } { + set top_instance [top_instance] + set top_cell [$top_instance cell] + set ports {} + set iter [$top_cell port_iterator] + while {[$iter has_next]} { + set port [$iter next] + set port_dir [port_direction $port] + if { $port_dir == $direction || $port_dir == "bidirect" } { + set ports [concat $ports [port_members $port]] + } + } + $iter finish + return $ports +} + +proc port_members { port } { + if [$port is_bus] { + # Expand bus ports. + set ports {} + set member_iter [$port member_iterator] + while {[$member_iter has_next]} { + set bit_port [$member_iter next] + lappend ports $bit_port + } + $member_iter finish + return $ports + } else { + return $port + } +} + +################################################################ + +define_cmd_args all_registers \ + {[-clock clocks] [-rise_clock clocks] [-fall_clock clocks] [-cells] [-data_pins] [-clock_pins]\ + [-async_pins] [-output_pins] [-level_sensitive] [-edge_triggered]} + +proc all_registers { args } { + parse_key_args "all_registers" args keys {-clock -rise_clock -fall_clock} \ + flags {-cells -data_pins -clock_pins -async_pins -output_pins -level_sensitive -edge_triggered} + check_argc_eq0 "all_registers" $args + + set clks {} + set clk_tr "rise_fall" + if [info exists keys(-clock)] { + set clks [get_clocks_warn "clocks" $keys(-clock)] + } + if [info exists keys(-rise_clock)] { + set clks [get_clocks_warn "clocks" $keys(-rise_clock)] + set clk_tr "rise" + } + if [info exists keys(-fall_clock)] { + set clks [get_clocks_warn "clocks" $keys(-fall_clock)] + set clk_tr "fall" + } + + if {[info exists flags(-edge_triggered)] \ + && ![info exists flags(-level_sensitive)]} { + set edge_triggered 1 + set level_sensitive 0 + } elseif {[info exists flags(-level_sensitive)] \ + && ![info exists flags(-edge_triggered)]} { + set level_sensitive 1 + set edge_triggered 0 + } else { + set edge_triggered 1 + set level_sensitive 1 + } + if [info exists flags(-cells)] { + return [find_register_instances $clks $clk_tr \ + $edge_triggered $level_sensitive] + } elseif [info exists flags(-data_pins)] { + return [find_register_data_pins $clks $clk_tr \ + $edge_triggered $level_sensitive] + } elseif [info exists flags(-clock_pins)] { + return [find_register_clk_pins $clks $clk_tr \ + $edge_triggered $level_sensitive] + } elseif [info exists flags(-async_pins)] { + return [find_register_async_pins $clks $clk_tr \ + $edge_triggered $level_sensitive] + } elseif [info exists flags(-output_pins)] { + return [find_register_output_pins $clks $clk_tr \ + $edge_triggered $level_sensitive] + } else { + # -cells is the default. + return [find_register_instances $clks $clk_tr \ + $edge_triggered $level_sensitive] + } +} + +################################################################ + +define_cmd_args "current_design" {[design]} + +variable current_design_name "" + +proc current_design { {design ""} } { + variable current_design_name + + if { $design == "" } { + # top_instance errors if the network has not been linked. + set current_design_name [get_name [get_object_property [top_instance] cell]] + } elseif { ![network_is_linked] } { + set current_design_name $design + return $design + } elseif { [network_is_linked] && $design == [get_name [get_object_property [top_instance] cell]] } { + set current_design_name $design + return $design + } else { + sta_warn "current_design for other than top cell not supported." + set current_design_name $design + return $design + } +} + +################################################################ + +define_cmd_args "get_cells" \ + {[-hierarchical] [-hsc separator] [-filter expr]\ + [-regexp] [-nocase] [-quiet] [-of_objects objects] [patterns]} + +define_cmd_alias "get_cell" "get_cells" + +# Really get_instances, but SDC calls instances "cells". +proc get_cells { args } { + global hierarchy_separator + + parse_key_args "get_cells" args keys {-hsc -filter -of_objects} \ + flags {-hierarchical -regexp -nocase -quiet} + check_argc_eq0or1 "get_cells" $args + check_nocase_flag flags + + set regexp [info exists flags(-regexp)] + set nocase [info exists flags(-nocase)] + set hierarchical [info exists flags(-hierarchical)] + set quiet [info exists flags(-quiet)] + # Copy backslashes that will be removed by foreach. + set patterns [string map {\\ \\\\} [lindex $args 0]] + set divider $hierarchy_separator + if [info exists keys(-hsc)] { + set divider $keys(-hsc) + check_path_divider $divider + } + set insts {} + if [info exists keys(-of_objects)] { + if { $args != {} } { + sta_warn "patterns argument not supported with -of_objects." + } + parse_pin_net_args $keys(-of_objects) pins nets + foreach pin $pins { + lappend insts [$pin instance] + } + foreach net $nets { + set pin_iter [$net pin_iterator] + while { [$pin_iter has_next] } { + set pin [$pin_iter next] + lappend insts [$pin instance] + } + $pin_iter finish + } + } else { + if { $args == {} } { + set insts [network_leaf_instances] + } else { + foreach pattern $patterns { + if { $divider != $hierarchy_separator } { + regsub $divider $pattern $hierarchy_separator pattern + } + if { $hierarchical } { + set matches [find_instances_hier_matching $pattern $regexp $nocase] + } else { + set matches [find_instances_matching $pattern $regexp $nocase] + } + if { $matches == {} && !$quiet} { + sta_warn "instance '$pattern' not found." + } + set insts [concat $insts $matches] + } + } + if [info exists keys(-filter)] { + set insts [filter_insts1 $keys(-filter) $insts] + } + } + return $insts +} + +proc filter_insts1 { filter objects } { + variable filter_regexp1 + variable filter_or_regexp + variable filter_and_regexp + set filtered_objects {} + # Ignore sub-exprs in filter_regexp1 for expr2 match var. + if { [regexp $filter_or_regexp $filter ignore expr1 \ + ignore ignore ignore expr2] } { + regexp $filter_regexp1 $expr1 ignore attr_name op arg + set filtered_objects1 [filter_insts $attr_name $op $arg $objects] + regexp $filter_regexp1 $expr2 ignore attr_name op arg + set filtered_objects2 [filter_insts $attr_name $op $arg $objects] + set filtered_objects [concat $filtered_objects1 $filtered_objects2] + } elseif { [regexp $filter_and_regexp $filter ignore expr1 \ + ignore ignore ignore expr2] } { + regexp $filter_regexp1 $expr1 ignore attr_name op arg + set filtered_objects [filter_insts $attr_name $op $arg $objects] + regexp $filter_regexp1 $expr2 ignore attr_name op arg + set filtered_objects [filter_insts $attr_name $op $arg $filtered_objects] + } elseif { [regexp $filter_regexp1 $filter ignore attr_name op arg] } { + set filtered_objects [filter_insts $attr_name $op $arg $objects] + } else { + sta_error "unsupported -filter expression." + } + return $filtered_objects +} + +################################################################ + +define_cmd_args "get_clocks" {[-regexp] [-nocase] [-quiet] patterns} + +define_cmd_alias "get_clock" "get_clocks" + +proc get_clocks { args } { + parse_key_args "get_clocks" args keys {} flags {-regexp -nocase -quiet} + check_argc_eq1 "get_clocks" $args + check_nocase_flag flags + + # Copy backslashes that will be removed by foreach. + set patterns [string map {\\ \\\\} [lindex $args 0]] + set regexp [info exists flags(-regexp)] + set nocase [info exists flags(-nocase)] + set clocks {} + foreach pattern $patterns { + set matches [find_clocks_matching $pattern $regexp $nocase] + if { $matches != {} } { + set clocks [concat $clocks $matches] + } else { + if {![info exists flags(-quiet)]} { + sta_warn "clock '$pattern' not found." + } + } + } + return $clocks +} + +################################################################ + +define_cmd_args "get_lib_cells" \ + {[-hsc separator] [-regexp] [-nocase] [-quiet]\ + [-of_objects objects] [patterns]} + +define_cmd_alias "get_lib_cell" "get_lib_cells" + +proc get_lib_cells { args } { + global hierarchy_separator + parse_key_args "get_lib_cells" args keys {-hsc -of_objects} \ + flags {-regexp -nocase -quiet} + check_argc_eq0or1 "get_lib_cells" $args + check_nocase_flag flags + + set regexp [info exists flags(-regexp)] + set nocase [info exists flags(-nocase)] + set cells {} + if [info exists keys(-of_objects)] { + if { $args != {} } { + sta_warn "positional arguments not supported with -of_objects." + } + set insts [get_instances_error "objects" $keys(-of_objects)] + foreach inst $insts { + lappend cells [$inst liberty_cell] + } + } else { + # Copy backslashes that will be removed by foreach. + set patterns [string map {\\ \\\\} [lindex $args 0]] + # Parse library_name/pattern. + set divider $hierarchy_separator + if [info exists keys(-hsc)] { + set divider $keys(-hsc) + check_path_divider $divider + } + set cell_regexp [cell_wild_regexp $divider] + set quiet [info exists flags(-quiet)] + foreach pattern $patterns { + if {[regexp $cell_regexp $pattern ignore lib_name cell_pattern]} { + # SDC does not allow wildcards in the library name. + set libs [get_libs -quiet $lib_name] + if { $libs == {} } { + if {!$quiet} { + sta_warn "library '$lib_name' not found." + } + } else { + foreach lib $libs { + set matches [$lib find_liberty_cells_matching $cell_pattern \ + $regexp $nocase] + if {$matches != {}} { + set cells [concat $cells $matches] + } + } + if { $cells == {} } { + if {!$quiet} { + sta_warn "cell '$cell_pattern' not found." + } + } + } + } else { + if {!$quiet} { + sta_warn "library/cell not found in $pattern." + } + } + } + } + return $cells +} + +################################################################ + +define_cmd_args "get_lib_pins" \ + {[-hsc separator] [-regexp] [-nocase] [-quiet] patterns} + +define_cmd_alias "get_lib_pin" "get_lib_pins" + +# "get_lib_ports" in sta terminology. +proc get_lib_pins { args } { + global hierarchy_separator + parse_key_args "get_lib_pins" args keys {-hsc} flags {-regexp -nocase -quiet} + check_argc_eq1 "get_lib_pins" $args + check_nocase_flag flags + + set regexp [info exists flags(-regexp)] + set nocase [info exists flags(-nocase)] + # Copy backslashes that will be removed by foreach. + set patterns [string map {\\ \\\\} [lindex $args 0]] + # Parse library_name/cell_name/pattern. + set divider $hierarchy_separator + if [info exists keys(-hsc)] { + set divider $keys(-hsc) + check_path_divider $divider + } + set port_regexp [port_wild_regexp $divider] + set ports {} + foreach pattern $patterns { + if [regexp $port_regexp $pattern ignore lib_name cell_name port_pattern] { + set liberty [find_liberty $lib_name] + if { $liberty != "NULL" } { + set cells [$liberty find_liberty_cells_matching $cell_name \ + $regexp $nocase] + if { $cells != {} } { + foreach cell $cells { + set matches [$cell find_liberty_ports_matching $port_pattern \ + $regexp $nocase] + if {$matches != {}} { + set ports [concat $ports $matches] + } + } + if { $ports == {} } { + if {![info exists flags(-quiet)]} { + sta_warn "port '$port_pattern' not found." + } + } + } else { + if {![info exists flags(-quiet)]} { + sta_warn "cell '$cell_name' not found." + } + } + } else { + if {![info exists flags(-quiet)]} { + sta_warn "library '$lib_name' not found." + } + } + } else { + if {![info exists flags(-quiet)]} { + sta_warn "library/cell/port '$pattern' not found." + } + } + } + return $ports +} + +proc check_nocase_flag { flags_var } { + upvar 1 $flags_var flags + if { [info exists flags(-nocase)] && ![info exists flags(-regexp)] } { + sta_warn "-nocase ignored without -regexp." + } +} + +################################################################ + +define_cmd_args "get_libs" {[-regexp] [-nocase] [-quiet] patterns} + +define_cmd_alias "get_lib" "get_libs" + +proc get_libs { args } { + parse_key_args "get_libs" args keys {} flags {-regexp -nocase -quiet} + check_argc_eq1 "get_libs" $args + check_nocase_flag flags + + # Copy backslashes that will be removed by foreach. + set patterns [string map {\\ \\\\} [lindex $args 0]] + set regexp [info exists flags(-regexp)] + set nocase [info exists flags(-nocase)] + set libs {} + foreach pattern $patterns { + set matches [find_liberty_libraries_matching $pattern $regexp $nocase] + if {$matches != {}} { + set libs [concat $libs $matches] + } else { + if {![info exists flags(-quiet)]} { + sta_warn "library '$pattern' not found." + } + } + } + return $libs +} + +proc find_liberty_libraries_matching { pattern regexp nocase } { + # Remove "lib.db:" reference from the library name. + set ix [string last ".db:" $pattern] + if { $ix != -1 } { + set pattern2 [string range $pattern [expr $ix + 4] end] + } else { + set pattern2 $pattern + } + if { $regexp } { + # Anchor the regexp pattern. + set pattern2 "^${pattern}$" + } + set lib_iter [liberty_library_iterator] + set matches {} + while { [$lib_iter has_next] } { + 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]) } { + lappend matches $lib + } + } + $lib_iter finish + return $matches +} + +################################################################ + +define_cmd_args "get_nets" \ + {[-hierarchical] [-hsc separator] [-regexp] [-nocase] [-quiet]\ + [-of_objects objects] [patterns]} + +define_cmd_alias "get_net" "get_nets" + +proc get_nets { args } { + global hierarchy_separator + + parse_key_args get_nets args keys {-hsc -of_objects} \ + flags {-hierarchical -regexp -nocase -quiet} + check_nocase_flag flags + + set regexp [info exists flags(-regexp)] + set nocase [info exists flags(-nocase)] + set hierarchical [info exists flags(-hierarchical)] + set quiet [info exists flags(-quiet)] + # Copy backslashes that will be removed by foreach. + set patterns [string map {\\ \\\\} [lindex $args 0]] + if [info exists keys(-hsc)] { + set divider $keys(-hsc) + check_path_divider $divider + set patterns [string map "$divider $hierarchy_separator" $patterns] + } + set nets {} + if [info exists keys(-of_objects)] { + if { $args != {} } { + sta_warn "patterns argument not supported with -of_objects." + } + parse_inst_pin_arg $keys(-of_objects) insts pins + foreach inst $insts { + set pin_iter [$inst pin_iterator] + while { [$pin_iter has_next] } { + set pin [$pin_iter next] + lappend nets [$pin net] + } + $pin_iter finish + } + foreach pin $pins { + lappend nets [$pin net] + } + } else { + check_argc_eq1 "get_nets" $args + foreach pattern $patterns { + if { $hierarchical } { + set matches [find_nets_hier_matching $pattern $regexp $nocase] + } else { + set matches [find_nets_matching $pattern $regexp $nocase] + } + set nets [concat $nets $matches] + if { $matches == {} && !$quiet } { + sta_warn "net '$pattern' not found." + } + } + } + return $nets +} + +################################################################ + +define_cmd_args "get_pins" \ + {[-hierarchical] [-hsc separator] [-quiet] [-filter expr]\ + [-regexp] [-nocase] [-of_objects objects] patterns} + +define_cmd_alias "get_pin" "get_pins" + +proc get_pins { args } { + global hierarchy_separator + + parse_key_args "get_pins" args keys {-hsc -of_objects -filter} \ + flags {-hierarchical -regexp -nocase -quiet} + check_nocase_flag flags + + set regexp [info exists flags(-regexp)] + set nocase [info exists flags(-nocase)] + set hierarchical [info exists flags(-hierarchical)] + set quiet [info exists flags(-quiet)] + set pins {} + if [info exists keys(-of_objects)] { + if { $args != {} } { + sta_warn "patterns argument not supported with -of_objects." + } + parse_inst_net_arg $keys(-of_objects) insts nets + foreach inst $insts { + set pin_iter [$inst pin_iterator] + while { [$pin_iter has_next] } { + set pin [$pin_iter next] + lappend pins $pin + } + $pin_iter finish + } + foreach net $nets { + set pin_iter [$net pin_iterator] + while { [$pin_iter has_next] } { + set pin [$pin_iter next] + lappend pins $pin + } + $pin_iter finish + } + } else { + check_argc_eq1 "get_pins" $args + set patterns [lindex $args 0] + if [info exists keys(-hsc)] { + set divider $keys(-hsc) + check_path_divider $divider + set patterns [string map "$divider $hierarchy_separator" $patterns] + } + # Copy backslashes that will be removed by foreach. + set patterns [string map {\\ \\\\} $patterns] + foreach pattern $patterns { + if { $hierarchical } { + set matches [find_pins_hier_matching $pattern $regexp $nocase] + } else { + set matches [find_pins_matching $pattern $regexp $nocase] + } + set pins [concat $pins $matches] + if { $matches == {} && !$quiet } { + sta_warn "pin '$pattern' not found." + } + } + } + if [info exists keys(-filter)] { + set pins [filter_pins1 $keys(-filter) $pins] + } + return $pins +} + +proc filter_pins1 { filter objects } { + variable filter_regexp1 + variable filter_or_regexp + variable filter_and_regexp + set filtered_objects {} + # Ignore sub-exprs in filter_regexp1 for expr2 match var. + if { [regexp $filter_or_regexp $filter ignore expr1 \ + ignore ignore ignore expr2] } { + regexp $filter_regexp1 $expr1 ignore attr_name op arg + set filtered_objects1 [filter_pins $attr_name $op $arg $objects] + regexp $filter_regexp1 $expr2 ignore attr_name op arg + set filtered_objects2 [filter_pins $attr_name $op $arg $objects] + set filtered_objects [concat $filtered_objects1 $filtered_objects2] + } elseif { [regexp $filter_and_regexp $filter ignore expr1 \ + ignore ignore ignore expr2] } { + regexp $filter_regexp1 $expr1 ignore attr_name op arg + set filtered_objects [filter_pins $attr_name $op $arg $objects] + regexp $filter_regexp1 $expr2 ignore attr_name op arg + set filtered_objects [filter_pins $attr_name $op $arg $filtered_objects] + } elseif { [regexp $filter_regexp1 $filter ignore attr_name op arg] } { + set filtered_objects [filter_pins $attr_name $op $arg $objects] + } else { + sta_error "unsupported -filter expression." + } + return $filtered_objects +} + +################################################################ + +define_cmd_args "get_ports" \ + {[-quiet] [-filter expr] [-regexp] [-nocase] [-of_objects objects] [patterns]} + +define_cmd_alias "get_port" "get_ports" + +# Find top level design ports matching pattern. +proc get_ports { args } { + parse_key_args "get_ports" args keys {-of_objects -filter} \ + flags {-regexp -nocase -quiet} + check_nocase_flag flags + + set regexp [info exists flags(-regexp)] + set nocase [info exists flags(-nocase)] + # Copy backslashes that will be removed by foreach. + set patterns [string map {\\ \\\\} [lindex $args 0]] + set ports {} + if [info exists keys(-of_objects)] { + if { $args != {} } { + sta_warn "patterns argument not supported with -of_objects." + } + set nets [get_nets_warn "objects" $keys(-of_objects)] + foreach net $nets { + set ports [concat $ports [$net ports]] + } + } else { + check_argc_eq1 "get_ports" $args + foreach pattern $patterns { + set matches [find_ports_matching $pattern $regexp $nocase] + if { $matches != {} } { + set ports [concat $ports $matches] + } else { + if {![info exists flags(-quiet)]} { + sta_warn "port '$pattern' not found." + } + } + } + } + if [info exists keys(-filter)] { + set ports [filter_ports1 $keys(-filter) $ports] + } + return $ports +} + +variable filter_regexp1 {@?([a-zA-Z_]+) +(==|=~) +([0-9a-zA-Z_\*]+)} +variable filter_or_regexp "($filter_regexp1) +\\|\\| +($filter_regexp1)" +variable filter_and_regexp "($filter_regexp1) +&& +($filter_regexp1)" + +proc filter_ports1 { filter objects } { + variable filter_regexp1 + variable filter_or_regexp + variable filter_and_regexp + set filtered_objects {} + # Ignore sub-exprs in filter_regexp1 for expr2 match var. + if { [regexp $filter_or_regexp $filter ignore expr1 \ + ignore ignore ignore expr2] } { + regexp $filter_regexp1 $expr1 ignore attr_name op arg + set filtered_objects1 [filter_ports $attr_name $op $arg $objects] + regexp $filter_regexp1 $expr2 ignore attr_name op arg + set filtered_objects2 [filter_ports $attr_name $op $arg $objects] + set filtered_objects [concat $filtered_objects1 $filtered_objects2] + } elseif { [regexp $filter_and_regexp $filter ignore expr1 \ + ignore ignore ignore expr2] } { + regexp $filter_regexp1 $expr1 ignore attr_name op arg + set filtered_objects [filter_ports $attr_name $op $arg $objects] + regexp $filter_regexp1 $expr2 ignore attr_name op arg + set filtered_objects [filter_ports $attr_name $op $arg $filtered_objects] + } elseif { [regexp $filter_regexp1 $filter ignore attr_name op arg] } { + set filtered_objects [filter_ports $attr_name $op $arg $objects] + } else { + sta_error "unsupported -filter expression." + } + return $filtered_objects +} + +################################################################ +# +# Timing Constraints +# +################################################################ + +define_cmd_args "create_clock" \ + {[-name name] [-period period] [-waveform waveform] [-add]\ + [-comment comment] [pins]} + +proc create_clock { args } { + parse_key_args "create_clock" args \ + keys {-name -period -waveform -comment} \ + flags {-add} + + check_argc_eq0or1 "create_clock" $args + set argc [llength $args] + if { $argc == 0 } { + set pins {} + } elseif { $argc == 1 } { + set pins [get_port_pins_error "pins" [lindex $args 0]] + } + + set add [info exists flags(-add)] + if [info exists keys(-name)] { + set name $keys(-name) + } elseif { $pins != {} } { + if { $add } { + sta_error "-add requires -name." + } + # Default clock name is the first pin name. + set name [get_full_name [lindex $pins 0]] + } else { + sta_error "-name or port_pin_list must be specified." + } + + if [info exists keys(-period)] { + set period $keys(-period) + check_positive_float "period" $period + set period [time_ui_sta $period] + } else { + sta_error "missing -period argument." + } + + if [info exists keys(-waveform)] { + set wave_arg $keys(-waveform) + if { [expr [llength $wave_arg] % 2] != 0 } { + sta_error "-waveform edge_list must have an even number of edge times." + } + set first_edge 1 + set prev_edge 0 + set waveform {} + foreach edge $wave_arg { + check_float "-waveform edge" $edge + set edge [time_ui_sta $edge] + if { $first_edge && $edge > $period } { + set edge [expr $edge - $period] + } + if { !$first_edge && $edge < $prev_edge } { + sta_warn "adjusting non-increasing clock -waveform edge times." + set edge [expr $edge + $period] + } + if { $edge > [expr $period * 2] } { + sta_warn "-waveform time greater than two periods." + } + lappend waveform $edge + set prev_edge $edge + set first_edge 0 + } + } else { + set waveform [list 0 [expr $period / 2.0]] + } + + set comment [parse_comment_key keys] + + make_clock $name $pins $add $period $waveform $comment +} + +################################################################ + +define_cmd_args "create_generated_clock" \ + {[-name clock_name] -source master_pin [-master_clock clock]\ + [-divide_by divisor | -multiply_by multiplier]\ + [-duty_cycle duty_cycle] [-invert] [-edges edge_list]\ + [-edge_shift edge_shift_list] [-combinational] [-add]\ + [-pll_output pll_out_pin] [-pll_feedback pll_fdbk_pin]\ + [-comment comment] port_pin_list} + +proc create_generated_clock { args } { + parse_key_args "create_generated_clock" args keys \ + {-name -source -master_clock -divide_by -multiply_by \ + -duty_cycle -edges -edge_shift -pll_output -pll_feedback -comment} \ + flags {-invert -combinational -add} + + check_argc_eq1 "create_generated_clock" $args + set pins [get_port_pins_error "pins" [lindex $args 0]] + if { $pins == {} } { + sta_error "empty ports/pins argument." + } + set add [info exists flags(-add)] + + if [info exists keys(-name)] { + set name $keys(-name) + } elseif { $pins != {} } { + if { $add } { + sta_error "-add requires -name." + } + # Default clock name is the first pin name. + set name [get_full_name [lindex $pins 0]] + } else { + sta_error "name or port_pin_list must be specified." + } + + if [info exists keys(-source)] { + set source $keys(-source) + set source_pin [get_port_pin_error "master_pin" $source] + } else { + sta_error "missing -source argument." + } + + set master_clk "NULL" + set divide_by 0 + set multiply_by 0 + set duty_cycle 0 + set edges {} + set edge_shifts {} + set pll_out "NULL" + set pll_feedback "NULL" + + set combinational [info exists flags(-combinational)] + + if {[info exists keys(-master_clock)]} { + set master_clk [get_clock_error "-master_clk" $keys(-master_clock)] + if { $master_clk == "NULL" } { + sta_error "-master_clock argument empty." + } + } elseif { $add } { + sta_error "-add requireds -master_clock." + } + + if {[info exists keys(-divide_by)] && [info exists keys(-multiply_by)]} { + sta_error "-multiply_by and -divide_by options are exclusive." + } elseif {[info exists keys(-divide_by)]} { + set divide_by $keys(-divide_by) + if {![string is integer $divide_by] || $divide_by < 1} { + sta_error "-divide_by is not an integer greater than one." + } + if {$combinational && $divide_by != 1} { + sta_error "-combinational implies -divide_by 1." + } + } elseif {[info exists keys(-multiply_by)]} { + set multiply_by $keys(-multiply_by) + if {![string is integer $multiply_by] || $multiply_by < 1} { + sta_error "-multiply_by is not an integer greater than one." + } + 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 "-duty_cycle is not a float between 0 and 100." + } + } + } elseif {[info exists keys(-edges)]} { + set edges $keys(-edges) + if { [llength $edges] != 3 } { + sta_error "-edges only supported for three edges." + } + set prev_edge [expr [lindex $edges 0] - 1] + foreach edge $edges { + check_cardinal "-edges" $edge + if { $edge <= $prev_edge } { + sta_error "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] + } + if { [llength $edge_shifts] != [llength $edges] } { + sta_error "-edge_shift length does not match -edges length." + } + } + } elseif { $combinational } { + set divide_by 1 + } else { + sta_error "missing -multiply_by, -divide_by, -combinational or -edges argument." + } + + set invert 0 + if {[info exists flags(-invert)]} { + if {!([info exists keys(-divide_by)] \ + || [info exists keys(-multiply_by)] \ + || [info exists flags(-combinational)])} { + sta_error "cannot specify -invert without -multiply_by, -divide_by or -combinational." + } + set invert 1 + } + + if {[info exists keys(-duty_cycle)] && ![info exists keys(-multiply_by)]} { + sta_error "-duty_cycle requires -multiply_by value." + } + + if { [info exists keys(-pll_feedback)] || [info exists keys(-pll_output)] } { + if {![info exists keys(-pll_output)] } { + sta_error "missing -pll_output argument." + } + if { ![info exists keys(-pll_feedback)] } { + sta_error "missing -pll_feedback argument." + } + set pll_feedback [get_pin_error "-pll_feedback" $keys(-pll_feedback)] + set pll_out [get_pin_error "-pll_output" $keys(-pll_output)] + set pll_inst [$pll_out instance] + if { [$pll_feedback instance] != $pll_inst } { + sta_error "PLL output and feedback pins must be on the same instance." + } + if { [$source_pin instance] != $pll_inst } { + sta_error "source pin must be on the same instance as the PLL output pin." + } + if { [lsearch $pins $pll_out] == -1 } { + sta_error "PLL output must be one of the clock pins." + } + } + + set comment [parse_comment_key keys] + + make_generated_clock $name $pins $add $source_pin $master_clk \ + $pll_out $pll_feedback \ + $divide_by $multiply_by $duty_cycle $invert $combinational \ + $edges $edge_shifts $comment +} + +################################################################ + +define_cmd_args "group_path" \ + {-name group_name [-weight weight] [-critical_range range]\ + [-default] [-comment comment]\ + [-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]} + +# The -weight and -critical_range arguments are ignored. +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} \ + flags {-default} 0 + + set cmd "group_path" + set arg_error 0 + set from [parse_from_arg keys arg_error] + set thrus [parse_thrus_arg args arg_error] + set to [parse_to_arg1 keys "rise_fall" arg_error] + check_exception_pins $from $to + if { $arg_error } { + delete_from_thrus_to $from $thrus $to + sta_error "group_path command failed." + return 0 + } + + check_for_key_args $cmd args + if { $args != {} } { + delete_from_thrus_to $from $thrus $to + sta_error "positional arguments not supported." + } + if { ($from == "NULL" && $thrus == "" && $to == "NULL") } { + delete_from_thrus_to $from $thrus $to + sta_error "-from, -through or -to required." + } + + set default [info exists flags(-default)] + set name_exists [info exists keys(-name)] + if { $default && $name_exists } { + sta_error "-name and -default are mutually exclusive." + } elseif { !$name_exists && !$default } { + sta_error "-name or -default option is required." + } elseif { $default } { + set name "" + } else { + set name $keys(-name) + } + + set comment [parse_comment_key keys] + + make_group_path $name $default $from $thrus $to $comment +} + +proc check_exception_pins { from to } { + variable sdc_file + variable sdc_line + if { [info exists sdc_file] } { + set file $sdc_file + set line $sdc_line + } else { + set file "" + set line 0 + } + check_exception_from_pins $from $file $line + check_exception_to_pins $to $file $line +} + +################################################################ + +define_cmd_args "set_clock_gating_check" \ + {[-setup setup_time] [-hold hold_time] [-rise] [-fall]\ + [-low] [-high] [objects]} + +proc set_clock_gating_check { args } { + parse_key_args "set_clock_gating_check" args keys {-setup -hold } \ + flags {-rise -fall -high -low} + + check_argc_eq0or1 "set_clock_gating_check" $args + set tr [parse_rise_fall_flags flags] + + set active_value "" + if {[info exists flags(-high)] && [info exists flags(-low)]} { + sta_error "cannot specify both -high and -low." + } elseif [info exists flags(-low)] { + set active_value "0" + } elseif [info exists flags(-high)] { + set active_value "1" + } + + if { !([info exists keys(-hold)] || [info exists keys(-setup)]) } { + sta_error "missing -setup or -hold argument." + } + if [info exists keys(-hold)] { + set_clock_gating_check1 $args $tr "min" $keys(-hold) $active_value + } + if [info exists keys(-setup)] { + set_clock_gating_check1 $args $tr "max" $keys(-setup) $active_value + } +} + +proc set_clock_gating_check1 { args tr setup_hold margin active_value } { + set margin [time_ui_sta $margin] + if { [llength $args] == 0 } { + if { $active_value != "" } { + sta_error "-high and -low only permitted for pins and instances." + } + set_clock_gating_check_cmd $tr $setup_hold $margin + } elseif { [llength $args] == 1 } { + parse_clk_inst_port_pin_arg [lindex $args 0] clks insts pins + + if { $clks != {} && $active_value != "" } { + sta_error "-high and -low only permitted for pins and instances." + } + foreach clk $clks { + set_clock_gating_check_clk_cmd $clk $tr $setup_hold $margin + } + + if { $active_value == "" } { + set active_value "X" + } + foreach pin $pins { + set_clock_gating_check_pin_cmd $pin $tr $setup_hold \ + $margin $active_value + } + foreach inst $insts { + set_clock_gating_check_instance_cmd $inst $tr $setup_hold \ + $margin $active_value + } + } +} + +################################################################ + +define_cmd_args "set_clock_groups" \ + {[-name name] [-logically_exclusive] [-physically_exclusive]\ + [-asynchronous] [-allow_paths] [-comment comment] -group clocks} + +proc set_clock_groups { args } { + parse_key_args "set_clock_groups" args \ + keys {-name -comment} \ + flags {-logically_exclusive -physically_exclusive \ + -asynchronous -allow_paths} 0 + + if {[info exists keys(-name)]} { + set name $keys(-name) + } else { + set name "" + } + set logically_exclusive [info exists flags(-logically_exclusive)] + set physically_exclusive [info exists flags(-physically_exclusive)] + set asynchronous [info exists flags(-asynchronous)] + set allow_paths [info exists flags(-allow_paths)] + + if { ($logically_exclusive+$physically_exclusive+$asynchronous) == 0 } { + sta_error "one of -logically_exclusive, -physically_exclusive or -asynchronous is required." + } + if { ($logically_exclusive+$physically_exclusive+$asynchronous) > 1 } { + sta_error "the keywords -logically_exclusive, -physically_exclusive and -asynchronous are mutually exclusive." + } + + set comment [parse_comment_key keys] + + set clk_groups [make_clock_groups $name $logically_exclusive \ + $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 { $group_clks != {} } { + clock_groups_make_group $clk_groups $group_clks + } + set args [lrange $args 2 end] + } else { + if {[is_keyword_arg $arg]} { + sta_warn "unknown keyword argument $arg." + } else { + sta_warn "extra positional argument $arg." + } + set args [lrange $args 1 end] + } + } +} + +################################################################ + +define_cmd_args "set_clock_latency" \ + {[-source] [-clock clock] [-rise] [-fall] [-min] [-max]\ + [-early] [-late] delay objects} + +proc set_clock_latency { args } { + parse_key_args "set_clock_latency" args keys {-clock} \ + flags {-rise -fall -min -max -source -late -early} + + check_argc_eq2 "set_clock_latency" $args + + set delay [lindex $args 0] + check_float "delay" $delay + set delay [time_ui_sta $delay] + set objects [lindex $args 1] + + parse_clk_port_pin_arg $objects clks pins + + set tr [parse_rise_fall_flags flags] + set min_max [parse_min_max_all_flags flags] + + set pin_clk "NULL" + if { [info exists keys(-clock)] } { + set pin_clk [get_clock_warn "clock" $keys(-clock)] + if { $clks != {} } { + sta_warn "-clock ignored for clock objects." + } + } + + if {[info exists flags(-source)]} { + # Insertion delay (source latency). + set early_late [parse_early_late_all_flags flags] + + foreach clk $clks { + set_clock_insertion_cmd $clk "NULL" $tr $min_max $early_late $delay + } + foreach pin $pins { + # Source only allowed on clocks and clock pins. + if { ![is_clock_src $pin] } { + sta_error "-source '[get_full_name $pin]' is not a clock pin." + } + set_clock_insertion_cmd $pin_clk $pin $tr $min_max $early_late $delay + } + } else { + # Latency. + if {[info exists flags(-early)] || [info exists flags(-late)]} { + sta_error "-early/-late is only allowed with -source." + } + + foreach clk $clks { + set_clock_latency_cmd $clk "NULL" $tr $min_max $delay + } + foreach pin $pins { + set_clock_latency_cmd $pin_clk $pin $tr $min_max $delay + } + } +} + +################################################################ + +define_cmd_args "set_sense" \ + {[-type clock|data] [-positive] [-negative] [-pulse pulse_type]\ + [-stop_propagation] [-clocks clocks] pins} + +proc set_sense { args } { + parse_key_args "set_sense" args keys {-type} flags {} 0 + + set type "clock" + if { [info exists keys(-type)] } { + set type $keys(-type) + if { $type == "data" } { + sdc_warn "set_sense -type data not supported." + } elseif { $type == "clock" } { + set_clock_sense_cmd1 "set_sense" $args + } else { + sdc_error "set_sense -type clock|data" + } + } +} + +# deprecated in SDC 2.1 +define_cmd_args "set_clock_sense" \ + {[-positive] [-negative] [-pulse pulse_type] [-stop_propagation] \ + [-clock clocks] pins} + +proc set_clock_sense { args } { + sdc_warn "set_clock_sense is deprecated as of SDC 2.1. Use set_sense -type clock." + set_clock_sense_cmd1 "set_clock_sense" $args +} + +proc set_clock_sense_cmd1 { cmd cmd_args } { + # SDC uses -clock, OT, OC use -clocks + parse_key_args $cmd cmd_args keys {-clock -clocks -pulse} \ + flags {-positive -negative -stop_propagation} 0 + check_argc_eq1 $cmd $cmd_args + + set pulse [info exists keys(-pulse)] + if { $pulse } { + sta_warn "-pulse argument not supported." + } + set positive [info exists flags(-positive)] + 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)) + || ($pulse && ($positive || $negative || $stop_propagation)) } { + sta_warn "-positive, -negative, -stop_propagation and -pulse are mutually exclusive." + } + + set pins [get_port_pins_error "pins" [lindex $cmd_args 0]] + set clks {} + if {[info exists keys(-clock)]} { + set clks [get_clocks_warn "clock" $keys(-clock)] + } + if {[info exists keys(-clocks)]} { + set clks [get_clocks_warn "clocks" $keys(-clocks)] + } + foreach pin $pins { + if {[$pin is_hierarchical]} { + sta_warn "hierarchical pin '[get_full_name $pin]' not supported." + } + } + set_clock_sense_cmd $pins $clks $positive $negative $stop_propagation +} + +################################################################ + +define_cmd_args "set_clock_transition" \ + {[-rise] [-fall] [-min] [-max] transition clocks} + +proc set_clock_transition { args } { + parse_key_args "set_clock_transition" args keys {} \ + flags {-rise -fall -max -min} + + set tr [parse_rise_fall_flags flags] + set min_max [parse_min_max_all_flags flags] + check_argc_eq2 "set_clock_transition" $args + + set slew [lindex $args 0] + set clks [get_clocks_warn "clocks" [lindex $args 1]] + + foreach clk $clks { + if { [$clk is_virtual] } { + sta_error "transition time can not be specified for virtual clocks." + } else { + set_clock_slew_cmd $clk $tr $min_max [time_ui_sta $slew] + } + } +} + +################################################################ + +# -rise/-fall are obsolete. +define_cmd_args "set_clock_uncertainty" \ + {[-from|-rise_from|-fall_from from_clock]\ + [-to|-rise_to|-fall_to to_clock] [-rise] [-fall]\ + [-setup] [-hold] uncertainty [objects]} + +proc set_clock_uncertainty { args } { + parse_key_args "set_clock_uncertainty" args \ + keys {-from -rise_from -fall_from -to -rise_to -fall_to} \ + flags {-rise -fall -setup -hold} + + if { [llength $args] == 0 } { + sta_error "missing uncertainty value." + } + set uncertainty [lindex $args 0] + check_float "uncertainty" $uncertainty + set uncertainty [time_ui_sta $uncertainty] + + set min_max "min_max" + if { [info exists flags(-setup)] && ![info exists flags(-hold)] } { + set min_max "max" + } + if { [info exists flags(-hold)] && ![info exists flags(-setup)] } { + set min_max "min" + } + + if { [info exists keys(-from)] } { + set from_key "-from" + set from_tr "rise_fall" + } elseif { [info exists keys(-rise_from)] } { + set from_key "-rise_from" + set from_tr "rise" + } elseif { [info exists keys(-fall_from)] } { + set from_key "-fall_from" + set from_tr "fall" + } else { + set from_key "none" + } + + if { [info exists keys(-to)] } { + set to_key "-to" + set to_tr "rise_fall" + } elseif { [info exists keys(-rise_to)] } { + set to_key "-rise_to" + set to_tr "rise" + } elseif { [info exists keys(-fall_to)] } { + set to_key "-fall_to" + set to_tr "fall" + } else { + set to_key "none" + } + + if { $from_key != "none" && $to_key == "none" \ + || $from_key == "none" && $to_key != "none" } { + sta_error "-from/-to must be used together." + } elseif { $from_key != "none" && $to_key != "none" } { + # Inter-clock uncertainty. + check_argc_eq1 "-from/-to" $args + + # -from/-to can be lists. + set from_clks [get_clocks_warn "from_clocks" $keys($from_key)] + set to_clks [get_clocks_warn "to_clocks" $keys($to_key)] + + foreach from_clk $from_clks { + foreach to_clk $to_clks { + set_inter_clock_uncertainty $from_clk $from_tr \ + $to_clk $to_tr $min_max $uncertainty + } + } + } else { + # Single clock uncertainty. + check_argc_eq2 "set_clock_uncertainty" $args + if { [info exists flags(-rise)] \ + || [info exists flags(-fall)] } { + sta_error "-rise, -fall options not allowed for single clock uncertainty." + } + set objects [lindex $args 1] + parse_clk_port_pin_arg $objects clks pins + + foreach clk $clks { + set_clock_uncertainty_clk $clk $min_max $uncertainty + } + foreach pin $pins { + set_clock_uncertainty_pin $pin $min_max $uncertainty + } + } +} + +################################################################ + +define_cmd_args "set_data_check" \ + {[-from from_pin] [-rise_from from_pin] [-fall_from from_pin]\ + [-to to_pin] [-rise_to to_pin] [-fall_to to_pin]\ + [-setup | -hold] [-clock clock] margin} + +proc set_data_check { args } { + parse_key_args "set_data_check" args \ + keys {-from -rise_from -fall_from -to -rise_to -fall_to -clock} \ + flags {-setup -hold} + check_argc_eq1 "set_data_check" $args + + set margin [time_ui_sta $args] + set from_tr "rise_fall" + set to_tr "rise_fall" + set clk "NULL" + + if [info exists keys(-from)] { + set from [get_port_pin_error "from_pin" $keys(-from)] + } elseif [info exists keys(-rise_from)] { + set from [get_port_pin_error "from_pin" $keys(-rise_from)] + set from_tr "rise" + } elseif [info exists keys(-fall_from)] { + set from [get_port_pin_error "from_pin" $keys(-fall_from)] + set from_tr "fall" + } else { + sta_error "missing -from, -rise_from or -fall_from argument." + } + + if [info exists keys(-to)] { + set to [get_port_pin_error "to_pin" $keys(-to)] + } elseif [info exists keys(-rise_to)] { + set to [get_port_pin_error "to_pin" $keys(-rise_to)] + set to_tr "rise" + } elseif [info exists keys(-fall_to)] { + set to [get_port_pin_error "to_pin" $keys(-fall_to)] + set to_tr "fall" + } else { + sta_error "missing -to, -rise_to or -fall_to argument." + } + + if [info exists keys(-clock)] { + set clk [get_clock_warn "clock" $keys(-clock)] + } + + if { [info exists flags(-setup)] && ![info exists flags(-hold)] } { + set setup_hold "setup" + } elseif { [info exists flags(-hold)] && ![info exists flags(-setup)] } { + set setup_hold "hold" + } else { + set setup_hold "setup_hold" + } + + set_data_check_cmd $from $from_tr $to $to_tr $clk $setup_hold $margin +} + +################################################################ + +define_cmd_args "set_disable_timing" \ + {[-from from_port] [-to to_port] objects} + +# Parallax supports -from or -to alone. +# OT requires both -from and -to args. +proc set_disable_timing { args } { + parse_key_args "set_disable_timing" args keys {-from -to} flags {} + check_argc_eq1 "set_disable_timing" $args + + set from {} + if { [info exists keys(-from)] } { + set from $keys(-from) + } + set to {} + if { [info exists keys(-to)] } { + set to $keys(-to) + } + parse_libcell_libport_inst_port_pin_edge_timing_arc_set_arg $args \ + libcells libports insts ports pins edges timing_arc_sets + + if { ([info exists keys(-from)] || [info exists keys(-to)]) \ + && ($libports != {} || $pins != {} || $ports != {}) } { + sta_warn "-from/-to keywords ignored for lib_pin, port and pin arguments." + } + + foreach libcell $libcells { + set_disable_timing_cell $libcell $from $to + } + foreach libport $libports { + disable_lib_port $libport + } + foreach inst $insts { + set_disable_timing_instance $inst $from $to + } + foreach pin $pins { + disable_pin $pin + } + foreach port $ports { + disable_port $port + } + foreach edge $edges { + disable_edge $edge + } + foreach timing_arc_set $timing_arc_sets { + disable_timing_arc_set $timing_arc_set + } +} + +proc set_disable_timing_instance { inst from to } { + set from_ports [parse_disable_inst_ports $inst $from] + set to_ports [parse_disable_inst_ports $inst $to] + if { ![$inst is_leaf] } { + sta_error "-from/-to hierarchical instance not supported." + } + if { $from_ports == "NULL" && $to_ports == "NULL" } { + disable_instance $inst "NULL" "NULL" + } elseif { $from_ports == "NULL" } { + foreach to_port $to_ports { + disable_instance $inst "NULL" $to_port + } + } elseif { $to_ports == "NULL" } { + foreach from_port $from_ports { + disable_instance $inst $from_port "NULL" + } + } else { + foreach from_port $from_ports { + foreach to_port $to_ports { + disable_instance $inst $from_port $to_port + } + } + } +} + +# Find ports in inst's cell that have pins. +# Bus ports are expanded into a list. +proc parse_disable_inst_ports { inst port_name } { + global hierarchy_separator + + if { $port_name == "" } { + set ports "NULL" + } else { + set cell [instance_property $inst cell] + set port [$cell find_port $port_name] + if { $port == "NULL" } { + sta_error "pin '[get_full_name $inst]${hierarchy_separator}${port_name}' not found." + } else { + set lib_port [get_property $port liberty_port] + set ports [port_members $lib_port] + } + } + return $ports +} + +proc set_disable_timing_cell { cell from to } { + set from_ports [parse_disable_cell_ports $cell $from] + set to_ports [parse_disable_cell_ports $cell $to] + if { $from_ports == "NULL" && $to_ports == "NULL" } { + disable_cell $cell "NULL" "NULL" + } elseif { $from_ports == "NULL" } { + foreach to_port $to_ports { + disable_cell $cell "NULL" $to_port + } + } elseif { $to_ports == "NULL" } { + foreach from_port $from_ports { + disable_cell $cell $from_port "NULL" + } + } else { + foreach from_port $from_ports { + foreach to_port $to_ports { + disable_cell $cell $from_port $to_port + } + } + } +} + +# Find cell ports. +# Bus ports are expanded into a list. +proc parse_disable_cell_ports { cell port_name } { + global hierarchy_separator + + if { $port_name == "" } { + set ports "NULL" + } else { + set port [$cell find_liberty_port $port_name] + if { $port == "NULL" } { + sta_error "pin '[get_name $cell]${hierarchy_separator}${port_name}' not found." + } else { + set ports [port_members $port] + } + } + return $ports +} + +################################################################ + +define_cmd_args "set_false_path" \ + {[-setup] [-hold] [-rise] [-fall] [-reset_path] [-comment comment]\ + [-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]} + +proc set_false_path { args } { + parse_key_args "set_false_path" args \ + keys {-from -rise_from -fall_from -to -rise_to -fall_to -comment} \ + flags {-setup -hold -rise -fall -reset_path} 0 + + set min_max "min_max" + if { [info exists flags(-setup)] && ![info exists flags(-hold)] } { + set min_max "max" + } + if { [info exists flags(-hold)] && ![info exists flags(-setup)] } { + set min_max "min" + } + + set cmd "set_false_path" + set arg_error 0 + set from [parse_from_arg keys arg_error] + set thrus [parse_thrus_arg args arg_error] + set to [parse_to_arg keys flags arg_error] + check_exception_pins $from $to + if { $arg_error } { + delete_from_thrus_to $from $thrus $to + sta_error "set_false_path command failed." + } + + check_for_key_args $cmd args + if { $args != {} } { + delete_from_thrus_to $from $thrus $to + sta_error "positional arguments not supported." + } + if { ($from == "NULL" && $thrus == "" && $to == "NULL") } { + delete_from_thrus_to $from $thrus $to + sta_error "-from, -through or -to required." + } + + if [info exists flags(-reset_path)] { + reset_path_cmd $from $thrus $to $min_max + } + + set comment [parse_comment_key keys] + + make_false_path $from $thrus $to $min_max $comment +} + +################################################################ + +define_cmd_args "set_ideal_latency" \ + {[-rise] [-fall] [-min] [-max] delay objects} + +proc set_ideal_latency { args } { + # ignored +} + +################################################################ + +# Specifies net has zero weight for placement. +define_cmd_args "set_ideal_net" { nets } + +################################################################ + +define_cmd_args "set_ideal_network" {[-no_propagation] objects} + +proc set_ideal_network { args } { + # ignored +} + +################################################################ + +define_cmd_args "set_ideal_transition" \ + {[-rise] [-fall] [-min] [-max] transition_time objects} + +proc set_ideal_transition { args } { + # ignored +} + +################################################################ + +define_cmd_args "set_input_delay" \ + {[-rise] [-fall] [-max] [-min]\ + [-clock clock] [-clock_fall]\ + [-reference_pin ref_pin]\ + [-source_latency_included] [-network_latency_included]\ + [-add_delay] delay port_pin_list} + +proc set_input_delay { args } { + set_port_delay "set_input_delay" "set_input_delay_cmd" $args \ + {"input" "bidirect"} +} + +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} + check_argc_eq2 $cmd $cmd_args + + set delay_arg [lindex $cmd_args 0] + check_float "delay" $delay_arg + set delay [time_ui_sta $delay_arg] + set pins [get_port_pins_error "pins" [lindex $cmd_args 1]] + + set clk "NULL" + if [info exists keys(-clock)] { + set clk [get_clock_warn "clock" $keys(-clock)] + } + + set ref_pin "NULL" + if [info exists keys(-reference_pin)] { + set ref_pin [get_port_pin_error "ref_pin" $keys(-reference_pin)] + if { [info exists flags(-source_latency_included)] } { + sta_warn "-source_latency_included ignored with -reference_pin." + } + if { [info exists flags(-network_latency_included)] } { + sta_warn "-network_latency_included ignored with -reference_pin." + } + } + + if [info exists flags(-clock_fall)] { + set clk_tr "fall" + } else { + set clk_tr "rise" + } + + set tr [parse_rise_fall_flags flags] + set min_max [parse_min_max_all_flags flags] + set add [info exists flags(-add_delay)] + set source_latency_included [info exists flags(-source_latency_included)] + set network_latency_included [info exists flags(-network_latency_included)] + + foreach pin $pins { + if { [$pin is_top_level_port] \ + && [lsearch $port_dirs [pin_direction $pin]] == -1 } { + sta_warn "$cmd not allowed on [pin_direction $pin] port '[get_full_name $pin]'." + } elseif { $clk != "NULL" && [lsearch [$clk sources] $pin] != -1 } { + sta_warn "$cmd relative to a clock defined on the same port/pin not allowed." + } else { + $sta_cmd $pin $tr $clk $clk_tr $ref_pin\ + $source_latency_included $network_latency_included \ + $min_max $add $delay + } + } +} + +################################################################ + +define_cmd_args "set_max_delay" \ + {[-rise] [-fall] [-ignore_clock_latency] [-reset_path] [-comment comment]\ + [-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] delay} + +proc set_max_delay { args } { + set_path_delay "set_max_delay" $args max +} + +proc set_path_delay { cmd args min_max } { + parse_key_args $cmd args \ + keys {-from -rise_from -fall_from -to -rise_to -fall_to -comment} \ + flags {-rise -fall -ignore_clock_latency -reset_path} 0 + + set arg_error 0 + set from [parse_from_arg keys arg_error] + set thrus [parse_thrus_arg args arg_error] + set to [parse_to_arg keys flags arg_error] + if { $arg_error } { + delete_from_thrus_to $from $thrus $to + sta_error "set_path_delay command failed." + } + + check_for_key_args $cmd args + if { [llength $args] == 0 } { + delete_from_thrus_to $from $thrus $to + sta_error "missing delay argument." + } elseif { [llength $args] == 1 } { + set delay $args + check_float "$cmd delay" $delay + set delay [time_ui_sta $delay] + } else { + delete_from_thrus_to $from $thrus $to + sta_error "extra positional arguments." + } + + set ignore_clk_latency [info exists flags(-ignore_clock_latency)] + + if [info exists flags(-reset_path)] { + reset_path_cmd $from $thrus $to "all" + } + + set comment [parse_comment_key keys] + + make_path_delay $from $thrus $to $min_max $ignore_clk_latency \ + $delay $comment +} + +################################################################ + +define_cmd_args "set_max_time_borrow" {limit objects} + +proc set_max_time_borrow { limit objects } { + check_positive_float "borrow_limit" $limit + set limit [time_ui_sta $limit] + parse_clk_inst_pin_arg $objects clks insts pins + foreach pin $pins { + set_latch_borrow_limit_pin $pin $limit + } + foreach inst $insts { + set_latch_borrow_limit_inst $inst $limit + } + foreach clk $clks { + set_latch_borrow_limit_clk $clk $limit + } +} + +################################################################ + +define_cmd_args "set_min_delay" \ + {[-rise] [-fall] [-ignore_clock_latency] [-reset_path] [-comment comment]\ + [-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] delay} + +proc set_min_delay { args } { + set_path_delay "set_min_delay" $args min +} + +################################################################ + +define_cmd_args "set_min_pulse_width" {[-low] [-high] value [objects]} + +proc set_min_pulse_width { args } { + parse_key_args "set_min_pulse_width" args keys {} flags {-low -high} + check_argc_eq1or2 "set_min_pulse_width" $args + + if { [info exists flags(-low)] } { + set hi_low "fall" + } elseif { [info exists flags(-high)] } { + set hi_low "rise" + } else { + set hi_low "rise_fall" + } + + set min_width [lindex $args 0] + check_positive_float "min pulse width" $min_width + set min_width [time_ui_sta $min_width] + + if { [llength $args] == 2 } { + set objects [lindex $args 1] + parse_clk_inst_pin_arg $objects clks insts pins + foreach pin $pins { + set_min_pulse_width_pin $pin $hi_low $min_width + } + foreach inst $insts { + set_min_pulse_width_inst $inst $hi_low $min_width + } + foreach clk $clks { + set_min_pulse_width_clk $clk $hi_low $min_width + } + } else { + set_min_pulse_width_global $hi_low $min_width + } +} + +################################################################ + +define_cmd_args "set_multicycle_path" \ + {[-setup] [-hold] [-rise] [-fall] [-start] [-end]\ + [-reset_path] [-comment comment]\ + [-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] path_multiplier} + +proc set_multicycle_path { args } { + parse_key_args "set_multicycle_path" args \ + keys {-from -rise_from -fall_from -to -rise_to -fall_to -comment} \ + flags {-setup -hold -rise -fall -start -end -reset_path} 0 + + set min_max "min_max" + set use_end_clk 1 + if { [info exists flags(-setup)] && ![info exists flags(-hold)] } { + # -setup + set min_max "max" + set use_end_clk 1 + } + if { [info exists flags(-hold)] && ![info exists flags(-setup)] } { + # -hold + set min_max "min" + set use_end_clk 0 + } + + set cmd "set_multicycle_path" + set arg_error 0 + set from [parse_from_arg keys arg_error] + set thrus [parse_thrus_arg args arg_error] + set to [parse_to_arg keys flags arg_error] + check_exception_pins $from $to + if { $arg_error } { + delete_from_thrus_to $from $thrus $to + sta_error "set_multicycle_path command failed." + } + + check_for_key_args $cmd args + if { [llength $args] == 0 } { + delete_from_thrus_to $from $thrus $to + sta_error "missing path multiplier argument." + } elseif { [llength $args] == 1 } { + set path_multiplier $args + check_integer "path multiplier" $path_multiplier + } else { + delete_from_thrus_to $from $thrus $to + sta_error "extra positional arguments." + } + + set start [info exists flags(-start)] + set end [info exists flags(-end)] + if { $start && $end } { + delete_from_thrus_to $from $thrus $to + sta_error "cannot use -start with -end." + } elseif { $start } { + set use_end_clk 0 + } elseif { $end } { + set use_end_clk 1 + } + + if [info exists flags(-reset_path)] { + reset_path_cmd $from $thrus $to $min_max + } + + set comment [parse_comment_key keys] + + make_multicycle_path $from $thrus $to $min_max $use_end_clk \ + $path_multiplier $comment +} + +################################################################ + +define_cmd_args "set_output_delay" \ + {[-rise] [-fall] [-max] [-min]\ + [-clock clock] [-clock_fall]\ + [-reference_pin ref_pin]\ + [-source_latency_included] [-network_latency_included]\ + [-add_delay] delay port_pin_list} + +proc set_output_delay { args } { + set_port_delay "set_output_delay" "set_output_delay_cmd" $args \ + {"output" "tristate" "bidirect"} +} + +################################################################ + +define_cmd_args "set_propagated_clock" {objects} + +proc set_propagated_clock { objects } { + parse_clk_port_pin_arg $objects clks pins + foreach clk $clks { + if { [$clk is_virtual] } { + sta_warn "virtual clock [get_name $clk] can not be propagated." + } else { + set_propagated_clock_cmd $clk + } + } + foreach pin $pins { + set_propagated_clock_pin_cmd $pin + } +} + +################################################################ +# +# Environment Commands +# +################################################################ + +define_cmd_args "set_case_analysis" \ + {0|1|zero|one|rise|rising|fall|falling pins} + +proc set_case_analysis { value pins } { + if { !($value == "0" \ + || $value == "1" \ + || $value == "zero" \ + || $value == "one" \ + || $value == "rise" \ + || $value == "rising" \ + || $value == "fall" \ + || $value == "falling") } { + sta_error "value must be 0, zero, 1, one, rise, rising, fall, or falling." + } + set pins1 [get_port_pins_error "pins" $pins] + foreach pin $pins1 { + set_case_analysis_cmd $pin $value + } +} + +################################################################ + +define_cmd_args "set_drive" {[-rise] [-fall] [-min] [-max] \ + resistance ports} + +proc set_drive { args } { + parse_key_args "set_drive" args keys {} flags {-rise -fall -min -max} + set tr [parse_rise_fall_flags flags] + set min_max [parse_min_max_all_check_flags flags] + + check_argc_eq2 "set_drive" $args + + set res [lindex $args 0] + check_positive_float "resistance" $res + set res [resistance_ui_sta $res] + set ports [get_ports_error "ports" [lindex $args 1]] + foreach port $ports { + set_drive_resistance_cmd $port $tr $min_max $res + } +} + +################################################################ + +define_cmd_args "set_driving_cell" \ + {[-lib_cell cell] [-library library]\ + [-rise] [-fall] [-min] [-max]\ + [-pin pin] [-from_pin from_pin]\ + [-input_transition_rise trans_rise] [-input_transition_fall trans_fall]\ + [-multiply_by factor] [-dont_scale] [-no_design_rule] ports} + +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} \ + flags {-rise -fall -min -max -dont_scale -no_design_rule} + + set tr [parse_rise_fall_flags flags] + set min_max [parse_min_max_all_flags flags] + + # -cell is an undocumented non-sdc alias for -lib_cell. + if { [info exists keys(-cell)] } { + set keys(-lib_cell) $keys(-cell) + } + + if { [info exists keys(-lib_cell)] } { + set cell_name $keys(-lib_cell) + if { [info exists keys(-library)] } { + set library [get_liberty_error "library" $keys(-library)] + set cell [$library find_liberty_cell $cell_name] + if { $cell == "NULL" } { + sta_error "cell '$lib_name:$cell_name' not found." + } + } else { + set library "NULL" + set cell [find_liberty_cell $cell_name] + if { $cell == "NULL" } { + sta_error "'$cell_name' not found." + } + } + } else { + sta_error "missing -lib_cell argument." + } + + set to_port "NULL" + if [info exists keys(-pin)] { + set to_port_name $keys(-pin) + set to_port [$cell find_liberty_port $to_port_name] + if { $to_port == "NULL" } { + sta_error "port '$to_port_name' not found." + } + } else { + set port_iter [$cell liberty_port_iterator] + set output_count 0 + while {[$port_iter has_next]} { + 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 "-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 + } + + set from_port "NULL" + if [info exists keys(-from_pin)] { + set from_port_name $keys(-from_pin) + set from_port [$cell find_liberty_port $from_port_name] + if { $from_port == "NULL" } { + sta_error "port '$from_port_name' not found." + } + } + + set from_slew_rise 0 + if [info exists keys(-input_transition_rise)] { + set from_slew_rise $keys(-input_transition_rise) + check_positive_float "-input_transition_rise" $from_slew_rise + set from_slew_rise [time_ui_sta $from_slew_rise] + } + set from_slew_fall 0 + if [info exists keys(-input_transition_fall)] { + set from_slew_fall $keys(-input_transition_fall) + check_positive_float "-input_transition_fall" $from_slew_fall + set from_slew_fall [time_ui_sta $from_slew_fall] + } + + if [info exists keys(-multiply_by)] { + sta_warn "-multiply_by ignored." + } + if [info exists flags(-dont_scale)] { + sta_warn "-dont_scale ignored." + } + if [info exists flags(-no_design_rule)] { + sta_warn "-no_design_rule ignored." + } + + check_argc_eq1 "set_driving_cell" $args + + set ports [get_ports_error "ports" [lindex $args 0]] + foreach port $ports { + set_drive_cell_cmd $library $cell $port $from_port \ + $from_slew_rise $from_slew_fall $to_port $tr $min_max + } +} + +################################################################ + +define_cmd_args "set_fanout_load" {fanout ports} + +proc set_fanout_load { fanout port_list } { + sta_warn "set_fanout_load not supported." +} + +################################################################ + +define_cmd_args "set_input_transition" \ + {[-rise] [-fall] [-min] [-max] transition ports} + +proc set_input_transition { args } { + parse_key_args "set_input_transition" args keys {-clock -clock_fall} \ + flags {-rise -fall -max -min} + + set tr [parse_rise_fall_flags flags] + set min_max [parse_min_max_all_flags flags] + + + check_argc_eq2 "set_input_transition" $args + + set slew [lindex $args 0] + check_positive_float "transition" $slew + set slew [time_ui_sta $slew] + set ports [get_ports_error "ports" [lindex $args 1]] + + if [info exists keys(-clock)] { + sta_warn "-clock not supported." + } + if [info exists keys(-clock_fall)] { + sta_warn "-clock_fall not supported." + } + + foreach port $ports { + set_input_slew_cmd $port $tr $min_max $slew + } +} + +################################################################ + +define_cmd_args "set_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} \ + flags {-rise -fall -min -max -subtract_pin_load -pin_load -wire_load}\ + + check_argc_eq2 "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_default keys] + set min_max [parse_min_max_all_check_flags flags] + set tr [parse_rise_fall_flags flags] + + set cap [lindex $args 0] + check_positive_float "capacitance" $cap + set cap [capacitance_ui_sta $cap] + parse_port_net_args [lindex $args 1] ports nets + + if { $ports != {} } { + # -pin_load is the default. + if { $pin_load || (!$pin_load && !$wire_load) } { + foreach port $ports { + set_port_pin_cap $port $tr $min_max $cap + } + } elseif { $wire_load } { + foreach port $ports { + set_port_wire_cap $port $subtract_pin_load $tr $min_max $cap + } + } + } + if { $nets != {} } { + if { $pin_load } { + sta_warn "-pin_load not allowed for net objects." + } + if { $wire_load } { + sta_warn "-wire_load not allowed for net objects." + } + if { $tr != "rise_fall" } { + sta_warn "-rise/-fall not allowed for net objects." + } + foreach net $nets { + set_net_wire_cap $net $subtract_pin_load $corner $min_max $cap + } + } +} + +################################################################ + +define_cmd_args "set_logic_dc" {port_list} + +proc set_logic_dc { port_list } { + set_logic_value $port_list "X" +} + +# OT supports set_logic cmds on pins. +# OC only supports them on ports. +proc set_logic_value { port_list value } { + set pins [get_port_pins_error "pins" $port_list] + foreach pin $pins { + set_logic_value_cmd $pin $value + } +} + +################################################################ + +define_cmd_args "set_logic_one" {port_list} + +proc set_logic_one { port_list } { + set_logic_value $port_list "1" +} + +################################################################ + +define_cmd_args "set_logic_zero" {port_list} + +proc set_logic_zero { port_list } { + set_logic_value $port_list "0" +} + +################################################################ + +define_cmd_args "set_max_area" {area} + +proc set_max_area { area } { + check_positive_float "area" $area + set_max_area_cmd $area +} + +################################################################ + +define_cmd_args "set_max_capacitance" {cap objects} + +proc set_max_capacitance { cap objects } { + set_capacitance_limit $cap "max" $objects +} + +proc set_capacitance_limit { cap min_max objects } { + parse_cell_port_pin_args $objects cells ports pins + check_positive_float "limit" $cap + set cap [capacitance_ui_sta $cap] + foreach cell $cells { + set_cell_capacitance_limit $cell $min_max $cap + } + foreach port $ports { + set_port_capacitance_limit $port $min_max $cap + } + foreach pin $pins { + set_pin_capacitance_limit $pin $min_max $cap + } +} + +################################################################ + +define_cmd_args "set_max_fanout" {fanout objects} + +proc set_max_fanout { fanout objects } { + set_fanout_limit $fanout "max" $objects +} + +proc set_fanout_limit { fanout min_max objects } { + check_positive_float "limit" $fanout + parse_cell_port_args $objects cells ports + foreach port $ports { + set dir [port_direction $port] + if { !($dir == "input" || $dir == "bidirect") } { + sta_error "port '[get_name $port]' is not an input." + } + set_port_fanout_limit $port $min_max $fanout + } + foreach cell $cells { + set_cell_fanout_limit $cell $min_max $fanout + } +} + +################################################################ + +define_cmd_args "set_max_transition" \ + {[-clock_path] [-data_path] [-rise] [-fall] slew objects} + +proc set_max_transition { args } { + parse_key_args "set_max_transition" args keys {} \ + flags {-clock_path -data_path -rise -fall} + check_argc_eq2 "set_max_transition" $args + + set slew [lindex $args 0] + check_positive_float "transition" $slew + set slew [time_ui_sta $slew] + + set objects [lindex $args 1] + parse_clk_cell_port_pin_args $objects clks cells ports pins + + set tr [parse_rise_fall_flags flags] + + set path_types {} + if { ![info exists flags(-clock_path)] \ + && ![info exists flags(-data_path)] } { + # Derate clk and data if neither -clock_path or -data_path. + set path_types {"clk" "data"} + } + if { [info exists flags(-clock_path)] } { + lappend path_types "clk" + } + if { [info exists flags(-data_path)] } { + lappend path_types "data" + } + + if { ($ports != {} || $pins != {} || $cells != {}) \ + && ([info exists flags(-clock_path)] \ + || [info exists flags(-data_path)] + || [info exists flags(-rise)] + || [info exists flags(-fall)]) } { + sta_warn "-data_path, -clock_path, -rise, -fall ignored for ports, pins and designs." + } + + # -clock_path/-data_path and transition only apply to clock objects. + foreach path_type $path_types { + foreach clk $clks { + set_slew_limit_clk $clk $tr $path_type "max" $slew + } + } + foreach cell $cells { + set_slew_limit_cell $cell "max" $slew + } + foreach port $ports { + set_slew_limit_port $port "max" $slew + } + foreach pin $pins { + set_slew_limit_pin $pin "max" $slew + } +} + +################################################################ + +define_cmd_args "set_port_fanout_number" \ + {[-max] [-min] fanout ports} + +proc set_port_fanout_number { args } { + 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 + + set fanout [lindex $args 0] + check_positive_integer "fanout" $fanout + set ports [get_ports_error "ports" [lindex $args 1]] + foreach port $ports { + set_port_ext_fanout_cmd $port $fanout $min_max + } +} + +################################################################ + +define_cmd_args "set_resistance" {[-min] [-max] resistance nets} + +proc set_resistance { args } { + parse_key_args "set_resistance" args keys {} flags {-max -min} + set min_max [parse_min_max_all_check_flags flags] + + check_argc_eq2 "set_resistance" $args + + set res [lindex $args 0] + check_positive_float "resistance" $res + set res [resistance_ui_sta $res] + set nets [get_nets_error "nets" [lindex $args 1]] + foreach net $nets { + set_net_resistance $net $min_max $res + } +} + +################################################################ + +define_cmd_args "set_timing_derate" \ + {-early|-late [-rise] [-fall] [-clock] [-data] \ + [-net_delay] [-cell_delay] [-cell_check] derate [objects]} + +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} + check_argc_eq1or2 "set_timing_derate" $args + + set derate [lindex $args 0] + check_float "derate" $derate + if { $derate > 2.0 } { + sta_warn "derating factor greater than 2.0." + } + + set tr [parse_rise_fall_flags flags] + set early_late [parse_early_late_flags flags] + + set path_types {} + if { ![info exists flags(-clock)] \ + && ![info exists flags(-data)] } { + # Derate clk and data if neither -clock or -data. + lappend path_types "clk" + lappend path_types "data" + } + if { [info exists flags(-clock)] } { + lappend path_types "clk" + } + if { [info exists flags(-data)] } { + lappend path_types "data" + } + + set derate_types {} + if { [info exists flags(-net_delay)] } { + lappend derate_types "net_delay" + } + if { [info exists flags(-cell_delay)] } { + lappend derate_types "cell_delay" + } + if { [info exists flags(-cell_check)] } { + lappend derate_types "cell_check" + } + + if { [llength $args] == 2 } { + set objects [lindex $args 1] + parse_libcell_inst_net_arg $objects libcells insts nets + if { $nets != {} } { + if { [info exists flags(-cell_delay)] \ + || [info exists flags(-cell_check)] } { + sta_warn "-cell_delay and -cell_check flags ignored for net objects." + } + foreach net $nets { + foreach path_type $path_types { + set_timing_derate_net_cmd $net $path_type $tr $early_late $derate + } + } + } + if { ![info exists flags(-cell_delay)] \ + && ![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 inst $insts { + set_timing_derate_inst_cmd $inst $derate_type $path_type \ + $tr $early_late $derate + } + foreach libcell $libcells { + set_timing_derate_cell_cmd $libcell $derate_type $path_type \ + $tr $early_late $derate + } + } + } + } else { + if { ![info exists flags(-net_delay)] \ + && ![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 $tr $early_late $derate + } + } + } +} + +proc parse_from_arg { keys_var arg_error_var } { + upvar 1 $keys_var keys + + if [info exists keys(-from)] { + set key "-from" + set tr "rise_fall" + } elseif [info exists keys(-rise_from)] { + set key "-rise_from" + set tr "rise" + } elseif [info exists keys(-fall_from)] { + set key "-fall_from" + set tr "fall" + } else { + return "NULL" + } + parse_clk_inst_port_pin_arg $keys($key) from_clks from_insts from_pins + if {$from_pins == {} && $from_insts == {} && $from_clks == {}} { + upvar 1 $arg_error_var arg_error + set arg_error 1 + sta_error "no valid objects specified for $key." + return "NULL" + } + return [make_exception_from $from_pins $from_clks $from_insts $tr] +} + +# "arg_error" is set to notify the caller to cleanup and post error. +proc parse_thrus_arg { args_var arg_error_var } { + upvar 1 $args_var args + + set thrus {} + set args_rtn {} + while { $args != {} } { + set arg [lindex $args 0] + set tr "" + if { $arg == "-through" } { + set tr "rise_fall" + } elseif { $arg == "-rise_through" } { + set tr "rise" + } elseif { $arg == "-fall_through" } { + set tr "fall" + } + if { $tr != "" } { + 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_error "no valid objects specified for -through." + } else { + lappend thrus [make_exception_thru $pins $nets $insts $tr] + } + } + } else { + lappend args_rtn $arg + } + set args [lrange $args 1 end] + } + set args $args_rtn + return $thrus +} + +# Parse -to|-rise_to|-fall_to keywords. +proc parse_to_arg { keys_var flags_var arg_error_var } { + upvar 1 $keys_var keys + upvar 1 $flags_var flags + upvar 1 $arg_error_var arg_error + + set end_tr [parse_rise_fall_flags flags] + return [parse_to_arg1 keys $end_tr arg_error] +} + +proc parse_to_arg1 { keys_var end_tr arg_error_var } { + upvar 1 $keys_var keys + upvar 1 $arg_error_var arg_error + + if [info exists keys(-to)] { + set key "-to" + set to_tr "rise_fall" + } elseif [info exists keys(-rise_to)] { + set key "-rise_to" + set to_tr "rise" + } elseif [info exists keys(-fall_to)] { + set key "-fall_to" + set to_tr "fall" + } else { + # -rise/-fall without -to/-rise_to/-fall_to (no objects). + if { $end_tr != "rise_fall" } { + return [make_exception_to {} {} {} "rise_fall" $end_tr] + } else { + return "NULL" + } + } + parse_clk_inst_port_pin_arg $keys($key) to_clks to_insts to_pins + if {$to_pins == {} && $to_insts == {} && $to_clks == {}} { + upvar 1 $arg_error_var arg_error + set arg_error 1 + puts "Error: no valid objects specified for $key." + return "NULL" + } + return [make_exception_to $to_pins $to_clks $to_insts $to_tr $end_tr] +} + +proc delete_from_thrus_to { from thrus to } { + if { $from != "NULL" } { + delete_exception_from $from + } + if { $thrus != {} } { + foreach thru $thrus { + delete_exception_thru $thru + } + } + if { $to != "NULL" } { + delete_exception_to $to + } +} + +proc parse_comment_key { keys_var } { + upvar 1 $keys_var keys + + set comment "" + if { [info exists keys(-comment)] } { + set comment $keys(-comment) + } + return $comment +} + +################################################################ + +define_cmd_args "set_min_capacitance" {cap objects} + +proc set_min_capacitance { cap objects } { + set_capacitance_limit $cap "min" $objects +} + +################################################################ + +define_cmd_args "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]} + +proc set_operating_conditions { args } { + parse_key_args "set_operating_conditions" args \ + keys {-analysis_type -library -min -max -min_library -max_library} flags {} + parse_op_cond_analysis_type keys + check_argc_eq0or1 set_operating_conditions $args + if { [llength $args] == 1 } { + set op_cond_name $args + parse_op_cond $op_cond_name "-library" "all" keys + } + if [info exists keys(-min)] { + parse_op_cond $keys(-min) "-min_library" "min" keys + } + if [info exists keys(-max)] { + parse_op_cond $keys(-max) "-max_library" "max" keys + } +} + +proc parse_op_cond { op_cond_name lib_key min_max key_var } { + upvar 1 $key_var keys + if [info exists keys($lib_key)] { + set liberty [get_liberty_error $lib_key $keys($lib_key)] + set op_cond [$liberty find_operating_conditions $op_cond_name] + if { $op_cond == "NULL" } { + sta_error "operating condition '$op_cond_name' not found." + } else { + set_operating_conditions_cmd $op_cond $min_max + } + } else { + set found 0 + set lib_iter [liberty_library_iterator] + while {[$lib_iter has_next]} { + 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 + } + } + $lib_iter finish + if { !$found } { + sta_error "operating condition '$op_cond_name' not found." + } + } +} + +proc parse_op_cond_analysis_type { key_var } { + upvar 1 $key_var keys + 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" } { + set_analysis_type_cmd $analysis_type + } else { + sta_error "-analysis_type must be single, bc_wc or on_chip_variation." + } + } elseif { [info exists keys(-min)] && [info exists keys(-max)] } { + set_analysis_type_cmd "bc_wc" + } +} + +################################################################ + +define_cmd_args "set_wire_load_min_block_size" {block_size} + +proc set_wire_load_min_block_size { block_size } { + sta_warn "set_wire_load_min_block_size not supported." +} + +################################################################ + +define_cmd_args "set_wire_load_mode" "top|enclosed|segmented" + +proc set_wire_load_mode { mode } { + if { $mode == "top" \ + || $mode == "enclosed" \ + || $mode == "segmented" } { + set_wire_load_mode_cmd $mode + } else { + sta_error "mode must be top, enclosed or segmented." + } +} + +################################################################ + +define_cmd_args "set_wire_load_model" \ + {-name model_name [-library lib_name] [-min] [-max] [objects]} + +proc set_wire_load_model { args } { + parse_key_args "set_wire_load_model" args keys {-name -library} \ + flags {-min -max} + + check_argc_eq0or1 "set_wire_load_model" $args + if { ![info exists keys(-name)] } { + sta_error "no wire load model specified." + } + + set model_name $keys(-name) + set min_max [parse_min_max_all_check_flags flags] + + set wireload "NULL" + if [info exists keys(-library)] { + set library [get_liberty_error "library" $keys(-library)] + set wireload [$library find_wireload $model_name] + } else { + set lib_iter [liberty_library_iterator] + while {[$lib_iter has_next]} { + set lib [$lib_iter next] + set wireload [$lib find_wireload $model_name] + if {$wireload != "NULL"} { + break; + } + } + $lib_iter finish + } + if {$wireload == "NULL"} { + sta_error "wire load model '$model_name' not found." + } + set objects $args + set_wire_load_cmd $wireload $min_max +} + +################################################################ + +define_cmd_args "set_wire_load_selection_group" \ + {[-library lib] [-min] [-max] group_name [objects]} + +proc set_wire_load_selection_group { args } { + parse_key_args "set_wire_load_selection_group" args keys {-library} \ + flags {-min -max} + + set argc [llength $args] + check_argc_eq1or2 "wire_load_selection_group" $args + + set selection_name [lindex $args 0] + set objects [lindex $args 1] + + set min_max [parse_min_max_all_check_flags flags] + + set selection "NULL" + if [info exists keys(-library)] { + set library [get_liberty_error "library" $keys(-library)] + set selection [$library find_wireload_selection $selection_name] + } else { + set lib_iter [liberty_library_iterator] + while {[$lib_iter has_next]} { + set lib [$lib_iter next] + set selection [$lib find_wireload_selection $selection_name] + if {$selection != "NULL"} { + break; + } + } + $lib_iter finish + } + if {$selection == "NULL"} { + sta_error "wire load selection group '$selection_name' not found." + } + set_wire_load_selection_group_cmd $selection $min_max +} + +################################################################ +# +# Multivoltage and Power Optimization Commands +# +################################################################ + +define_cmd_args "create_voltage_area" \ + {[-name name] [-coordinate coordinates] [-guard_band_x guard_x]\ + [-guard_band_y guard_y] cells } + +proc create_voltage_area { args } { + # ignored +} + +################################################################ + +define_cmd_args "set_level_shifter_strategy" {[-rule rule_type]} + +proc set_level_shifter_strategy { args } { + # ignored +} + +################################################################ + +define_cmd_args "set_level_shifter_threshold" {[-voltage volt]} + +proc set_level_shifter_threshold { args } { + # ignored +} + +################################################################ + +define_cmd_args "set_max_dynamic_power" {power [unit]} + +proc set_max_dynamic_power { power {unit {}} } { + # ignored +} + +################################################################ + +define_cmd_args "set_max_leakage_power" {power [unit]} + +proc set_max_leakage_power { power {unit {}} } { + # ignored +} + +################################################################ +# +# Non-SDC commands +# +################################################################ + +define_cmd_args "define_corners" { corner1 [corner2]... } + +proc define_corners { args } { + define_corners_cmd $args +} + +################################################################ + +define_cmd_args "set_pvt"\ + {insts [-min] [-max] [-process process] [-voltage voltage]\ + [-temperature temperature]} + +proc set_pvt { args } { + parse_key_args "set_pvt" args \ + keys {-process -voltage -temperature} flags {-min -max} + + set min_max [parse_min_max_all_flags flags] + check_argc_eq1 "set_pvt" $args + set insts [get_instances_error "instances" [lindex $args 0]] + + if { $min_max == "all" } { + set_pvt_min_max $insts "min" keys + set_pvt_min_max $insts "max" keys + } else { + set_pvt_min_max $insts $min_max keys + } +} + +proc set_pvt_min_max { insts min_max keys_var } { + upvar 1 $keys_var keys + set op_cond [operating_conditions $min_max] + if { $op_cond == "NULL" } { + set op_cond [default_operating_conditions] + } + if [info exists keys(-process)] { + set process $keys(-process) + check_float "-process" $process + } else { + set process [$op_cond process] + } + if [info exists keys(-voltage)] { + set voltage $keys(-voltage) + check_float "-voltage" $voltage + } else { + set voltage [$op_cond voltage] + } + if [info exists keys(-temperature)] { + set temperature $keys(-temperature) + check_float "-temperature" $temperature + } else { + set temperature [$op_cond temperature] + } + + foreach inst $insts { + set_instance_pvt $inst $min_max $process $voltage $temperature + } +} + +proc default_operating_conditions {} { + set found 0 + set lib_iter [liberty_library_iterator] + while {[$lib_iter has_next]} { + set lib [$lib_iter next] + set op_cond [$lib default_operating_conditions] + if { $op_cond != "NULL" } { + set found 1 + break + } + } + $lib_iter finish + if { !$found } { + sta_error "no default operating conditions found." + } + return $op_cond +} + +# sta namespace end. +} diff --git a/tcl/Search.tcl b/tcl/Search.tcl new file mode 100644 index 0000000..0b6b1f1 --- /dev/null +++ b/tcl/Search.tcl @@ -0,0 +1,420 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +namespace eval sta { + +################################################################ +# +# Search debugging/probing commands +# +################################################################ + +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" + set clk_iter [clock_iterator] + while {[$clk_iter has_next]} { + set clk [$clk_iter next] + report_delays_wrt_clk $vertex $what $clk "rise" + report_delays_wrt_clk $vertex $what $clk "fall" + } + $clk_iter finish + } + } +} + +proc report_delays_wrt_clk { vertex what clk clk_tr } { + global sta_report_default_digits + + set rise [$vertex $what rise $clk $clk_tr $sta_report_default_digits] + set fall [$vertex $what fall $clk $clk_tr $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] [rise_fall_short_name $clk_tr])" + } else { + set clk_str "" + } + puts "$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" + set clk_iter [clock_iterator] + while {[$clk_iter has_next]} { + set clk [$clk_iter next] + report_wrt_clk $vertex $what $clk "rise" + report_wrt_clk $vertex $what $clk "fall" + } + $clk_iter finish + } + } +} + +proc report_wrt_clk { vertex what clk clk_tr } { + global sta_report_default_digits + + set rise [$vertex $what rise $clk $clk_tr] + set fall [$vertex $what fall $clk $clk_tr] + # 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] [rise_fall_short_name $clk_tr])" + } else { + set clk_str "" + } + puts "$clk_str r $rise_fmt f $fall_fmt" + } +} + +proc rise_fall_short_name { tr } { + if { $tr eq "rise" } { + return [rise_short_name] + } elseif { $tr eq "fall" } { + return [fall_short_name] + } else { + error "unknown transition name $tr" + } +} + +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 +} + +################################################################ + +# Note that -all and -tags are intentionally "hidden". +define_cmd_args "report_path" \ + {[-min|-max]\ + [-format full|full_clock|full_clock_expanded|short|end|summary]\ + [-fields [capacitance|slew|input_pin|net]\ + [-digits digits] [-no_line_splits]\ + [> filename] [>> filename]\ + pin ^|r|rise|v|f|fall} + +proc_redirect report_path { + parse_key_args "report_path" args keys {} \ + flags {-max -min -all -tags} 0 + + if { [info exists flags(-min)] && [info exists flags(-max)] } { + sta_error "-min and -max cannot both be specified." + } elseif [info exists flags(-min)] { + set min_max "min" + } elseif [info exists flags(-max)] { + set min_max "max" + } else { + # Default to max path. + set min_max "max" + } + set report_tags [info exists flags(-tags)] + set report_all [info exists flags(-all)] + + parse_report_path_options "report_path" args "full" 1 + check_argc_eq2 "report_path" $args + + set pin_arg [lindex $args 0] + set tr [parse_rise_fall_arg [lindex $args 1]] + + set pin [get_port_pin_error "pin" $pin_arg] + if { [$pin is_hierarchical] } { + sta_error "pin '$pin_arg' is hierarchical." + } else { + foreach vertex [$pin vertices] { + if { $vertex != "NULL" } { + if { $report_all } { + set first 1 + set path_iter [$vertex path_iterator $tr $min_max] + while {[$path_iter has_next]} { + set path [$path_iter next] + if { $first } { + puts "Tag group: [$vertex tag_group_index]" + } else { + puts "" + } + if { $report_tags } { + puts "Tag: [$path tag]" + } + report_path_cmd $path + delete_path_ref $path + set first 0 + } + $path_iter finish + } else { + set worst_path [vertex_worst_arrival_path_tr $vertex $tr $min_max] + if { $worst_path != "NULL" } { + if { $report_tags } { + puts "Tag: [$worst_path tag]" + } + report_path_cmd $worst_path + delete_path_ref $worst_path + } + } + } + } + } +} + +proc parse_rise_fall_arg { arg } { + if { $arg eq "r" || $arg eq "^" || $arg eq "rise" } { + return "rise" + } elseif { $arg eq "f" || $arg eq "v" || $arg eq "fall" } { + retur "fall" + } else { + error "unknown rise/fall transition name." + } +} + +proc parse_report_path_options { cmd args_var default_format + unknown_key_is_error } { + variable path_options + variable report_path_field_width_extra + global sta_report_default_digits + + upvar 1 $args_var args + if [info exists path_options] { + unset path_options + } + parse_key_args $cmd args path_options {-format -digits -fields} \ + path_options {-no_line_splits} $unknown_key_is_error + + set format $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} + if { [lsearch $formats $format] == -1 } { + sta_error "-format $format not recognized." + } + } else { + set path_options(-format) $default_format + } + set_report_path_format $format + + set digits $sta_report_default_digits + if [info exists path_options(-digits)] { + set digits $path_options(-digits) + check_positive_integer "-digits" $digits + } + set path_options(num_fmt) "%.${digits}f" + set_report_path_digits $digits + # Numberic field width expands with digits. + set field_width [expr $digits + $report_path_field_width_extra] + foreach field {total incr capacitance slew} { + set_report_path_field_width $field $field_width + } + + if { [info exists path_options(-fields)] } { + set fields $path_options(-fields) + set report_input_pin [expr [lsearch $fields "input*"] != -1] + set report_cap [expr [lsearch $fields "cap*"] != -1] + set report_net [expr [lsearch $fields "net*"] != -1] + # transition_time - compatibility 06/24/2019 + set report_slew [expr [lsearch $fields "slew*"] != -1 \ + || [lsearch $fields "trans*"] != -1] + } else { + set report_input_pin 0 + set report_cap 0 + set report_net 0 + set report_slew 0 + } + set_report_path_fields $report_input_pin $report_net \ + $report_cap $report_slew + + set_report_path_no_split [info exists path_options(-no_line_splits)] +} + +################################################################ + +define_cmd_args "report_required" {pin} + +proc report_required { pin } { + report_delays_wrt_clks $pin "requireds_clk_delays" +} + +################################################################ + +define_cmd_args "report_slack" {pin} + +proc report_slack { pin } { + report_delays_wrt_clks $pin "slacks_clk_delays" +} + +################################################################ + +# Internal debugging command. +proc report_tag_arrivals { pin } { + set pin [get_port_pin_error "pin" $pin] + foreach vertex [$pin vertices] { + report_tag_arrivals_cmd $vertex + } +} + +################################################################ + +define_hidden_cmd_args "total_negative_slack" \ + {[-corner corner] [-min]|[-max]} + +proc total_negative_slack { args } { + parse_key_args "total_negative_slack" args \ + keys {-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] + } else { + set tns [total_negative_slack_cmd $min_max] + } + return [time_sta_ui $tns] +} + +################################################################ + +define_hidden_cmd_args "worst_negative_slack" \ + {[-corner corner] [-min]|[-max]} + +proc worst_negative_slack { args } { + set worst_slack [worst_slack1 "worst_negative_slack" $args] + if { $worst_slack < 0.0 } { + return $worst_slack + } else { + return 0.0 + } +} + +################################################################ + +define_hidden_cmd_args "worst_slack" \ + {[-corner corner] [-min]|[-max]} + +proc worst_slack { args } { + return [worst_slack1 "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} + 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] + } else { + set worst_slack [worst_slack_cmd $min_max] + } + return [time_sta_ui $worst_slack] +} + +################################################################ +# +# Helper functions +# +################################################################ + +proc parse_path_group_arg { group_names } { + set names {} + foreach name $group_names { + if { [is_path_group_name $name] } { + lappend names $name + } else { + sta_warn "unknown path group '$name'." + } + } + return $names +} + +proc report_slew_limits { corner min_max all_violators verbose nosplit } { + if { $all_violators } { + set violators [pin_slew_limit_violations $corner $min_max] + if { $violators != {} } { + puts "${min_max}_transition" + puts "" + if { $verbose } { + foreach pin $violators { + report_slew_limit_verbose $pin $corner $min_max + puts "" + } + } else { + report_slew_limit_short_header + foreach pin $violators { + report_slew_limit_short $pin $corner $min_max + } + puts "" + } + } + } else { + set pin [pin_min_slew_limit_slack $corner $min_max] + if { $pin != "NULL" } { + puts "${min_max}_transition" + puts "" + if { $verbose } { + report_slew_limit_verbose $pin $corner $min_max + puts "" + } else { + report_slew_limit_short_header + report_slew_limit_short $pin $corner $min_max + puts "" + } + } + } +} + +proc report_path_ends { path_ends } { + report_path_end_header + set prev_end "NULL" + foreach path_end $path_ends { + report_path_end2 $path_end $prev_end + set prev_end $path_end + } + report_path_end_footer +} + +# sta namespace end. +} diff --git a/tcl/Splash.tcl b/tcl/Splash.tcl new file mode 100644 index 0000000..27f639d --- /dev/null +++ b/tcl/Splash.tcl @@ -0,0 +1,642 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +namespace eval sta { + +################################################################ +# +# Startup splash message. +# +################################################################ + +define_cmd_args show_splash {} + +proc show_splash {} { + puts "OpenSTA [sta::version] [string range [sta::git_sha1] 0 9] Copyright (c) 2019, Parallax Software, Inc. +License GPLv3: GNU GPL version 3 + +This is free software, and you are free to change and redistribute it +under certain conditions; type `show_copying' for details. +This program comes with ABSOLUTELY NO WARRANTY; for details type `show_warranty'." +} + +define_cmd_args show_warranty {} + +proc show_warranty {} { + puts {15. Disclaimer of Warranty. +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. Limitation of Liability. +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +17. Interpretation of Sections 15 and 16. +If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.} +} + +define_cmd_args show_copying {} + +proc show_copying {} { + puts { GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version.} +} + +# sta namespace end +} diff --git a/tcl/Sta.tcl b/tcl/Sta.tcl new file mode 100644 index 0000000..6dae23a --- /dev/null +++ b/tcl/Sta.tcl @@ -0,0 +1,1080 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +namespace eval sta { + +proc define_sta_cmd_args { cmd arglist } { + variable sta_cmd_args + + set sta_cmd_args($cmd) $arglist +} + +# Import Sta commands to global namespace. +proc define_sta_cmds {} { + variable sta_cmd_args + variable native + + foreach cmd [array names sta_cmd_args] { + define_cmd_args $cmd $sta_cmd_args($cmd) + } + define_report_path_fields + set native 1 +} + +proc define_report_path_fields {} { + variable report_path_field_width_extra + + set_rise_fall_short_names "^" "v" + set_report_path_field_order { fanout capacitance slew \ + incr total edge case description } + set_report_path_field_properties "description" "Description" 36 1 + set width $report_path_field_width_extra + set_report_path_field_properties "total" "Time" $width 0 + set_report_path_field_properties "incr" "Delay" $width 0 + set_report_path_field_properties "capacitance" "Cap" $width 0 + set_report_path_field_properties "slew" "Slew" $width 0 + set_report_path_field_properties "fanout" "Fanout" 5 0 + set_report_path_field_properties "edge" " " 1 0 + set_report_path_field_properties "case" " " 11 0 +} + +################################################################ +# +# Search commands +# +################################################################ + +define_sta_cmd_args "check_setup" \ + { [-verbose] [-no_input_delay] [-no_output_delay]\ + [-multiple_clock] [-no_clock]\ + [-unconstrained_endpoints] [-loops] [-generated_clocks]\ + [> filename] [>> filename] } + +proc_redirect check_setup { + check_setup_cmd "check_setup" $args +} + +################################################################ + +define_sta_cmd_args "delete_clock" {[-all] clocks} + +proc delete_clock { args } { + parse_key_args "delete_clock" args keys {} flags {-all} + if { [info exists flags(-all)] } { + check_argc_eq0 "delete_clock" $args + set clks [all_clocks] + } else { + check_argc_eq1 "delete_clock" $args + set clks [get_clocks_warn "clocks" [lindex $args 0]] + } + foreach clk $clks { + remove_clock_cmd $clk + } +} + +################################################################ + +define_sta_cmd_args "delete_generated_clock" {[-all] clocks} + +proc delete_generated_clock { args } { + remove_gclk_cmd "delete_generated_clock" $args +} + +################################################################ + +define_sta_cmd_args "find_timing" {[-full_update]} + +proc find_timing { args } { + parse_key_args "find_timing" args keys {} flags {-full_update} + find_timing_cmd [info exists flags(-full_update)] +} + +################################################################ + +define_sta_cmd_args "report_clock_skew" {[-setup|-hold]\ + [-clock clocks]\ + [-corner corner_name]]\ + [-digits digits]} + +proc_redirect report_clock_skew { + global sta_report_default_digits + + parse_key_args "report_clock_skew" args \ + keys {-clock -corner -digits} flags {-setup -hold} + check_argc_eq0 "report_clock_skew" $args + + if { [info exists flags(-setup)] && [info exists flags(-hold)] } { + sta_error "report_clock_skew -setup and -hold are mutually exclusive options." + } elseif { [info exists flags(-setup)] } { + set setup_hold "setup" + } elseif { [info exists flags(-hold)] } { + set setup_hold "hold" + } else { + set setup_hold "setup" + } + + if [info exists keys(-clock)] { + set clks [get_clocks_warn "-clocks" $keys(-clock)] + } else { + set clks [all_clocks] + } + set corner [parse_corner_or_all keys] + if [info exists keys(-digits)] { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } + report_clk_skew $clks $corner $setup_hold $digits +} + +################################################################ + +define_sta_cmd_args "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]\ + [-path_delay min|min_rise|min_fall|max|max_rise|max_fall|min_max]\ + [-unconstrained] + [-corner corner_name]\ + [-group_count path_count] \ + [-endpoint_count path_count]\ + [-unique_paths_to_endpoint]\ + [-slack_max slack_max]\ + [-slack_min slack_min]\ + [-sort_by_slack]\ + [-path_group group_name]} + +proc find_timing_paths { args } { + set path_ends [find_timing_paths_cmd "find_timing_paths" args] + return $path_ends +} + +proc find_timing_paths_cmd { cmd args_var } { + global sta_report_unconstrained_paths + upvar 1 $args_var args + + parse_key_args $cmd args \ + keys {-from -rise_from -fall_from -to -rise_to -fall_to \ + -path_delay -corner -group_count -endpoint_count \ + -slack_max -slack_min -path_group} \ + flags {-unconstrained -sort_by_slack -unique_paths_to_endpoint} 0 + + set min_max "max" + set end_tr "rise_fall" + if [info exists keys(-path_delay)] { + set mm_key $keys(-path_delay) + if { $mm_key == "max_rise" } { + set min_max "max" + set end_tr "rise" + } elseif { $mm_key == "max_fall" } { + set min_max "max" + set end_tr "fall" + } elseif { $mm_key == "min_rise" } { + set min_max "min" + set end_tr "rise" + } elseif { $mm_key == "min_fall" } { + set min_max "min" + set end_tr "fall" + } elseif { $mm_key == "min" || $mm_key == "max" || $mm_key == "min_max" } { + set min_max $mm_key + } else { + sta_error "$cmd -path_delay must be min, min_rise, min_fall, max, max_rise, max_fall or min_max." + } + } + + set arg_error 0 + set from [parse_from_arg keys arg_error] + set thrus [parse_thrus_arg args arg_error] + set to [parse_to_arg1 keys $end_tr arg_error] + if { $arg_error } { + delete_from_thrus_to $from $thrus $to + sta_error "$cmd command failed." + } + + check_for_key_args $cmd args + + if { [info exists flags(-unconstrained)] } { + set unconstrained 1 + } elseif { [info exists sta_report_unconstrained_paths] } { + set unconstrained $sta_report_unconstrained_paths + } else { + set unconstrained 0 + } + + set corner [parse_corner_or_all keys] + + set endpoint_count 1 + if [info exists keys(-endpoint_count)] { + set endpoint_count $keys(-endpoint_count) + if { $endpoint_count < 1 } { + sta_error "-endpoint_count must be a positive integer." + } + } + + set group_count $endpoint_count + if [info exists keys(-group_count)] { + set group_count $keys(-group_count) + if { $group_count < 1 } { + sta_error "-group_count must be a positive integer." + } + } + + set unique_pins [info exists flags(-unique_paths_to_endpoint)] + + set slack_min "-1e+30" + if [info exist keys(-slack_min)] { + set slack_min $keys(-slack_min) + check_float "-slack_min" $slack_min + set slack_min [time_ui_sta $slack_min] + } + + set slack_max "1e+30" + if [info exist keys(-slack_max)] { + set slack_max $keys(-slack_max) + check_float "-slack_max" $slack_max + set slack_max [time_ui_sta $slack_max] + } + + set sort_by_slack [info exists flags(-sort_by_slack)] + + set groups {} + if [info exists keys(-path_group)] { + set groups [parse_path_group_arg $keys(-path_group)] + } + + if { [llength $args] != 0 } { + delete_from_thrus_to $from $thrus $to + set arg [lindex $args 0] + if { [is_keyword_arg $arg] } { + sta_error "'$arg' is not a known keyword or flag." + } else { + sta_error "positional arguments not supported." + } + } + + set path_ends [find_path_ends $from $thrus $to $unconstrained \ + $corner $min_max \ + $group_count $endpoint_count $unique_pins \ + $slack_min $slack_max \ + $sort_by_slack $groups \ + 1 1 1 1 1 1] + return $path_ends +} + +################################################################ + +define_sta_cmd_args "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_name]\ + [-group_count path_count] \ + [-endpoint_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]\ + [-fields [capacitance|slew|input_pin|net]]\ + [-digits digits]\ + [-no_line_splits]\ + [> filename] [>> filename]} + +proc_redirect report_checks { + global sta_report_unconstrained_paths + + parse_report_path_options "report_checks" args "full" 0 + set path_ends [find_timing_paths_cmd "report_checks" args] + if { $path_ends == {} } { + puts "No paths found." + } else { + report_path_ends $path_ends + } +} + +################################################################ + +define_sta_cmd_args "report_check_types" \ + {[-all_violators] [-verbose]\ + [-corner corner_name]\ + [-format slack_only|end]\ + [-max_delay] [-min_delay]\ + [-recovery] [-removal]\ + [-clock_gating_setup] [-clock_gating_hold]\ + [-max_transition] [-min_transition]\ + [-min_pulse_width] [-min_period] [-max_skew]\ + [-digits digits] [-no_line_splits]\ + [> filename] [>> filename]} + +proc_redirect report_check_types { + variable path_options + + parse_key_args "report_check_types" args keys {-corner}\ + flags {-all_violators -verbose -no_line_splits} 0 + + set all_violators [info exists flags(-all_violators)] + set verbose [info exists flags(-verbose)] + set nosplit [info exists flags(-no_line_splits)] + + if { $verbose } { + set default_format "full" + } else { + set default_format "slack_only" + } + parse_report_path_options "report_check_types" args $default_format 0 + + set min_max "min_max" + if { [operating_condition_analysis_type] == "single" } { + set min_max "max" + } + + set corner [parse_corner_or_all keys] + + if { $args == {} } { + if { $min_max == "max" || $min_max == "min_max" } { + set setup 1 + set recovery 1 + set clk_gating_setup 1 + set max_transition 1 + } else { + set setup 0 + set recovery 0 + set clk_gating_setup 0 + set max_transition 0 + } + if { $min_max == "min" || $min_max == "min_max" } { + set hold 1 + set removal 1 + set clk_gating_hold 1 + set min_transition 1 + } else { + set hold 0 + set min_delay 0 + set removal 0 + set clk_gating_hold 0 + set min_transition 0 + } + set min_pulse_width 1 + set min_period 1 + set max_skew 1 + } else { + parse_key_args "report_check_types" args keys {}\ + flags {-max_delay -min_delay -recovery -removal\ + -clock_gating_setup -clock_gating_hold\ + -max_transition -min_transition -min_pulse_width\ + -min_period -max_skew} 1 + + set setup [info exists flags(-max_delay)] + set hold [info exists flags(-min_delay)] + set recovery [info exists flags(-recovery)] + set removal [info exists flags(-removal)] + set clk_gating_setup [info exists flags(-clock_gating_setup)] + set clk_gating_hold [info exists flags(-clock_gating_hold)] + set max_transition [info exists flags(-max_transition)] + set min_transition [info exists flags(-min_transition)] + set min_pulse_width [info exists flags(-min_pulse_width)] + 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)) } { + sta_error "analysis type single is not consistent with doing both setup/max and hold/min checks." + } + } + + if { $args != {} } { + sta_error "positional arguments not supported." + } + + set corner [parse_corner_or_all keys] + + if { $setup || $hold || $recovery || $removal \ + || $clk_gating_setup || $clk_gating_hold } { + if { ($setup && $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" + } elseif { $hold || $removal || $clk_gating_hold } { + set path_min_max "min" + } + if { $all_violators } { + set group_count $sta::group_count_max + set slack_min [expr -$sta::float_inf] + set slack_max 0.0 + } else { + set group_count 1 + set slack_min [expr -$sta::float_inf] + set slack_max $sta::float_inf + } + set path_ends [find_path_ends "NULL" {} "NULL" 0 \ + $corner $path_min_max $group_count 1 0 \ + $slack_min $slack_max \ + 0 {} \ + $setup $hold \ + $recovery $removal \ + $clk_gating_setup $clk_gating_hold] + report_path_ends $path_ends + } + + if { $max_transition } { + report_slew_limits $corner "max" $all_violators $verbose $nosplit + } + if { $min_transition } { + report_slew_limits $corner "min" $all_violators $verbose $nosplit + } + if { $min_pulse_width } { + if { $all_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 + } + } + } + if { $min_period } { + if { $all_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 + } + } + } + if { $max_skew } { + if { $all_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 + } + } + } +} + +################################################################ + +define_sta_cmd_args "report_tns" { [-digits digits]} + +proc_redirect report_tns { + global sta_report_default_digits + + parse_key_args "report_tns" args keys {-digits} flags {} + if [info exists keys(-digits)] { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } + + puts "tns [format_time [total_negative_slack_cmd "max"] $digits]" +} + +################################################################ + +define_sta_cmd_args "report_wns" { [-digits digits]} + +proc_redirect report_wns { + global sta_report_default_digits + + parse_key_args "report_wns" args keys {-digits} flags {} + if [info exists keys(-digits)] { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } + + set slack [worst_slack_cmd "max"] + if { $slack > 0.0 } { + set slack 0.0 + } + puts "wns [format_time $slack $digits]" +} + +################################################################ + +define_sta_cmd_args "report_worst_slack" { [-digits digits]} + +proc_redirect report_worst_slack { + global sta_report_default_digits + + parse_key_args "report_worst_slack" args keys {-digits} flags {} + if [info exists keys(-digits)] { + set digits $keys(-digits) + check_positive_integer "-digits" $digits + } else { + set digits $sta_report_default_digits + } + + puts "worst slack [format_time [worst_slack_cmd "max"] $digits]" +} + +################################################################ + +define_sta_cmd_args "report_dcalc" \ + {[-from from_pin] [-to to_pin] [-corner corner_name] [-min] [-max] [-digits digits]} + +proc_redirect report_dcalc { + report_dcalc_cmd "report_dcalc" $args "-digits" +} + +################################################################ + +define_sta_cmd_args "report_disabled_edges" {} + +################################################################ + +define_sta_cmd_args "report_pulse_width_checks" \ + {[-verbose] [-corner corner_name] [-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 + } +} + +################################################################ + +define_sta_cmd_args "set_disable_inferred_clock_gating" { objects } + +proc set_disable_inferred_clock_gating { objects } { + set_disable_inferred_clock_gating_cmd $objects +} + +################################################################ + +define_sta_cmd_args "unset_case_analysis" {pins} + +proc unset_case_analysis { pins } { + set pins1 [get_port_pins_error "pins" $pins] + foreach pin $pins1 { + unset_case_analysis_cmd $pin + } +} + +################################################################ + +define_sta_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 +} + +################################################################ + +define_sta_cmd_args "unset_clock_latency" {[-source] [-clock clock] objects} + +proc unset_clock_latency { args } { + unset_clk_latency_cmd "unset_clock_latency" $args +} + +################################################################ + +define_sta_cmd_args "unset_clock_transition" {clocks} + +proc unset_clock_transition { args } { + check_argc_eq1 "unset_clock_transition" $args + set clks [get_clocks_warn "clocks" [lindex $args 0]] + foreach clk $clks { + unset_clock_slew_cmd $clk + } +} + +################################################################ + +define_sta_cmd_args "unset_clock_uncertainty" \ + {[-from|-rise_from|-fall_from from_clock]\ + [-to|-rise_to|-fall_to to_clock] [-rise] [-fall]\ + [-setup] [-hold] [objects]} + +proc unset_clock_uncertainty { args } { + unset_clk_uncertainty_cmd "unset_clock_uncertainty" $args +} + +################################################################ + +define_sta_cmd_args "unset_data_check" \ + {[-from from_pin] [-rise_from from_pin] [-fall_from from_pin]\ + [-to to_pin] [-rise_to to_pin] [-fall_to to_pin]\ + [-setup | -hold] [-clock clock]} + +proc unset_data_check { args } { + unset_data_checks_cmd "unset_data_check" $args +} + +################################################################ + +define_sta_cmd_args "unset_disable_inferred_clock_gating" { objects } + +proc unset_disable_inferred_clock_gating { objects } { + unset_disable_inferred_clock_gating_cmd $objects +} + +################################################################ + +define_sta_cmd_args "unset_disable_timing" \ + {[-from from_port] [-to to_port] objects} + +proc unset_disable_timing { args } { + unset_disable_cmd "unset_disable_timing" $args +} + +################################################################ + +define_sta_cmd_args "unset_generated_clock" {[-all] clocks} + +proc unset_generated_clock { args } { + unset_gclk_cmd "unset_generated_clock" $args +} + +################################################################ + +define_sta_cmd_args "unset_input_delay" \ + {[-rise] [-fall] [-max] [-min]\ + [-clock clock] [-clock_fall]\ + port_pin_list} + +proc unset_input_delay { args } { + unset_port_delay "unset_input_delay" "unset_input_delay_cmd" $args +} + +################################################################ + +define_sta_cmd_args "unset_output_delay" \ + {[-rise] [-fall] [-max] [-min]\ + [-clock clock] [-clock_fall]\ + port_pin_list} + +proc unset_output_delay { args } { + unset_port_delay "unset_output_delay" "unset_output_delay_cmd" $args +} + +################################################################ + +define_sta_cmd_args "unset_path_exceptions" \ + {[-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]} + +proc unset_path_exceptions { args } { + unset_path_exceptions_cmd "unset_path_exceptions" $args +} + +################################################################ + +define_sta_cmd_args "unset_propagated_clock" {objects} + +proc unset_propagated_clock { objects } { + parse_clk_port_pin_arg $objects clks pins + foreach clk $clks { + unset_propagated_clock_cmd $clk + } + foreach pin $pins { + unset_propagated_clock_pin_cmd $pin + } +} + +################################################################ + +define_sta_cmd_args "unset_timing_derate" {} + +proc unset_timing_derate { args } { + check_argc_eq0 "unset_timing_derate" $args + reset_timing_derate_cmd +} + +################################################################ +# +# Network editing commands +# +################################################################ + +define_sta_cmd_args "connect_pin" {net pin} +# deprecated 2.0.16 05/02/2019 +define_sta_cmd_args "connect_pins" {net pins} + +define_sta_cmd_args "delete_instance" {inst} + +define_sta_cmd_args "delete_net" {net} + +define_sta_cmd_args "disconnect_pin" {net -all|pin} +# deprecated 2.0.16 05/02/2019 +define_sta_cmd_args "disconnect_pins" {net -all|pins} + +define_sta_cmd_args "make_instance" {inst_path lib_cell} + +define_sta_cmd_args "make_net" {} + +define_sta_cmd_args "replace_cell" {instance lib_cell} + +define_sta_cmd_args "insert_buffer" {buffer_name buffer_cell net load_pins\ + buffer_out_net_name} + +################################################################ +# +# Delay calculation commands +# +################################################################ + +define_sta_cmd_args "set_assigned_delay" \ + {-cell|-net [-rise] [-fall] [-corner corner_name] [-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 } { + set_assigned_delay_cmd "set_assigned_delay" $args +} + +# compatibility +define_sta_cmd_args "read_parasitics" \ + {[-min]\ + [-max]\ + [-elmore]\ + [-path path]\ + [-increment]\ + [-pin_cap_included]\ + [-keep_capacitive_coupling]\ + [-coupling_reduction_factor factor]\ + [-reduce_to pi_elmore|pi_pole_residue2]\ + [-delete_after_reduce]\ + [-quiet]\ + [-save]\ + filename} + +################################################################a + +define_sta_cmd_args "set_assigned_check" \ + {-setup|-hold|-recovery|-removal [-rise] [-fall]\ + [-corner corner_name] [-min] [-max]\ + [-from from_pins] [-to to_pins] [-clock rise|fall]\ + [-cond sdf_cond] [-worst] check_value} + +# -worst is ignored. +proc set_assigned_check { args } { + set_assigned_check_cmd "set_assigned_check" $args +} + +################################################################a + +define_sta_cmd_args "set_assigned_transition" \ + {[-rise] [-fall] [-corner corner_name] [-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} \ + flags {-rise -fall -max -min} + + set corner [parse_corner 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 "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 + if { [llength $vertices] == 2 } { + # Bidirect driver. + set vertex [lindex $vertices 1] + set_annotated_slew $vertex $min_max $tr $slew + } + } +} + +################################################################ +# +# Utility commands +# +################################################################ + +define_sta_cmd_args "delete_from_list" {list objs} + +proc delete_from_list { list objects } { + delete_objects_from_list_cmd $list $objects +} + +################################################################ + +define_sta_cmd_args "get_fanin" \ + {-to sink_list [-flat] [-only_cells] [-startpoints_only]\ + [-levels level_count] [-pin_levels pin_count]\ + [-trace_arcs timing|enabled|all]} + +proc get_fanin { args } { + parse_key_args "get_fanin" args \ + keys {-to -levels -pin_levels -trace_arcs} \ + flags {-flat -only_cells -startpoints_only} + if { [llength $args] != 0 } { + cmd_usage_error "get_fanin" + } + if { ![info exists keys(-to)] } { + cmd_usage_error "get_fanin" + } + parse_port_pin_net_arg $keys(-to) pins nets + foreach net $nets { + lappend pins [net_driver_pins $net] + } + set flat [info exists flags(-flat)] + set only_insts [info exists flags(-only_cells)] + set startpoints_only [info exists flags(-startpoints_only)] + set inst_levels 0 + if { [info exists keys(-levels)] } { + set inst_levels $keys(-levels) + } + set pin_levels 0 + if { [info exists keys(-pin_levels)] } { + set pin_levels $keys(-pin_levels) + } + + set thru_disabled 0 + set thru_constants 0 + if { [info exists keys(-trace_arcs)] } { + set trace_arcs $keys(-trace_arcs) + if { $trace_arcs == "timing" } { + set thru_disabled 0 + set thru_constants 0 + } elseif { $trace_arcs == "enabled" } { + set thru_disabled 0 + set thru_constants 0 + } elseif { $trace_arcs == "all" } { + set thru_disabled 1 + set thru_constants 1 + } else { + cmd_usage_error "get_fanout" + } + } + if { $only_insts } { + return [find_fanin_insts $pins $flat $startpoints_only \ + $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] + } +} + +################################################################ + +define_sta_cmd_args "get_fanout" \ + {-from source_list [-flat] [-only_cells] [-endpoints_only]\ + [-levels level_count] [-pin_levels pin_count]\ + [-trace_arcs timing|enabled|all]} + +proc get_fanout { args } { + parse_key_args "get_fanout" args \ + keys {-from -levels -pin_levels -trace_arcs} \ + flags {-flat -only_cells -endpoints_only} + if { [llength $args] != 0 } { + cmd_usage_error "get_fanout" + } + parse_port_pin_net_arg $keys(-from) pins nets + foreach net $nets { + lappend pins [net_load_pins $net] + } + set flat [info exists flags(-flat)] + set only_insts [info exists flags(-only_cells)] + set endpoints_only [info exists flags(-endpoints_only)] + + set inst_levels 0 + if { [info exists keys(-levels)] } { + set inst_levels $keys(-levels) + } + + set pin_levels 0 + if { [info exists keys(-pin_levels)] } { + set pin_levels $keys(-pin_levels) + } + + set thru_disabled 0 + set thru_constants 0 + if { [info exists keys(-trace_arcs)] } { + set trace_arcs $keys(-trace_arcs) + if { $trace_arcs == "timing" } { + set thru_disabled 0 + set thru_constants 0 + } elseif { $trace_arcs == "enabled" } { + set thru_disabled 0 + set thru_constants 0 + } elseif { $trace_arcs == "all" } { + set thru_disabled 1 + set thru_constants 1 + } else { + cmd_usage_error "get_fanout" + } + } + if { $only_insts } { + return [find_fanout_insts $pins $flat $endpoints_only \ + $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] + } +} + +################################################################ + +define_sta_cmd_args "get_name" {objects} +define_sta_cmd_args "get_full_name" {objects} + +################################################################ + +define_sta_cmd_args "get_property" \ + {[-object_type cell|pin|net|port|clock|timing_arc] object property} + +proc get_property { args } { + return [get_property_cmd "get_property" "-object_type" $args] +} + +################################################################ + +define_sta_cmd_args "get_timing_edges" \ + {[-from from_pin] [-to to_pin] [-of_objects objects] [-filter expr]} + +proc get_timing_edges { args } { + return [get_timing_edges_cmd "get_timing_edges" $args] +} + +################################################################ + +define_sta_cmd_args "report_clock_properties" {[clocks]} + +proc_redirect report_clock_properties { + check_argc_eq0or1 "report_clock_properties" $args + update_generated_clks + puts "Clock Period Waveform" + puts "----------------------------------------------------" + if { [llength $args] == 0 } { + set clk_iter [clock_iterator] + while {[$clk_iter has_next]} { + set clk [$clk_iter next] + report_clock1 $clk + } + $clk_iter finish + } else { + foreach clk [get_clocks_warn "clock_name" [lindex $args 0]] { + report_clock1 $clk + } + } +} + +################################################################ + +define_sta_cmd_args "report_object_full_names" {[-verbose] objects} + +proc report_object_full_names { args } { + parse_key_args "report_object_full_names" args keys {} flags {-verbose} + + set objects [lindex $args 0] + if { [info exists flags(-verbose)] } { + puts -nonewline "{" + set first 1 + foreach obj [sort_by_full_name $objects] { + if { !$first } { + puts -nonewline ", " + } + puts -nonewline \"[get_object_type $obj]:[get_full_name $obj]\" + set first 0 + } + puts "}" + } else { + foreach obj [sort_by_full_name $objects] { + puts [get_full_name $obj] + } + } +} + +define_sta_cmd_args "report_object_names" {[-verbose] objects} + +proc report_object_names { args } { + parse_key_args "report_object_names" args keys {} flags {-verbose} + + set objects [lindex $args 0] + if { [info exists flags(-verbose)] } { + puts -nonewline "{" + set first 1 + foreach obj [sort_by_name $objects] { + if { !$first } { + puts -nonewline ", " + } + puts -nonewline \"[get_object_type $obj]:[get_name $obj]\" + set first 0 + } + puts "}" + } else { + foreach obj [sort_by_name $objects] { + puts [get_name $obj] + } + } +} + +################################################################ + +define_sta_cmd_args "with_output_to_variable" { var { cmds }} + +# with_output_to_variable variable { command args... } +proc with_output_to_variable { var_name args } { + upvar 1 $var_name var + + set body [lindex $args 0] + sta::redirect_string_begin; + catch $body ret + set var [sta::redirect_string_end] + return $ret +} + +define_sta_cmd_args "set_pocv_sigma_factor" { factor } + +# sta namespace end. +} diff --git a/tcl/StaException.i b/tcl/StaException.i new file mode 100644 index 0000000..0659e84 --- /dev/null +++ b/tcl/StaException.i @@ -0,0 +1,36 @@ +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Error.hh" + +using sta::StaException; + +%} + +%exception { + try { $function } + catch (StaException &excp) { + Tcl_SetResult(interp, const_cast(excp.what()), TCL_VOLATILE); + return TCL_ERROR; + } + catch (std::bad_alloc &) { + fprintf(stderr, "Error: out of memory.\n"); + exit(0); + } +} diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i new file mode 100644 index 0000000..079c020 --- /dev/null +++ b/tcl/StaTcl.i @@ -0,0 +1,6150 @@ +%module sta + +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +//////////////////////////////////////////////////////////////// +// +// Most of the TCL SWIG interface code is in this file. This and any +// optional interface code is %included into a final interface file +// used by the application. +// +// Define TCL methods for each network object. This works despite the +// fact that the underlying implementation does not have class methods +// corresponding to the TCL methods defined here. +// +// Note the function name changes from sta naming convention +// (lower/capitalize) to TCL naming convention (lower/underscore). +// +//////////////////////////////////////////////////////////////// + +#include +#include "Machine.hh" +#include "StaConfig.hh" // STA_VERSION +#include "Stats.hh" +#include "Report.hh" +#include "Error.hh" +#include "StringUtil.hh" +#include "PatternMatch.hh" +#include "MinMax.hh" +#include "Fuzzy.hh" +#include "PortDirection.hh" +#include "FuncExpr.hh" +#include "Units.hh" +#include "MinMax.hh" +#include "Transition.hh" +#include "TimingRole.hh" +#include "TimingArc.hh" +#include "Liberty.hh" +#include "EquivCells.hh" +#include "Network.hh" +#include "Clock.hh" +#include "PortDelay.hh" +#include "ExceptionPath.hh" +#include "Sdc.hh" +#include "Graph.hh" +#include "Parasitics.hh" +#include "Wireload.hh" +#include "DelayCalc.hh" +#include "DcalcAnalysisPt.hh" +#include "Corner.hh" +#include "Tag.hh" +#include "PathVertex.hh" +#include "PathRef.hh" +#include "PathExpanded.hh" +#include "PathEnd.hh" +#include "PathGroup.hh" +#include "CheckTiming.hh" +#include "CheckMinPulseWidths.hh" +#include "Levelize.hh" +#include "Bfs.hh" +#include "Search.hh" +#include "SearchPred.hh" +#include "PathAnalysisPt.hh" +#include "ReportPath.hh" +#include "Power.hh" +#include "Property.hh" +#include "WritePathSpice.hh" +#include "Sta.hh" + +namespace sta { + +//////////////////////////////////////////////////////////////// +// +// C++ helper functions used by the interface functions. +// These are not visible in the TCL API. +// +//////////////////////////////////////////////////////////////// + +typedef Vector LibrarySeq; +typedef CellSeq TmpCellSeq; +typedef LibertyCellSeq TmpLibertyCellSeq; +typedef PortSeq TmpPortSeq; +typedef LibertyPortSeq TmpLibertyPortSeq; +typedef PinSet TmpPinSet; +typedef PinSeq TmpPinSeq; +typedef InstanceSeq TmpInstanceSeq; +typedef InstanceSet TmpInstanceSet; +typedef MinPulseWidthCheckSeq::Iterator MinPulseWidthCheckSeqIterator; +typedef FloatSeq TmpFloatSeq; +typedef string TmpString; +typedef Set StringSet; +typedef MinMaxAll MinMaxAllNull; +typedef ClockSet TmpClockSet; +typedef StringSeq TmpStringSeq; + +class CmdErrorNetworkNotLinked : public StaException +{ +public: + virtual const char *what() const throw() + { return "Error: no network has been linked."; } +}; + +class CmdErrorNetworkNotEditable : public StaException +{ +public: + virtual const char *what() const throw() + { return "Error: network does not support edits."; } +}; + + +// Get the network for commands. +Network * +cmdNetwork() +{ + return Sta::sta()->cmdNetwork(); +} + +// Make sure the network has been read and linked. +// Throwing an error means the caller doesn't have to check the result. +Network * +cmdLinkedNetwork() +{ + Network *network = cmdNetwork(); + if (network->isLinked()) + return network; + else { + throw CmdErrorNetworkNotLinked(); + return nullptr; + } +} + +// Make sure an editable network has been read and linked. +NetworkEdit * +cmdEditNetwork() +{ + Network *network = cmdLinkedNetwork(); + if (network->isEditable()) + return dynamic_cast(network); + else { + throw CmdErrorNetworkNotEditable(); + return nullptr; + } +} + +// Get the graph for commands. +// Throw to cmd level on failure. +Graph * +cmdGraph() +{ + cmdLinkedNetwork(); + return Sta::sta()->ensureGraph(); +} + +template +Vector * +tclListSeq(Tcl_Obj *const source, + swig_type_info *swig_type, + Tcl_Interp *interp) +{ + int argc; + Tcl_Obj **argv; + + if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK + && argc > 0) { + Vector *seq = new Vector; + for (int i = 0; i < argc; i++) { + void *obj; + // Ignore returned TCL_ERROR because can't get swig_type_info. + SWIG_ConvertPtr(argv[i], &obj, swig_type, false); + seq->push_back(reinterpret_cast(obj)); + } + return seq; + } + else + return nullptr; +} + +LibertyLibrarySeq * +tclListSeqLibertyLibrary(Tcl_Obj *const source, + Tcl_Interp *interp) +{ + return tclListSeq(source, SWIGTYPE_p_LibertyLibrary, interp); +} + +LibertyCellSeq * +tclListSeqLibertyCell(Tcl_Obj *const source, + Tcl_Interp *interp) +{ + return tclListSeq(source, SWIGTYPE_p_LibertyCell, interp); +} + +template +Set * +tclListSet(Tcl_Obj *const source, + swig_type_info *swig_type, + Tcl_Interp *interp) +{ + int argc; + Tcl_Obj **argv; + + if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK + && argc > 0) { + Set *set = new Set; + for (int i = 0; i < argc; i++) { + void *obj; + // Ignore returned TCL_ERROR because can't get swig_type_info. + SWIG_ConvertPtr(argv[i], &obj, swig_type, false); + set->insert(reinterpret_cast(obj)); + } + return set; + } + else + return nullptr; +} + +StringSet * +tclListSetConstChar(Tcl_Obj *const source, + Tcl_Interp *interp) +{ + int argc; + Tcl_Obj **argv; + + 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); + } + return set; + } + else + return nullptr; +} + +StringSeq * +TclListSeqConstChar(Tcl_Obj *const source, + Tcl_Interp *interp) +{ + int argc; + Tcl_Obj **argv; + + 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); + } + return seq; + } + else + return nullptr; +} + +//////////////////////////////////////////////////////////////// + +TmpPinSet * +findStartpoints() +{ + PinSet *pins = new PinSet; + VertexPinCollector visitor(pins); + Sta::sta()->visitStartpoints(&visitor); + return pins; +} + +TmpPinSet * +findEndpoints() +{ + PinSet *pins = new PinSet; + VertexPinCollector visitor(pins); + Sta::sta()->visitEndpoints(&visitor); + return pins; +} + +void +pushPowerResultFloats(PowerResult &power, + TmpFloatSeq *floats) +{ + floats->push_back(power.internal()); + floats->push_back(power.switching()); + floats->push_back(power.leakage()); + floats->push_back(power.total()); +} + +//////////////////////////////////////////////////////////////// + +void +tclError(Tcl_Interp *interp, + const char *msg, + const char *arg) +{ + char *error = stringPrint(msg, arg); + Tcl_SetResult(interp, error, TCL_VOLATILE); + stringDelete(error); +} + +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; + } + } +} + +} // namespace + +using namespace sta; + +%} + +//////////////////////////////////////////////////////////////// +// +// SWIG type definitions. +// +//////////////////////////////////////////////////////////////// + +// 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(out) StringSeq* { + StringSeq *strs = $1; + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + StringSeq::Iterator str_iter(strs); + while (str_iter.hasNext()) { + const char *str = str_iter.next(); + Tcl_Obj *obj = Tcl_NewStringObj(str, strlen(str)); + Tcl_ListObjAppendElement(interp, list, obj); + } + Tcl_SetObjResult(interp, list); +} + +%typemap(out) TmpStringSeq* { + StringSeq *strs = $1; + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + StringSeq::Iterator str_iter(strs); + while (str_iter.hasNext()) { + const char *str = str_iter.next(); + Tcl_Obj *obj = Tcl_NewStringObj(str, strlen(str)); + Tcl_ListObjAppendElement(interp, list, obj); + } + Tcl_SetObjResult(interp, list); + delete strs; +} + +%typemap(out) Library* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) LibraryIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) LibertyLibraryIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) Cell* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) CellSeq* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + CellSeq *cells = $1; + CellSeq::Iterator cell_iter(cells); + while (cell_iter.hasNext()) { + Cell *cell = cell_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(cell, SWIGTYPE_p_Cell, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + Tcl_SetObjResult(interp, list); +} + +%typemap(out) TmpCellSeq* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + CellSeq *cells = $1; + CellSeq::Iterator cell_iter(cells); + while (cell_iter.hasNext()) { + Cell *cell = cell_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(cell, SWIGTYPE_p_Cell, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + Tcl_SetObjResult(interp, list); + delete cells; +} + +%typemap(out) LibertyCellSeq* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + LibertyCellSeq *cells = $1; + LibertyCellSeq::Iterator cell_iter(cells); + while (cell_iter.hasNext()) { + LibertyCell *cell = cell_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(cell, SWIGTYPE_p_LibertyCell, + false); + Tcl_ListObjAppendElement(interp, list, obj); + } + Tcl_SetObjResult(interp, list); +} + +%typemap(out) TmpLibertyCellSeq* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + LibertyCellSeq *cells = $1; + LibertyCellSeq::Iterator cell_iter(cells); + while (cell_iter.hasNext()) { + LibertyCell *cell = cell_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(cell, SWIGTYPE_p_LibertyCell, + false); + Tcl_ListObjAppendElement(interp, list, obj); + } + Tcl_SetObjResult(interp, list); + delete cells; +} + +%typemap(out) CellPortIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) LibertyCellPortIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) Port* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(in) PortSet* { + $1 = tclListSet($input, SWIGTYPE_p_Port, interp); +} + +%typemap(in) PortSeq* { + $1 = tclListSeq($input, SWIGTYPE_p_Port, interp); +} + +%typemap(out) TmpPortSeq* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + TmpPortSeq *ports = $1; + TmpPortSeq::Iterator port_iter(ports); + while (port_iter.hasNext()) { + Port *port = port_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(port, SWIGTYPE_p_Port, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + delete ports; + Tcl_SetObjResult(interp, list); +} + +%typemap(out) TmpLibertyPortSeq* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + TmpLibertyPortSeq *ports = $1; + TmpLibertyPortSeq::Iterator port_iter(ports); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(port, SWIGTYPE_p_LibertyPort, + false); + Tcl_ListObjAppendElement(interp, list, obj); + } + delete ports; + Tcl_SetObjResult(interp, list); +} + +%typemap(out) TmpPinSet* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + PinSet *pins = $1; + PinSet::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(pin, SWIGTYPE_p_Pin, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + delete pins; + Tcl_SetObjResult(interp, list); +} + +%typemap(out) TmpPinSeq* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + PinSeq *pins = $1; + PinSeq::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(pin, SWIGTYPE_p_Pin, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + delete pins; + Tcl_SetObjResult(interp, list); +} + +%typemap(out) PortMemberIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) LibertyCell* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) LibertyPort* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) LibertyPortMemberIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, + SWIGTYPE_p_LibertyPortMemberIterator, + false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) TimingArc* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) Wireload* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) WireloadSelection* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(in) Transition* { + int length; + const char *arg = Tcl_GetStringFromObj($input, &length); + Transition *tr = Transition::find(arg); + if (tr == nullptr) { + Tcl_SetResult(interp,const_cast("Error: transition not found."), + TCL_STATIC); + return TCL_ERROR; + } + else + $1 = tr; +} + +%typemap(out) Transition* { + Transition *tr = $1; + const char *str = ""; + if (tr) + str = tr->asString(); + Tcl_SetResult(interp, const_cast(str), TCL_STATIC); +} + +%typemap(in) TransRiseFall* { + int length; + const char *arg = Tcl_GetStringFromObj($input, &length); + TransRiseFall *tr = TransRiseFall::find(arg); + if (tr == nullptr) { + Tcl_SetResult(interp,const_cast("Error: unknown transition name."), + TCL_STATIC); + return TCL_ERROR; + } + $1 = tr; +} + +%typemap(out) TransRiseFall* { + const TransRiseFall *tr = $1; + const char *str = ""; + if (tr) + str = tr->asString(); + Tcl_SetResult(interp, const_cast(str), TCL_STATIC); +} + +%typemap(in) TransRiseFallBoth* { + int length; + const char *arg = Tcl_GetStringFromObj($input, &length); + TransRiseFallBoth *tr = TransRiseFallBoth::find(arg); + if (tr == nullptr) { + Tcl_SetResult(interp,const_cast("Error: unknown transition name."), + TCL_STATIC); + return TCL_ERROR; + } + $1 = tr; +} + +%typemap(out) TransRiseFallBoth* { + TransRiseFallBoth *tr = $1; + const char *str = ""; + if (tr) + str = tr->asString(); + Tcl_SetResult(interp, const_cast(str), TCL_STATIC); +} + +%typemap(in) TimingRole* { + int length; + const char *arg = Tcl_GetStringFromObj($input, &length); + TimingRole *role = TimingRole::find(arg); + if (role) + $1 = TimingRole::find(arg); + else { + Tcl_SetResult(interp,const_cast("Error: unknown timing role."), + TCL_STATIC); + return TCL_ERROR; + } +} + +%typemap(out) TimingRole* { + Tcl_SetResult(interp, const_cast($1->asString()), TCL_STATIC); +} + +%typemap(in) LogicValue { + int length; + const char *arg = Tcl_GetStringFromObj($input, &length); + if (stringEq(arg, "0") || stringEq(arg, "zero")) + $1 = LogicValue::zero; + else if (stringEq(arg, "1") || stringEq(arg, "one")) + $1 = LogicValue::one; + else if (stringEq(arg, "X")) + $1 = LogicValue::unknown; + else if (stringEq(arg, "rise") || stringEq(arg, "rising")) + $1 = LogicValue::rise; + else if (stringEq(arg, "fall") || stringEq(arg, "falling")) + $1 = LogicValue::fall; + else { + Tcl_SetResult(interp,const_cast("Error: unknown logic value."), + TCL_STATIC); + return TCL_ERROR; + } +} + +%typemap(in) AnalysisType { + int 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")) + $1 = AnalysisType::ocv; + else { + Tcl_SetResult(interp,const_cast("Error: unknown analysis type."), + TCL_STATIC); + + return TCL_ERROR; + } +} + +%typemap(out) Instance* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(in) InstanceSeq* { + $1 = tclListSeq($input, SWIGTYPE_p_Instance, interp); +} + +%typemap(out) TmpInstanceSeq* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + TmpInstanceSeq *insts = $1; + TmpInstanceSeq::Iterator inst_iter(insts); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(inst, SWIGTYPE_p_Instance,false); + Tcl_ListObjAppendElement(interp, list, obj); + } + delete insts; + Tcl_SetObjResult(interp, list); +} + +%typemap(out) InstanceChildIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) LeafInstanceIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) InstancePinIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) InstanceNetIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) Pin* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) PinSeq* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + PinSeq *pins = $1; + PinSeq::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(pin, SWIGTYPE_p_Pin, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + delete pins; + Tcl_SetObjResult(interp, list); +} + +%typemap(out) Net* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) NetSeq* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + NetSeq *nets = $1; + NetSeq::Iterator net_iter(nets); + while (net_iter.hasNext()) { + Net *net = net_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(net, SWIGTYPE_p_Net, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + delete nets; + Tcl_SetObjResult(interp, list); +} + +%typemap(out) NetPinIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) NetTermIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) NetConnectedPinIterator* { + Tcl_Obj *obj=SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) PinConnectedPinIterator* { + Tcl_Obj *obj=SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) Clock* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) ClockSeq* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + ClockSeq *clks = $1; + ClockSeq::Iterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(clk, SWIGTYPE_p_Clock, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + delete clks; + Tcl_SetObjResult(interp, list); +} + +%typemap(out) ClockIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) ClockPinIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1,$1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) ClockEdge* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1,$1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(in) FloatSeq* { + int argc; + Tcl_Obj **argv; + FloatSeq *floats = nullptr; + + if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK) { + if (argc) + floats = new FloatSeq; + 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 { + delete floats; + tclError(interp, "Error: %s is not a floating point number.", arg); + return TCL_ERROR; + } + } + } + $1 = floats; +} + +%typemap(out) FloatSeq* { + FloatSeq *floats = $1; + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + if (floats) { + for (unsigned i = 0; i < floats->size(); i++) { + Tcl_Obj *obj = Tcl_NewDoubleObj((*floats)[i]); + Tcl_ListObjAppendElement(interp, list, obj); + } + } + Tcl_SetObjResult(interp, list); +} + +%typemap(out) TmpFloatSeq* { + FloatSeq *floats = $1; + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + if (floats) { + for (unsigned i = 0; i < floats->size(); i++) { + Tcl_Obj *obj = Tcl_NewDoubleObj((*floats)[i]); + Tcl_ListObjAppendElement(interp, list, obj); + } + delete floats; + } + Tcl_SetObjResult(interp, list); +} + +%typemap(in) IntSeq* { + int argc; + Tcl_Obj **argv; + IntSeq *ints = nullptr; + + 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); + else { + delete ints; + tclError(interp, "Error: %s is not an integer.", arg); + return TCL_ERROR; + } + } + } + $1 = ints; +} + +%typemap(in) MinMax* { + int length; + char *arg = Tcl_GetStringFromObj($input, &length); + MinMax *min_max = MinMax::find(arg); + if (min_max) + $1 = min_max; + else { + tclError(interp, "Error: %s not min or max.", arg); + return TCL_ERROR; + } +} + +%typemap(out) MinMax* { + Tcl_SetResult(interp, const_cast($1->asString()), TCL_STATIC); +} + +%typemap(out) MinMax* { + Tcl_SetResult(interp, const_cast($1->asString()), TCL_STATIC); +} + +%typemap(in) MinMaxAll* { + int length; + char *arg = Tcl_GetStringFromObj($input, &length); + MinMaxAll *min_max = MinMaxAll::find(arg); + if (min_max) + $1 = min_max; + else { + tclError(interp, "Error: %s not min, max or min_max.", arg); + return TCL_ERROR; + } +} + +%typemap(in) MinMaxAllNull* { + int length; + char *arg = Tcl_GetStringFromObj($input, &length); + if (stringEqual(arg, "NULL")) + $1 = nullptr; + else { + MinMaxAll *min_max = MinMaxAll::find(arg); + if (min_max) + $1 = min_max; + else { + tclError(interp, "Error: %s not min, max or min_max.", arg); + return TCL_ERROR; + } + } +} + +%typemap(out) MinMaxAll* { + Tcl_SetResult(interp, const_cast($1->asString()), TCL_STATIC); +} + +// SetupHold is typedef'd to MinMax. +%typemap(in) SetupHold* { + int length; + char *arg = Tcl_GetStringFromObj($input, &length); + if (stringEqual(arg, "hold") + || stringEqual(arg, "min")) + $1 = MinMax::min(); + else if (stringEqual(arg, "setup") + || stringEqual(arg, "max")) + $1 = MinMax::max(); + else { + tclError(interp, "Error: %s not setup, hold, min or max.", arg); + return TCL_ERROR; + } +} + +// SetupHoldAll is typedef'd to MinMaxAll. +%typemap(in) SetupHoldAll* { + int length; + char *arg = Tcl_GetStringFromObj($input, &length); + if (stringEqual(arg, "hold") + || stringEqual(arg, "min")) + $1 = SetupHoldAll::min(); + else if (stringEqual(arg, "setup") + || stringEqual(arg, "max")) + $1 = SetupHoldAll::max(); + else if (stringEqual(arg, "setup_hold") + || stringEqual(arg, "min_max")) + $1 = SetupHoldAll::all(); + else { + tclError(interp, "Error: %s not setup, hold, setup_hold, min, max or min_max.", arg); + return TCL_ERROR; + } +} + +// EarlyLate is typedef'd to MinMax. +%typemap(in) EarlyLate* { + int length; + char *arg = Tcl_GetStringFromObj($input, &length); + EarlyLate *early_late = EarlyLate::find(arg); + if (early_late) + $1 = early_late; + else { + tclError(interp, "Error: %s not early/min or late/max.", arg); + return TCL_ERROR; + } +} + +// EarlyLateAll is typedef'd to MinMaxAll. +%typemap(in) EarlyLateAll* { + int length; + char *arg = Tcl_GetStringFromObj($input, &length); + EarlyLateAll *early_late = EarlyLateAll::find(arg); + if (early_late) + $1 = early_late; + else { + tclError(interp, "Error: %s not early/min, late/max or early_late/min_max.", arg); + return TCL_ERROR; + } +} + +%typemap(in) TimingDerateType { + int length; + char *arg = Tcl_GetStringFromObj($input, &length); + if (stringEq(arg, "net_delay")) + $1 = TimingDerateType::net_delay; + else if (stringEq(arg, "cell_delay")) + $1 = TimingDerateType::cell_delay; + else if (stringEq(arg, "cell_check")) + $1 = TimingDerateType::cell_check; + else { + tclError(interp, "Error: %s not clk or data.", arg); + return TCL_ERROR; + } +} + +%typemap(in) PathClkOrData { + int length; + char *arg = Tcl_GetStringFromObj($input, &length); + if (stringEq(arg, "clk")) + $1 = PathClkOrData::clk; + else if (stringEq(arg, "data")) + $1 = PathClkOrData::data; + else { + tclError(interp, "Error: %s not clk or data.", arg); + return TCL_ERROR; + } +} + +%typemap(in) ReportSortBy { + int length; + char *arg = Tcl_GetStringFromObj($input, &length); + if (stringEq(arg, "group")) + $1 = sort_by_group; + else if (stringEq(arg, "slack")) + $1 = sort_by_slack; + else { + tclError(interp, "Error: %s not group or slack.", arg); + return TCL_ERROR; + } +} + +%typemap(in) ReportPathFormat { + int length; + char *arg = Tcl_GetStringFromObj($input, &length); + if (stringEq(arg, "full")) + $1 = ReportPathFormat::full; + else if (stringEq(arg, "full_clock")) + $1 = ReportPathFormat::full_clock; + else if (stringEq(arg, "full_clock_expanded")) + $1 = ReportPathFormat::full_clock_expanded; + else if (stringEq(arg, "short")) + $1 = ReportPathFormat::shorter; + else if (stringEq(arg, "end")) + $1 = ReportPathFormat::endpoint; + else if (stringEq(arg, "summary")) + $1 = ReportPathFormat::summary; + else if (stringEq(arg, "slack_only")) + $1 = ReportPathFormat::slack_only; + else { + tclError(interp, "Error: unknown path type %s.", arg); + return TCL_ERROR; + } +} + +%typemap(in) PinSeq* { + $1 = tclListSeq($input, SWIGTYPE_p_Pin, interp); +} + +%typemap(in) PinSet* { + $1 = tclListSet($input, SWIGTYPE_p_Pin, interp); +} + +%typemap(out) PinSet* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + const PinSet *pins = $1; + if (pins) { + PinSet::ConstIterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(pin, SWIGTYPE_p_Pin, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + } + Tcl_SetObjResult(interp, list); +} + +%typemap(in) ClockSet* { + $1 = tclListSet($input, SWIGTYPE_p_Clock, interp); +} + +%typemap(out) ClockSet* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + const ClockSet *clks = $1; + if (clks) { + ClockSet::ConstIterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(clk, SWIGTYPE_p_Clock, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + } + Tcl_SetObjResult(interp, list); +} + +%typemap(out) TmpClockSet* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + const ClockSet *clks = $1; + if (clks) { + ClockSet::ConstIterator clk_iter(clks); + while (clk_iter.hasNext()) { + Clock *clk = clk_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(clk, SWIGTYPE_p_Clock, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + delete clks; + } + Tcl_SetObjResult(interp, list); +} + +%typemap(in) InstanceSet* { + $1 = tclListSet($input, SWIGTYPE_p_Instance, interp); +} + +%typemap(out) TmpInstanceSet* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + InstanceSet *insts = $1; + InstanceSet::Iterator inst_iter(insts); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(inst, SWIGTYPE_p_Instance,false); + Tcl_ListObjAppendElement(interp, list, obj); + } + delete insts; + Tcl_SetObjResult(interp, list); +} + +%typemap(in) NetSet* { + $1 = tclListSet($input, SWIGTYPE_p_Net, interp); +} + +%typemap(in) ExceptionThruSeq* { + $1 = tclListSeq($input, SWIGTYPE_p_ExceptionThru, interp); +} + +%typemap(out) Vertex* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) Vertex** { + int i = 0; + Tcl_ResetResult(interp); + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + while ($1[i]) { + Tcl_Obj *obj = SWIG_NewInstanceObj($1[i], SWIGTYPE_p_Vertex,false); + Tcl_ListObjAppendElement(interp, list, obj); + i++; + } + Tcl_SetObjResult(interp, list); +} + +%typemap(in) EdgeSeq* { + $1 = tclListSeq($input, SWIGTYPE_p_Edge, interp); +} + +%typemap(out) Edge* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) EdgeSeq* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + EdgeSeq *edges = $1; + EdgeSeq::Iterator edge_iter(edges); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(edge, SWIGTYPE_p_Edge, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + delete edges; + Tcl_SetObjResult(interp, list); +} + +%typemap(out) VertexIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) VertexInEdgeIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) VertexOutEdgeIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) TimingArcSetArcIterator* { + Tcl_Obj *obj=SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) LibertyCellTimingArcSetIterator* { + Tcl_Obj *obj=SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%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(); + 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)); + Tcl_ListObjAppendElement(interp, string_list, obj); + } + Tcl_ListObjAppendElement(interp, error_list, string_list); + } + Tcl_SetObjResult(interp, error_list); +} + +%typemap(out) PathEnd* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) PathEndSeq* { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + const PathEndSeq *path_ends = $1; + PathEndSeq::ConstIterator end_iter(path_ends); + while (end_iter.hasNext()) { + PathEnd *path_end = end_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(path_end, SWIGTYPE_p_PathEnd, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + // Delete the PathEndSeq, not the ends. + delete path_ends; + Tcl_SetObjResult(interp, list); +} + +%typemap(out) MinPulseWidthCheckSeqIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) PathRefSeq* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); + + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + PathRefSeq *paths = $1; + PathRefSeq::Iterator path_iter(paths); + while (path_iter.hasNext()) { + PathRef *path = &path_iter.next(); + PathRef *copy = new PathRef(path); + Tcl_Obj *obj = SWIG_NewInstanceObj(copy, SWIGTYPE_p_PathRef, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + 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); +} + +%typemap(out) SlowDrvrIterator* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) ExceptionFrom* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) ExceptionTo* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) ExceptionThru* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) OperatingConditions* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(in) ReduceParasiticsTo { + int length; + char *arg = Tcl_GetStringFromObj($input, &length); + if (stringEq(arg, "pi_elmore")) + $1 = ReduceParasiticsTo::pi_elmore; + else if (stringEq(arg, "pi_pole_residue2")) + $1 = ReduceParasiticsTo::pi_pole_residue2; + else if (stringEq(arg, "none")) + $1 = ReduceParasiticsTo::none; + else { + tclError(interp, "Error: %s pi_elmore, pi_pole_residue2, or none.", arg); + return TCL_ERROR; + } +} + +%typemap(out) Arrival { + Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); +} + +%typemap(out) Required { + Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); +} + +%typemap(out) Slack { + Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); +} + +%typemap(out) ArcDelay { + Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); +} + +%typemap(out) Slew { + Tcl_SetObjResult(interp,Tcl_NewDoubleObj(delayAsFloat($1))); +} + +%typemap(in) PathGroupNameSet* { + $1 = tclListSetConstChar($input, interp); +} + +%typemap(in) StringSet* { + $1 = tclListSetConstChar($input, interp); +} + +%typemap(out) Corner* { + Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false); + Tcl_SetObjResult(interp, obj); +} + +%typemap(out) PropertyValue { + PropertyValue value = $1; + switch (value.type()) { + case PropertyValue::Type::type_none: + Tcl_SetResult(interp, const_cast(""), TCL_STATIC); + break; + case PropertyValue::Type::type_string: + Tcl_SetResult(interp, const_cast(value.stringValue()), TCL_VOLATILE); + break; + case PropertyValue::Type::type_float: { + char *float_string = stringPrint("%.6e", value.floatValue()); + Tcl_SetResult(interp, float_string, TCL_VOLATILE); + stringDelete(float_string); + } + break; + case PropertyValue::Type::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: { + Tcl_Obj *obj = SWIG_NewInstanceObj(value.library(), + SWIGTYPE_p_Library, false); + Tcl_SetObjResult(interp, obj); + } + break; + case PropertyValue::Type::type_cell: { + Tcl_Obj *obj = SWIG_NewInstanceObj(value.cell(), + SWIGTYPE_p_Cell, false); + Tcl_SetObjResult(interp, obj); + } + break; + case PropertyValue::Type::type_port: { + Tcl_Obj *obj = SWIG_NewInstanceObj(value.port(), + SWIGTYPE_p_Port, false); + Tcl_SetObjResult(interp, obj); + } + break; + case PropertyValue::Type::type_liberty_library: { + Tcl_Obj *obj = SWIG_NewInstanceObj(value.libertyLibrary(), + SWIGTYPE_p_LibertyLibrary, false); + Tcl_SetObjResult(interp, obj); + } + break; + case PropertyValue::Type::type_liberty_cell: { + Tcl_Obj *obj = SWIG_NewInstanceObj(value.libertyCell(), + SWIGTYPE_p_LibertyCell, false); + Tcl_SetObjResult(interp, obj); + } + break; + case PropertyValue::Type::type_liberty_port: { + Tcl_Obj *obj = SWIG_NewInstanceObj(value.libertyPort(), + SWIGTYPE_p_LibertyPort, false); + Tcl_SetObjResult(interp, obj); + } + break; + case PropertyValue::Type::type_instance: { + Tcl_Obj *obj = SWIG_NewInstanceObj(value.instance(), + SWIGTYPE_p_Instance, false); + Tcl_SetObjResult(interp, obj); + } + break; + case PropertyValue::Type::type_pin: { + Tcl_Obj *obj = SWIG_NewInstanceObj(value.pin(), SWIGTYPE_p_Pin, false); + Tcl_SetObjResult(interp, obj); + } + break; + case PropertyValue::Type::type_pins: { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + PinSeq *pins = value.pins(); + PinSeq::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + Tcl_Obj *obj = SWIG_NewInstanceObj(pin, SWIGTYPE_p_Pin, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + Tcl_SetObjResult(interp, list); + } + break; + case PropertyValue::Type::type_net: { + Tcl_Obj *obj = SWIG_NewInstanceObj(value.net(), + SWIGTYPE_p_Net, false); + Tcl_SetObjResult(interp, obj); + } + break; + case PropertyValue::Type::type_clk: { + Tcl_Obj *obj = SWIG_NewInstanceObj(value.clock(), + SWIGTYPE_p_Clock, false); + Tcl_SetObjResult(interp, obj); + } + break; + case PropertyValue::Type::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(); + Tcl_Obj *obj = SWIG_NewInstanceObj(clk, SWIGTYPE_p_Clock, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + Tcl_SetObjResult(interp, list); + } + break; + case PropertyValue::Type::type_path_refs: { + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + PathRefSeq *paths = value.pathRefs(); + PathRefSeq::Iterator path_iter(paths); + while (path_iter.hasNext()) { + PathRef &path = path_iter.next(); + PathRef *copy = new PathRef(path); + Tcl_Obj *obj = SWIG_NewInstanceObj(copy, SWIGTYPE_p_PathRef, false); + Tcl_ListObjAppendElement(interp, list, obj); + } + Tcl_SetObjResult(interp, list); + } + break; + case PropertyValue::Type::type_pwr_activity: { + PwrActivity activity = value.pwrActivity(); + Tcl_Obj *list = Tcl_NewListObj(0, nullptr); + Tcl_Obj *obj; + const char *str; + + str = stringPrintTmp("%.5e", activity.activity()); + obj = Tcl_NewStringObj(str, strlen(str)); + Tcl_ListObjAppendElement(interp, list, obj); + + str = stringPrintTmp("%.3f", activity.duty()); + obj = Tcl_NewStringObj(str, strlen(str)); + Tcl_ListObjAppendElement(interp, list, obj); + + str = activity.originName(); + obj = Tcl_NewStringObj(str, strlen(str)); + Tcl_ListObjAppendElement(interp, list, obj); + + Tcl_SetObjResult(interp, list); + } + break; + } +} + +//////////////////////////////////////////////////////////////// +// +// Empty class definitions to make swig happy. +// Private constructor/destructor so swig doesn't emit them. +// +//////////////////////////////////////////////////////////////// + +class Library +{ +private: + Library(); + ~Library(); +}; + +class LibraryIterator +{ +private: + LibraryIterator(); + ~LibraryIterator(); +}; + +class Cell +{ +private: + Cell(); + ~Cell(); +}; + +class CellPortIterator +{ +private: + CellPortIterator(); + ~CellPortIterator(); +}; + +class LibertyCellPortIterator +{ +private: + LibertyCellPortIterator(); + ~LibertyCellPortIterator(); +}; + +class Port +{ +private: + Port(); + ~Port(); +}; + +class PortMemberIterator +{ +private: + PortMemberIterator(); + ~PortMemberIterator(); +}; + +class LibertyLibrary +{ +private: + LibertyLibrary(); + ~LibertyLibrary(); +}; + +class LibertyLibraryIterator +{ +private: + LibertyLibraryIterator(); + ~LibertyLibraryIterator(); +}; + +class LibertyCell +{ +private: + LibertyCell(); + ~LibertyCell(); +}; + +class LibertyPort +{ +private: + LibertyPort(); + ~LibertyPort(); +}; + +class LibertyPortMemberIterator +{ +private: + LibertyPortMemberIterator(); + ~LibertyPortMemberIterator(); +}; + +class TimingArcSet +{ +private: + TimingArcSet(); + ~TimingArcSet(); +}; + +class LibertyCellTimingArcSetIterator +{ +private: + LibertyCellTimingArcSetIterator(); + ~LibertyCellTimingArcSetIterator(); +}; + +class TimingArcSetArcIterator +{ +private: + TimingArcSetArcIterator(); + ~TimingArcSetArcIterator(); +}; + +class TimingArc +{ +private: + TimingArc(); + ~TimingArc(); +}; + +class Wireload +{ +private: + Wireload(); + ~Wireload(); +}; + +class WireloadSelection +{ +private: + WireloadSelection(); + ~WireloadSelection(); +}; + +class Transition +{ +private: + Transition(); + ~Transition(); +}; + +class Instance +{ +private: + Instance(); + ~Instance(); +}; + +class Pin +{ +private: + Pin(); + ~Pin(); +}; + +class Term +{ +private: + Term(); + ~Term(); +}; + +class InstanceChildIterator +{ +private: + InstanceChildIterator(); + ~InstanceChildIterator(); +}; + +class InstancePinIterator +{ +private: + InstancePinIterator(); + ~InstancePinIterator(); +}; + +class InstanceNetIterator +{ +private: + InstanceNetIterator(); + ~InstanceNetIterator(); +}; + +class LeafInstanceIterator +{ +private: + LeafInstanceIterator(); + ~LeafInstanceIterator(); +}; + +class Net +{ +private: + Net(); + ~Net(); +}; + +class NetPinIterator +{ +private: + NetPinIterator(); + ~NetPinIterator(); +}; + +class NetTermIterator +{ +private: + NetTermIterator(); + ~NetTermIterator(); +}; + +class NetConnectedPinIterator +{ +private: + NetConnectedPinIterator(); + ~NetConnectedPinIterator(); +}; + +class PinConnectedPinIterator +{ +private: + PinConnectedPinIterator(); + ~PinConnectedPinIterator(); +}; + +class Clock +{ +private: + Clock(); + ~Clock(); +}; + +class ClockIterator +{ +private: + ClockIterator(); + ~ClockIterator(); +}; + +class ClockPinIterator +{ +private: + ClockPinIterator(); + ~ClockPinIterator(); +}; + +class ClockEdge +{ +private: + ClockEdge(); + ~ClockEdge(); +}; + +class Vertex +{ +private: + Vertex(); + ~Vertex(); +}; + +class Edge +{ +private: + Edge(); + ~Edge(); +}; + +class VertexIterator +{ +private: + VertexIterator(); + ~VertexIterator(); +}; + +class VertexInEdgeIterator +{ +private: + VertexInEdgeIterator(); + ~VertexInEdgeIterator(); +}; + +class VertexOutEdgeIterator +{ +private: + VertexOutEdgeIterator(); + ~VertexOutEdgeIterator(); +}; + +class PathRef +{ +private: + PathRef(); + ~PathRef(); +}; + +class PathEnd +{ +private: + PathEnd(); + ~PathEnd(); +}; + +class MinPulseWidthCheck +{ +private: + MinPulseWidthCheck(); + ~MinPulseWidthCheck(); +}; + +class MinPulseWidthCheckSeq +{ +private: + MinPulseWidthCheckSeq(); + ~MinPulseWidthCheckSeq(); +}; + +class MinPulseWidthCheckSeqIterator +{ +private: + MinPulseWidthCheckSeqIterator(); + ~MinPulseWidthCheckSeqIterator(); +}; + +class VertexPathIterator +{ +private: + VertexPathIterator(); + ~VertexPathIterator(); +}; + +class SlowDrvrIterator +{ +private: + SlowDrvrIterator(); + ~SlowDrvrIterator(); +}; + +class ExceptionFrom +{ +private: + ExceptionFrom(); + ~ExceptionFrom(); +}; + +class ExceptionThru +{ +private: + ExceptionThru(); + ~ExceptionThru(); +}; + +class ExceptionTo +{ +private: + ExceptionTo(); + ~ExceptionTo(); +}; + +class OperatingConditions +{ +private: + OperatingConditions(); + ~OperatingConditions(); +}; + +class Corner +{ +private: + Corner(); + ~Corner(); +}; + +//////////////////////////////////////////////////////////////// +// +// C++ functions visible as TCL functions. +// +//////////////////////////////////////////////////////////////// + +%inline %{ + +float float_inf = INF; +int group_count_max = PathGroup::group_count_max; + +const char * +version() +{ + return STA_VERSION; +} + +const char * +git_sha1() +{ + return STA_GIT_SHA1; +} + +void +redirect_file_begin(const char *filename) +{ + Sta::sta()->report()->redirectFileBegin(filename); +} + +void +redirect_file_append_begin(const char *filename) +{ + Sta::sta()->report()->redirectFileAppendBegin(filename); +} + +void +redirect_file_end() +{ + Sta::sta()->report()->redirectFileEnd(); +} + +void +redirect_string_begin() +{ + Sta::sta()->report()->redirectStringBegin(); +} + +const char * +redirect_string_end() +{ + return Sta::sta()->report()->redirectStringEnd(); +} + +void +log_begin(const char *filename) +{ + Sta::sta()->report()->logBegin(filename); +} + +void +log_end() +{ + Sta::sta()->report()->logEnd(); +} + +void +set_debug(const char *what, + int level) +{ + Sta::sta()->setDebugLevel(what, level); +} + +bool +is_object(const char *obj) +{ + // _hexaddress_p_type + const char *s = obj; + char ch = *s++; + if (ch != '_') + return false; + while (*s && isxdigit(*s)) + s++; + if ((s - obj - 1) == sizeof(void*) * 2 + && *s && *s++ == '_' + && *s && *s++ == 'p' + && *s && *s++ == '_') { + while (*s && *s != ' ') + s++; + return *s == '\0'; + } + else + return false; +} + +// Assumes is_object is true. +const char * +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; +} + +void +set_rise_fall_short_names(const char *rise_short_name, + const char *fall_short_name) +{ + TransRiseFall::rise()->setShortName(rise_short_name); + TransRiseFall::fall()->setShortName(fall_short_name); + + TransRiseFallBoth::rise()->setShortName(rise_short_name); + TransRiseFallBoth::fall()->setShortName(fall_short_name); + + Transition::rise()->setName(rise_short_name); + Transition::fall()->setName(fall_short_name); +} + +const char * +rise_short_name() +{ + return TransRiseFall::rise()->shortName(); +} + +const char * +fall_short_name() +{ + return TransRiseFall::fall()->shortName(); +} + +bool +pin_is_constrained(Pin *pin) +{ + return Sta::sta()->sdc()->isConstrained(pin); +} + +bool +instance_is_constrained(Instance *inst) +{ + return Sta::sta()->sdc()->isConstrained(inst); +} + +bool +net_is_constrained(Net *net) +{ + return Sta::sta()->sdc()->isConstrained(net); +} + +bool +clk_thru_tristate_enabled() +{ + return Sta::sta()->clkThruTristateEnabled(); +} + +void +set_clk_thru_tristate_enabled(bool enabled) +{ + Sta::sta()->setClkThruTristateEnabled(enabled); +} + +bool +network_is_linked() +{ + return Sta::sta()->cmdNetwork()->isLinked(); +} + +void +set_path_divider(char divider) +{ + cmdNetwork()->setPathDivider(divider); +} + +void +set_current_instance(Instance *inst) +{ + Sta::sta()->setCurrentInstance(inst); +} + +bool +read_liberty_cmd(char *filename, + Corner *corner, + const MinMaxAll *min_max, + bool infer_latches) +{ + LibertyLibrary *lib = Sta::sta()->readLiberty(filename, corner, min_max, + infer_latches); + return (lib != nullptr); +} + +bool +set_min_library_cmd(char *min_filename, + char *max_filename) +{ + return Sta::sta()->setMinLibrary(min_filename, max_filename); +} + +Library * +find_library(const char *name) +{ + return cmdNetwork()->findLibrary(name); +} + +LibraryIterator * +library_iterator() +{ + return cmdNetwork()->libraryIterator(); +} + +LibertyLibrary * +find_liberty(const char *name) +{ + return cmdNetwork()->findLiberty(name); +} + +LibertyLibraryIterator * +liberty_library_iterator() +{ + return cmdNetwork()->libertyLibraryIterator(); +} + +LibertyCell * +find_liberty_cell(const char *name) +{ + return cmdNetwork()->findLibertyCell(name); +} + +TmpCellSeq * +find_cells_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Network *network = cmdNetwork(); + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + TmpCellSeq *cells = new TmpCellSeq; + LibraryIterator *lib_iter = network->libraryIterator(); + while (lib_iter->hasNext()) { + Library *lib = lib_iter->next(); + network->findCellsMatching(lib, &matcher, cells); + } + delete lib_iter; + return cells; +} + +LibertyCellSeq * +find_library_buffers(LibertyLibrary *library) +{ + return library->buffers(); +} + +void +make_equiv_cells(LibertyLibrary *lib) +{ + LibertyLibrarySeq libs; + libs.push_back(lib); + Sta::sta()->makeEquivCells(&libs, nullptr); +} + +LibertyCellSeq * +find_equiv_cells(LibertyCell *cell) +{ + return Sta::sta()->equivCells(cell); +} + +bool +equiv_cells(LibertyCell *cell1, + LibertyCell *cell2) +{ + return sta::equivCells(cell1, cell2); +} + +bool +equiv_cell_ports(LibertyCell *cell1, + LibertyCell *cell2) +{ + return equivCellPorts(cell1, cell2); +} + +bool +equiv_cell_timing_arcs(LibertyCell *cell1, + LibertyCell *cell2) +{ + return equivCellTimingArcSets(cell1, cell2); +} + +void +set_cmd_namespace_cmd(const char *namespc) +{ + if (stringEq(namespc, "sdc")) + Sta::sta()->setCmdNamespace(CmdNamespace::sdc); + else if (stringEq(namespc, "sta")) + Sta::sta()->setCmdNamespace(CmdNamespace::sta); + else + internalError("unknown namespace"); +} + +bool +link_design_cmd(const char *top_cell_name) +{ + return Sta::sta()->linkDesign(top_cell_name); +} + +bool +link_make_black_boxes() +{ + return Sta::sta()->linkMakeBlackBoxes(); +} + +void +set_link_make_black_boxes(bool make) +{ + Sta::sta()->setLinkMakeBlackBoxes(make); +} + +Instance * +top_instance() +{ + return cmdLinkedNetwork()->topInstance(); +} + +const char * +liberty_port_direction(const LibertyPort *port) +{ + return port->direction()->name(); +} + +const char * +port_direction(const Port *port) +{ + return cmdLinkedNetwork()->direction(port)->name(); +} + +const char * +pin_direction(const Pin *pin) +{ + return cmdLinkedNetwork()->direction(pin)->name(); +} + +TmpPortSeq * +find_ports_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Network *network = cmdLinkedNetwork(); + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + Cell *top_cell = network->cell(network->topInstance()); + PortSeq ports1; + network->findPortsMatching(top_cell, &matcher, &ports1); + // Expand bus/bundle ports. + TmpPortSeq *ports = new TmpPortSeq; + PortSeq::Iterator port_iter(ports1); + while (port_iter.hasNext()) { + Port *port = port_iter.next(); + if (network->isBus(port) + || network->isBundle(port)) { + PortMemberIterator *member_iter = network->memberIterator(port); + while (member_iter->hasNext()) { + Port *member = member_iter->next(); + ports->push_back(member); + } + delete member_iter; + } + else + ports->push_back(port); + } + return ports; +} + +TmpPinSeq * +find_port_pins_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Network *network = cmdLinkedNetwork(); + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + PortSeq ports; + Instance *top_inst = network->topInstance(); + Cell *top_cell = network->cell(top_inst); + network->findPortsMatching(top_cell, &matcher, &ports); + TmpPinSeq *pins = new TmpPinSeq; + PortSeq::Iterator port_iter(ports); + while (port_iter.hasNext()) { + Port *port = port_iter.next(); + if (network->isBus(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); + } + delete member_iter; + } + else { + Pin *pin = network->findPin(top_inst, port); + if (pin) + pins->push_back(pin); + } + } + return pins; +} + +Pin * +find_pin(const char *path_name) +{ + return cmdLinkedNetwork()->findPin(path_name); +} + +TmpPinSeq * +find_pins_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Sta *sta = Sta::sta(); + Network *network = cmdLinkedNetwork(); + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + Instance *current_instance = sta->currentInstance(); + TmpPinSeq *pins = new TmpPinSeq; + network->findPinsMatching(current_instance, &matcher, pins); + return pins; +} + +TmpPinSeq * +find_pins_hier_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Sta *sta = Sta::sta(); + Network *network = cmdLinkedNetwork(); + Instance *current_instance = sta->currentInstance(); + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + TmpPinSeq *pins = new TmpPinSeq; + network->findPinsHierMatching(current_instance, &matcher, pins); + return pins; +} + +Instance * +find_instance(char *path_name) +{ + return cmdLinkedNetwork()->findInstance(path_name); +} + +TmpInstanceSeq * +network_leaf_instances() +{ + InstanceSeq *insts = new InstanceSeq; + LeafInstanceIterator *iter = cmdLinkedNetwork()->leafInstanceIterator(); + while (iter->hasNext()) { + Instance *inst = iter->next(); + insts->push_back(inst); + } + delete iter; + return insts; +} + +TmpInstanceSeq * +find_instances_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Sta *sta = Sta::sta(); + Instance *current_instance = sta->currentInstance(); + PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp()); + TmpInstanceSeq *insts = new InstanceSeq; + cmdLinkedNetwork()->findInstancesMatching(current_instance, &matcher, insts); + return insts; +} + +TmpInstanceSeq * +find_instances_hier_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Sta *sta = Sta::sta(); + Network *network = cmdLinkedNetwork(); + Instance *current_instance = sta->currentInstance(); + PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp()); + TmpInstanceSeq *insts = new InstanceSeq; + network->findInstancesHierMatching(current_instance, &matcher, insts); + return insts; +} + +TmpInstanceSet * +find_register_instances(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + cmdLinkedNetwork(); + InstanceSet *insts = Sta::sta()->findRegisterInstances(clks, clk_tr, + edge_triggered, + latches); + delete clks; + return insts; +} + +TmpPinSet * +find_register_data_pins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + cmdLinkedNetwork(); + PinSet *pins = Sta::sta()->findRegisterDataPins(clks, clk_tr, + edge_triggered, latches); + delete clks; + return pins; +} + +TmpPinSet * +find_register_clk_pins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + cmdLinkedNetwork(); + PinSet *pins = Sta::sta()->findRegisterClkPins(clks, clk_tr, + edge_triggered, latches); + delete clks; + return pins; +} + +TmpPinSet * +find_register_async_pins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + cmdLinkedNetwork(); + PinSet *pins = Sta::sta()->findRegisterAsyncPins(clks, clk_tr, + edge_triggered, latches); + delete clks; + return pins; +} + +TmpPinSet * +find_register_output_pins(ClockSet *clks, + const TransRiseFallBoth *clk_tr, + bool edge_triggered, + bool latches) +{ + cmdLinkedNetwork(); + PinSet *pins = Sta::sta()->findRegisterOutputPins(clks, clk_tr, + edge_triggered, latches); + delete clks; + return pins; +} + +Net * +find_net(char *path_name) +{ + return cmdLinkedNetwork()->findNet(path_name); +} + +NetSeq * +find_nets_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Network *network = cmdLinkedNetwork(); + Instance *current_instance = Sta::sta()->currentInstance(); + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + NetSeq *nets = new NetSeq; + network->findNetsMatching(current_instance, &matcher, nets); + return nets; +} + +NetSeq * +find_nets_hier_matching(const char *pattern, + bool regexp, + bool nocase) +{ + Network *network = cmdLinkedNetwork(); + Instance *current_instance = Sta::sta()->currentInstance(); + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + NetSeq *nets = new NetSeq; + network->findNetsHierMatching(current_instance, &matcher, nets); + return nets; +} + +TmpPortSeq * +filter_ports(const char *property, + const char *op, + const char *pattern, + PortSeq *ports) +{ + Sta *sta = Sta::sta(); + TmpPortSeq *filtered_ports = new TmpPortSeq; + PortSeq::Iterator port_iter(ports); + bool exact_match = stringEq(op, "=="); + while (port_iter.hasNext()) { + Port *port = port_iter.next(); + PropertyValue value(getProperty(port, property, sta)); + const char *prop = value.stringValue(); + if (prop && + ((exact_match && stringEq(prop, pattern)) + || (!exact_match && patternMatch(pattern, prop)))) + filtered_ports->push_back(port); + } + delete ports; + return filtered_ports; +} + +TmpInstanceSeq * +filter_insts(const char *property, + const char *op, + const char *pattern, + InstanceSeq *insts) +{ + Sta *sta = Sta::sta(); + cmdLinkedNetwork(); + TmpInstanceSeq *filtered_insts = new TmpInstanceSeq; + TmpInstanceSeq::Iterator inst_iter(insts); + bool exact_match = stringEq(op, "=="); + while (inst_iter.hasNext()) { + Instance *inst = inst_iter.next(); + PropertyValue value(getProperty(inst, property, sta)); + const char *prop = value.stringValue(); + if (prop && + ((exact_match && stringEq(prop, pattern)) + || (!exact_match && patternMatch(pattern, prop)))) + filtered_insts->push_back(inst); + } + delete insts; + return filtered_insts; +} + +PinSeq * +filter_pins(const char *property, + const char *op, + const char *pattern, + PinSeq *pins) +{ + Sta *sta = Sta::sta(); + PinSeq *filtered_pins = new PinSeq; + PinSeq::Iterator pin_iter(pins); + bool exact_match = stringEq(op, "=="); + while (pin_iter.hasNext()) { + Pin *pin = pin_iter.next(); + PropertyValue value(getProperty(pin, property, sta)); + const char *prop = value.stringValue(); + if (prop && + ((exact_match && stringEq(prop, pattern)) + || (!exact_match && patternMatch(pattern, prop)))) + filtered_pins->push_back(pin); + } + delete pins; + return filtered_pins; +} + +PropertyValue +pin_property(const Pin *pin, + const char *property) +{ + cmdLinkedNetwork(); + return getProperty(pin, property, Sta::sta()); +} + +PropertyValue +instance_property(const Instance *inst, + const char *property) +{ + cmdLinkedNetwork(); + return getProperty(inst, property, Sta::sta()); +} + +PropertyValue +net_property(const Net *net, + const char *property) +{ + cmdLinkedNetwork(); + return getProperty(net, property, Sta::sta()); +} + +PropertyValue +port_property(const Port *port, + const char *property) +{ + return getProperty(port, property, Sta::sta()); +} + + +PropertyValue +liberty_cell_property(const LibertyCell *cell, + const char *property) +{ + return getProperty(cell, property, Sta::sta()); +} + +PropertyValue +cell_property(const Cell *cell, + const char *property) +{ + return getProperty(cell, property, Sta::sta()); +} + +PropertyValue +liberty_port_property(const LibertyPort *port, + const char *property) +{ + return getProperty(port, property, Sta::sta()); +} + +PropertyValue +library_property(const Library *lib, + const char *property) +{ + return getProperty(lib, property, Sta::sta()); +} + +PropertyValue +liberty_library_property(const LibertyLibrary *lib, + const char *property) +{ + return getProperty(lib, property, Sta::sta()); +} + +PropertyValue +edge_property(Edge *edge, + const char *property) +{ + cmdGraph(); + return getProperty(edge, property, Sta::sta()); +} + +PropertyValue +clock_property(Clock *clk, + const char *property) +{ + cmdLinkedNetwork(); + return getProperty(clk, property, Sta::sta()); +} + +PropertyValue +path_end_property(PathEnd *end, + const char *property) +{ + cmdLinkedNetwork(); + return getProperty(end, property, Sta::sta()); +} + +PropertyValue +path_ref_property(PathRef *path, + const char *property) +{ + cmdLinkedNetwork(); + return getProperty(path, property, Sta::sta()); +} + +PropertyValue +timing_arc_set_property(TimingArcSet *arc_set, + const char *property) +{ + cmdLinkedNetwork(); + return getProperty(arc_set, property, Sta::sta()); +} + +LeafInstanceIterator * +leaf_instance_iterator() +{ + return cmdLinkedNetwork()->leafInstanceIterator(); +} + +//////////////////////////////////////////////////////////////// + +void +define_corners_cmd(StringSet *corner_names) +{ + Sta *sta = Sta::sta(); + sta->makeCorners(corner_names); + delete corner_names; +} + +Corner * +cmd_corner() +{ + return Sta::sta()->cmdCorner(); +} + +void +set_cmd_corner(Corner *corner) +{ + Sta::sta()->setCmdCorner(corner); +} + +Corner * +find_corner(const char *corner_name) +{ + return Sta::sta()->findCorner(corner_name); +} + +bool +multi_corner() +{ + return Sta::sta()->multiCorner(); +} + +//////////////////////////////////////////////////////////////// + +void +set_analysis_type_cmd(const char *analysis_type) +{ + AnalysisType type; + if (stringEq(analysis_type, "single")) + type = AnalysisType::single; + else if (stringEq(analysis_type, "bc_wc")) + type = AnalysisType::bc_wc; + else if (stringEq(analysis_type, "on_chip_variation")) + type = AnalysisType::ocv; + else { + internalError("unknown analysis type"); + type = AnalysisType::single; + } + Sta::sta()->setAnalysisType(type); +} + +OperatingConditions * +operating_conditions(const MinMax *min_max) +{ + return Sta::sta()->operatingConditions(min_max); +} + +void +set_operating_conditions_cmd(OperatingConditions *op_cond, + const MinMaxAll *min_max) +{ + Sta::sta()->setOperatingConditions(op_cond, min_max); +} + +EdgeSeq * +filter_timing_arcs(const char *property, + const char *op, + const char *pattern, + EdgeSeq *edges) +{ + Sta *sta = Sta::sta(); + EdgeSeq *filtered_edges = new EdgeSeq; + EdgeSeq::Iterator edge_iter(edges); + bool exact_match = stringEq(op, "=="); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + PropertyValue value(getProperty(edge, property, sta)); + const char *prop = value.stringValue(); + if (prop && + ((exact_match && stringEq(prop, pattern)) + || (!exact_match && patternMatch(pattern, prop)))) + filtered_edges->push_back(edge); + } + delete edges; + return filtered_edges; +} + +const char * +operating_condition_analysis_type() +{ + switch (Sta::sta()->sdc()->analysisType()){ + case AnalysisType::single: + return "single"; + case AnalysisType::bc_wc: + return "bc_wc"; + case AnalysisType::ocv: + return "on_chip_variation"; + } + // Prevent warnings from lame compilers. + return "?"; +} + +void +set_instance_pvt(Instance *inst, + const MinMaxAll *min_max, + float process, + float voltage, + float temperature) +{ + cmdLinkedNetwork(); + Pvt *pvt = new Pvt(process, voltage, temperature); + Sta::sta()->setPvt(inst, min_max, pvt); +} + +float +port_ext_pin_cap(Port *port, + const TransRiseFall *tr, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + return Sta::sta()->portExtPinCap(port, tr, min_max); +} + +void +set_port_pin_cap(Port *port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float cap) +{ + Sta::sta()->setPortExtPinCap(port, tr, min_max, cap); +} + +float +port_ext_wire_cap(Port *port, + const TransRiseFall *tr, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + return Sta::sta()->portExtWireCap(port, tr, min_max); +} + +void +set_port_wire_cap(Port *port, + bool subtract_pin_cap, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float cap) +{ + Sta::sta()->setPortExtWireCap(port, subtract_pin_cap, tr, min_max, cap); +} + +int +port_ext_fanout(Port *port, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + return Sta::sta()->portExtFanout(port, min_max); +} + +void +set_port_ext_fanout_cmd(Port *port, + int fanout, + const MinMaxAll *min_max) +{ + Sta::sta()->setPortExtFanout(port, fanout, min_max); +} + +void +set_net_wire_cap(Net *net, + bool subtract_pin_cap, + Corner *corner, + const MinMaxAll *min_max, + float cap) +{ + Sta::sta()->setNetWireCap(net, subtract_pin_cap, corner, min_max, cap); +} + +void +set_wire_load_mode_cmd(const char *mode_name) +{ + WireloadMode mode = stringWireloadMode(mode_name); + if (mode == WireloadMode::unknown) + internalError("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); +} + +void +set_wire_load_cmd(Wireload *wireload, + const MinMaxAll *min_max) +{ + Sta::sta()->setWireload(wireload, min_max); +} + +void +set_wire_load_selection_group_cmd(WireloadSelection *selection, + const MinMaxAll *min_max) +{ + Sta::sta()->setWireloadSelection(selection, min_max); +} + +void +make_clock(const char *name, + PinSet *pins, + bool add_to_pins, + float period, + FloatSeq *waveform, + char *comment) +{ + cmdLinkedNetwork(); + Sta::sta()->makeClock(name, pins, add_to_pins, period, waveform, comment); +} + +void +make_generated_clock(const char *name, + PinSet *pins, + bool add_to_pins, + Pin *src_pin, + Clock *master_clk, + Pin *pll_out, + Pin *pll_fdbk, + int divide_by, + int multiply_by, + float duty_cycle, + bool invert, + bool combinational, + IntSeq *edges, + FloatSeq *edge_shifts, + char *comment) +{ + cmdLinkedNetwork(); + Sta::sta()->makeGeneratedClock(name, pins, add_to_pins, + src_pin, master_clk, pll_out, pll_fdbk, + divide_by, multiply_by, duty_cycle, invert, + combinational, edges, edge_shifts, + comment); +} + +void +remove_clock_cmd(Clock *clk) +{ + cmdLinkedNetwork(); + Sta::sta()->removeClock(clk); +} + +void +set_propagated_clock_cmd(Clock *clk) +{ + cmdLinkedNetwork(); + Sta::sta()->setPropagatedClock(clk); +} + +void +set_propagated_clock_pin_cmd(Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->setPropagatedClock(pin); +} + +void +unset_propagated_clock_cmd(Clock *clk) +{ + cmdLinkedNetwork(); + Sta::sta()->removePropagatedClock(clk); +} + +void +unset_propagated_clock_pin_cmd(Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->removePropagatedClock(pin); +} + +void +set_clock_slew_cmd(Clock *clk, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float slew) +{ + cmdLinkedNetwork(); + Sta::sta()->setClockSlew(clk, tr, min_max, slew); +} + +void +unset_clock_slew_cmd(Clock *clk) +{ + cmdLinkedNetwork(); + Sta::sta()->removeClockSlew(clk); +} + +void +set_clock_latency_cmd(Clock *clk, + Pin *pin, + const TransRiseFallBoth *tr, + MinMaxAll *min_max, float delay) +{ + cmdLinkedNetwork(); + Sta::sta()->setClockLatency(clk, pin, tr, min_max, delay); +} + +void +set_clock_insertion_cmd(Clock *clk, + Pin *pin, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + const EarlyLateAll *early_late, + float delay) +{ + cmdLinkedNetwork(); + Sta::sta()->setClockInsertion(clk, pin, tr, min_max, early_late, delay); +} + +void +unset_clock_latency_cmd(Clock *clk, + Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->removeClockLatency(clk, pin); +} + +void +unset_clock_insertion_cmd(Clock *clk, + Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->removeClockInsertion(clk, pin); +} + +void +set_clock_uncertainty_clk(Clock *clk, + const SetupHoldAll *setup_hold, + float uncertainty) +{ + cmdLinkedNetwork(); + Sta::sta()->setClockUncertainty(clk, setup_hold, uncertainty); +} + +void +unset_clock_uncertainty_clk(Clock *clk, + const SetupHoldAll *setup_hold) +{ + cmdLinkedNetwork(); + Sta::sta()->removeClockUncertainty(clk, setup_hold); +} + +void +set_clock_uncertainty_pin(Pin *pin, + const MinMaxAll *min_max, + float uncertainty) +{ + cmdLinkedNetwork(); + Sta::sta()->setClockUncertainty(pin, min_max, uncertainty); +} + +void +unset_clock_uncertainty_pin(Pin *pin, + const MinMaxAll *min_max) +{ + cmdLinkedNetwork(); + Sta::sta()->removeClockUncertainty(pin, min_max); +} + +void +set_inter_clock_uncertainty(Clock *from_clk, + const TransRiseFallBoth *from_tr, + Clock *to_clk, + const TransRiseFallBoth *to_tr, + const MinMaxAll *min_max, + float uncertainty) +{ + cmdLinkedNetwork(); + Sta::sta()->setClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max, + uncertainty); +} + +void +unset_inter_clock_uncertainty(Clock *from_clk, + const TransRiseFallBoth *from_tr, + Clock *to_clk, + const TransRiseFallBoth *to_tr, + const MinMaxAll *min_max) +{ + cmdLinkedNetwork(); + Sta::sta()->removeClockUncertainty(from_clk, from_tr, to_clk, to_tr, min_max); +} + +void +set_clock_gating_check_cmd(const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin) +{ + Sta::sta()->setClockGatingCheck(tr, setup_hold, margin); +} + +void +set_clock_gating_check_clk_cmd(Clock *clk, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin) +{ + Sta::sta()->setClockGatingCheck(clk, tr, setup_hold, margin); +} + +void +set_clock_gating_check_pin_cmd(Pin *pin, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) +{ + Sta::sta()->setClockGatingCheck(pin, tr, setup_hold, margin, active_value); +} + +void +set_clock_gating_check_instance_cmd(Instance *inst, + const TransRiseFallBoth *tr, + const SetupHold *setup_hold, + float margin, + LogicValue active_value) +{ + Sta::sta()->setClockGatingCheck(inst, tr, setup_hold, margin, active_value); +} + +void +set_data_check_cmd(Pin *from, + const TransRiseFallBoth *from_tr, + Pin *to, + const TransRiseFallBoth *to_tr, + Clock *clk, + const SetupHoldAll *setup_hold, + float margin) +{ + Sta::sta()->setDataCheck(from, from_tr, to, to_tr, clk, setup_hold, margin); +} + +void +unset_data_check_cmd(Pin *from, + const TransRiseFallBoth *from_tr, + Pin *to, + const TransRiseFallBoth *to_tr, + Clock *clk, + const SetupHoldAll *setup_hold) +{ + Sta::sta()->removeDataCheck(from, from_tr, to, to_tr, clk, setup_hold); +} + +void +set_input_delay_cmd(Pin *pin, + TransRiseFallBoth *tr, + Clock *clk, + TransRiseFall *clk_tr, + Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + MinMaxAll *min_max, + bool add, + float delay) +{ + cmdLinkedNetwork(); + Sta::sta()->setInputDelay(pin, tr, clk, clk_tr, ref_pin, + source_latency_included, network_latency_included, + min_max, add, delay); +} + +void +unset_input_delay_cmd(Pin *pin, + TransRiseFallBoth *tr, + Clock *clk, + TransRiseFall *clk_tr, + MinMaxAll *min_max) +{ + cmdLinkedNetwork(); + Sta::sta()->removeInputDelay(pin, tr, clk, clk_tr, min_max); +} + +void +set_output_delay_cmd(Pin *pin, + const TransRiseFallBoth *tr, + Clock *clk, + const TransRiseFall *clk_tr, + Pin *ref_pin, + bool source_latency_included, + bool network_latency_included, + const MinMaxAll *min_max, + bool add, + float delay) +{ + cmdLinkedNetwork(); + Sta::sta()->setOutputDelay(pin, tr, clk, clk_tr, ref_pin, + source_latency_included, network_latency_included, + min_max, add, delay); +} + +void +unset_output_delay_cmd(Pin *pin, + TransRiseFallBoth *tr, + Clock *clk, + TransRiseFall *clk_tr, + MinMaxAll *min_max) +{ + cmdLinkedNetwork(); + Sta::sta()->removeOutputDelay(pin, tr, clk, clk_tr, min_max); +} + +void +disable_cell(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to) +{ + cmdLinkedNetwork(); + Sta::sta()->disable(cell, from, to); +} + +void +unset_disable_cell(LibertyCell *cell, + LibertyPort *from, + LibertyPort *to) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisable(cell, from, to); +} + +void +disable_lib_port(LibertyPort *port) +{ + cmdLinkedNetwork(); + Sta::sta()->disable(port); +} + +void +unset_disable_lib_port(LibertyPort *port) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisable(port); +} + +void +disable_port(Port *port) +{ + cmdLinkedNetwork(); + Sta::sta()->disable(port); +} + +void +unset_disable_port(Port *port) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisable(port); +} + +void +disable_instance(Instance *instance, + LibertyPort *from, + LibertyPort *to) +{ + cmdLinkedNetwork(); + Sta::sta()->disable(instance, from, to); +} + +void +unset_disable_instance(Instance *instance, + LibertyPort *from, + LibertyPort *to) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisable(instance, from, to); +} + +void +disable_pin(Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->disable(pin); +} + +void +unset_disable_pin(Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisable(pin); +} + +void +disable_edge(Edge *edge) +{ + cmdLinkedNetwork(); + Sta::sta()->disable(edge); +} + +void +unset_disable_edge(Edge *edge) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisable(edge); +} + +void +disable_timing_arc_set(TimingArcSet *arc_set) +{ + cmdLinkedNetwork(); + Sta::sta()->disable(arc_set); +} + +void +unset_disable_timing_arc_set(TimingArcSet *arc_set) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisable(arc_set); +} + +void +disable_clock_gating_check_inst(Instance *inst) +{ + cmdLinkedNetwork(); + Sta::sta()->disableClockGatingCheck(inst); +} + +void +disable_clock_gating_check_pin(Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->disableClockGatingCheck(pin); +} + +void +unset_disable_clock_gating_check_inst(Instance *inst) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisableClockGatingCheck(inst); +} + +void +unset_disable_clock_gating_check_pin(Pin *pin) +{ + cmdLinkedNetwork(); + Sta::sta()->removeDisableClockGatingCheck(pin); +} + +void +make_false_path(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + const char *comment) +{ + cmdLinkedNetwork(); + Sta::sta()->makeFalsePath(from, thrus, to, min_max, comment); +} + +void +make_multicycle_path(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max, + bool use_end_clk, + int path_multiplier, + const char *comment) +{ + cmdLinkedNetwork(); + Sta::sta()->makeMulticyclePath(from, thrus, to, min_max, use_end_clk, + path_multiplier, comment); +} + +void +make_path_delay(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMax *min_max, + bool ignore_clk_latency, + float delay, + const char *comment) +{ + cmdLinkedNetwork(); + Sta::sta()->makePathDelay(from, thrus, to, min_max, + ignore_clk_latency, delay, comment); +} + +void +reset_path_cmd(ExceptionFrom * + from, ExceptionThruSeq *thrus, + ExceptionTo *to, + const MinMaxAll *min_max) +{ + cmdLinkedNetwork(); + Sta::sta()->resetPath(from, thrus, to, min_max); + // 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. + delete thrus; +} + +void +make_group_path(const char *name, + bool is_default, + ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + const char *comment) +{ + cmdLinkedNetwork(); + if (name[0] == '\0') + name = nullptr; + Sta::sta()->makeGroupPath(name, is_default, from, thrus, to, comment); +} + +bool +is_path_group_name(const char *name) +{ + cmdLinkedNetwork(); + return Sta::sta()->isGroupPathName(name); +} + +ExceptionFrom * +make_exception_from(PinSet *from_pins, + ClockSet *from_clks, + InstanceSet *from_insts, + const TransRiseFallBoth *from_tr) +{ + cmdLinkedNetwork(); + return Sta::sta()->makeExceptionFrom(from_pins, from_clks, from_insts, + from_tr); +} + +void +delete_exception_from(ExceptionFrom *from) +{ + Sta::sta()->deleteExceptionFrom(from); +} + +void +check_exception_from_pins(ExceptionFrom *from, + const char *file, + int line) +{ + Sta::sta()->checkExceptionFromPins(from, file, line); +} + +ExceptionThru * +make_exception_thru(PinSet *pins, + NetSet *nets, + InstanceSet *insts, + const TransRiseFallBoth *tr) +{ + cmdLinkedNetwork(); + return Sta::sta()->makeExceptionThru(pins, nets, insts, tr); +} + +void +delete_exception_thru(ExceptionThru *thru) +{ + Sta::sta()->deleteExceptionThru(thru); +} + +ExceptionTo * +make_exception_to(PinSet *to_pins, + ClockSet *to_clks, + InstanceSet *to_insts, + const TransRiseFallBoth *tr, + TransRiseFallBoth *end_tr) +{ + cmdLinkedNetwork(); + return Sta::sta()->makeExceptionTo(to_pins, to_clks, to_insts, tr, end_tr); +} + +void +delete_exception_to(ExceptionTo *to) +{ + Sta::sta()->deleteExceptionTo(to); +} + +void +check_exception_to_pins(ExceptionTo *to, + const char *file, + int line) +{ + Sta::sta()->checkExceptionToPins(to, file, line); +} + +void +set_input_slew_cmd(Port *port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float slew) +{ + cmdLinkedNetwork(); + Sta::sta()->setInputSlew(port, tr, min_max, slew); +} + +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 TransRiseFallBoth *tr, + const MinMaxAll *min_max) +{ + float from_slews[TransRiseFall::index_count]; + from_slews[TransRiseFall::riseIndex()] = from_slew_rise; + from_slews[TransRiseFall::fallIndex()] = from_slew_fall; + Sta::sta()->setDriveCell(library, cell, port, from_port, from_slews, + to_port, tr, min_max); +} + +void +set_drive_resistance_cmd(Port *port, + const TransRiseFallBoth *tr, + const MinMaxAll *min_max, + float res) +{ + cmdLinkedNetwork(); + Sta::sta()->setDriveResistance(port, tr, min_max, res); +} + +void +set_slew_limit_clk(Clock *clk, + const TransRiseFallBoth *tr, + PathClkOrData clk_data, + const MinMax *min_max, + float slew) +{ + cmdLinkedNetwork(); + Sta::sta()->setSlewLimit(clk, tr, clk_data, min_max, slew); +} + +void +set_slew_limit_port(Port *port, + const MinMax *min_max, + float slew) +{ + cmdLinkedNetwork(); + Sta::sta()->setSlewLimit(port, min_max, slew); +} + +void +set_slew_limit_pin(Pin *pin, + const MinMax *min_max, + float slew) +{ + cmdLinkedNetwork(); + Sta::sta()->setSlewLimit(pin, min_max, slew); +} + +void +set_slew_limit_cell(Cell *cell, + const MinMax *min_max, + float slew) +{ + cmdLinkedNetwork(); + Sta::sta()->setSlewLimit(cell, min_max, slew); +} + +void +set_port_capacitance_limit(Port *port, + const MinMax *min_max, + float cap) +{ + Sta::sta()->setCapacitanceLimit(port, min_max, cap); +} + +void +set_pin_capacitance_limit(Pin *pin, + const MinMax *min_max, + float cap) +{ + Sta::sta()->setCapacitanceLimit(pin, min_max, cap); +} + +void +set_cell_capacitance_limit(Cell *cell, + const MinMax *min_max, + float cap) +{ + Sta::sta()->setCapacitanceLimit(cell, min_max, cap); +} + +void +set_latch_borrow_limit_pin(Pin *pin, + float limit) +{ + Sta::sta()->setLatchBorrowLimit(pin, limit); +} + +void +set_latch_borrow_limit_inst(Instance *inst, + float limit) +{ + Sta::sta()->setLatchBorrowLimit(inst, limit); +} + +void +set_latch_borrow_limit_clk(Clock *clk, float limit) +{ + Sta::sta()->setLatchBorrowLimit(clk, limit); +} + +void +set_min_pulse_width_global(const TransRiseFallBoth *tr, + float min_width) +{ + Sta::sta()->setMinPulseWidth(tr, min_width); +} + +void +set_min_pulse_width_pin(Pin *pin, + const TransRiseFallBoth *tr, + float min_width) +{ + Sta::sta()->setMinPulseWidth(pin, tr, min_width); +} + +void +set_min_pulse_width_clk(Clock *clk, + const TransRiseFallBoth *tr, + float min_width) +{ + Sta::sta()->setMinPulseWidth(clk, tr, min_width); +} + +void +set_min_pulse_width_inst(Instance *inst, + const TransRiseFallBoth *tr, + float min_width) +{ + Sta::sta()->setMinPulseWidth(inst, tr, min_width); +} + +void +set_max_area_cmd(float area) +{ + Sta::sta()->setMaxArea(area); +} + +void +set_port_fanout_limit(Port *port, + const MinMax *min_max, + float fanout) +{ + Sta::sta()->setFanoutLimit(port, min_max, fanout); +} + +void +set_cell_fanout_limit(Cell *cell, + const MinMax *min_max, + float fanout) +{ + Sta::sta()->setFanoutLimit(cell, min_max, fanout); +} + +void +set_logic_value_cmd(Pin *pin, + LogicValue value) +{ + Sta::sta()->setLogicValue(pin, value); +} + +void +set_case_analysis_cmd(Pin *pin, + LogicValue value) +{ + Sta::sta()->setCaseAnalysis(pin, value); +} + +void +unset_case_analysis_cmd(Pin *pin) +{ + Sta::sta()->removeCaseAnalysis(pin); +} + +void +set_timing_derate_cmd(TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate) +{ + Sta::sta()->setTimingDerate(type, clk_data, tr, early_late, derate); +} + +void +set_timing_derate_net_cmd(const Net *net, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate) +{ + Sta::sta()->setTimingDerate(net, clk_data, tr, early_late, derate); +} + +void +set_timing_derate_inst_cmd(const Instance *inst, + TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate) +{ + Sta::sta()->setTimingDerate(inst, type, clk_data, tr, early_late, derate); +} + +void +set_timing_derate_cell_cmd(const LibertyCell *cell, + TimingDerateType type, + PathClkOrData clk_data, + const TransRiseFallBoth *tr, + const EarlyLate *early_late, + float derate) +{ + Sta::sta()->setTimingDerate(cell, type, clk_data, tr, early_late, derate); +} + +void +unset_timing_derate_cmd() +{ + Sta::sta()->unsetTimingDerate(); +} + +ClockIterator * +clock_iterator() +{ + return new ClockIterator(Sta::sta()->sdc()); +} + +Clock * +find_clock(const char *name) +{ + cmdLinkedNetwork(); + return Sta::sta()->sdc()->findClock(name); +} + +bool +is_clock_src(const Pin *pin) +{ + return Sta::sta()->isClockSrc(pin); +} + +Clock * +default_arrival_clock() +{ + return Sta::sta()->sdc()->defaultArrivalClock(); +} + +ClockSeq * +find_clocks_matching(const char *pattern, + bool regexp, + bool nocase) +{ + cmdLinkedNetwork(); + ClockSeq *clks = new ClockSeq; + Sta *sta = Sta::sta(); + Sdc *sdc = sta->sdc(); + PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp()); + sdc->findClocksMatching(&matcher, clks); + return clks; +} + +void +update_generated_clks() +{ + cmdLinkedNetwork(); + Sta::sta()->updateGeneratedClks(); +} + +bool +pin_is_clock(const Pin *pin) +{ + 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); +} + +bool +pin_is_genclk_src(const Pin *pin) +{ + 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); +} + +// 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) +{ + float value1 = strtof(value, nullptr); + return Sta::sta()->units()->timeUnit()->asString(value1, digits); +} + +const char * +format_capacitance(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + return Sta::sta()->units()->capacitanceUnit()->asString(value1, digits); +} + +const char * +format_resistance(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + return Sta::sta()->units()->resistanceUnit()->asString(value1, digits); +} + +const char * +format_voltage(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + return Sta::sta()->units()->voltageUnit()->asString(value1, digits); +} + +const char * +format_power(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + return Sta::sta()->units()->powerUnit()->asString(value1, digits); +} + +const char * +format_distance(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + Unit *dist_unit = Sta::sta()->units()->distanceUnit(); + return dist_unit->asString(value1, digits); +} + +const char * +format_area(const char *value, + int digits) +{ + float value1 = strtof(value, nullptr); + Unit *dist_unit = Sta::sta()->units()->distanceUnit(); + return dist_unit->asString(value1 / dist_unit->scale(), digits); +} + +//////////////////////////////////////////////////////////////// + +// Unit converstion from sta unit to user interface and visa versa. +double +time_ui_sta(double value) +{ + return value * Sta::sta()->units()->timeUnit()->scale(); +} + +double +time_sta_ui(double value) +{ + return value / Sta::sta()->units()->timeUnit()->scale(); +} + +double +capacitance_ui_sta(double value) +{ + return value * Sta::sta()->units()->capacitanceUnit()->scale(); +} + +double +capacitance_sta_ui(double value) +{ + return value / Sta::sta()->units()->capacitanceUnit()->scale(); +} + +double +resistance_ui_sta(double value) +{ + return value * Sta::sta()->units()->resistanceUnit()->scale(); +} + +double +resistance_sta_ui(double value) +{ + return value / Sta::sta()->units()->resistanceUnit()->scale(); +} + +double +voltage_ui_sta(double value) +{ + return value * Sta::sta()->units()->voltageUnit()->scale(); +} + +double +voltage_sta_ui(double value) +{ + return value / Sta::sta()->units()->voltageUnit()->scale(); +} + +double +current_ui_sta(double value) +{ + return value * Sta::sta()->units()->currentUnit()->scale(); +} + +double +current_sta_ui(double value) +{ + return value / Sta::sta()->units()->currentUnit()->scale(); +} + +double +power_ui_sta(double value) +{ + return value * Sta::sta()->units()->powerUnit()->scale(); +} + +double +power_sta_ui(double value) +{ + return value / Sta::sta()->units()->powerUnit()->scale(); +} + +double +distance_ui_sta(double value) +{ + return value * Sta::sta()->units()->distanceUnit()->scale(); +} + +double +distance_sta_ui(double value) +{ + return value / Sta::sta()->units()->distanceUnit()->scale(); +} + +double +area_ui_sta(double value) +{ + double scale = Sta::sta()->units()->distanceUnit()->scale(); + return value * scale * scale; +} + +double +area_sta_ui(double value) +{ + double scale = Sta::sta()->units()->distanceUnit()->scale(); + return value / (scale * scale); +} + +//////////////////////////////////////////////////////////////// + +void +set_cmd_unit_scale(const char *unit_name, + float scale) +{ + Unit *unit = Sta::sta()->units()->find(unit_name); + if (unit) + unit->setScale(scale); +} + +void +set_cmd_unit_digits(const char *unit_name, + int digits) +{ + Unit *unit = Sta::sta()->units()->find(unit_name); + if (unit) + unit->setDigits(digits); +} + +void +set_cmd_unit_suffix(const char *unit_name, + const char *suffix) +{ + Unit *unit = Sta::sta()->units()->find(unit_name); + if (unit) { + unit->setSuffix(suffix); + } +} + +//////////////////////////////////////////////////////////////// + +VertexIterator * +vertex_iterator() +{ + return new VertexIterator(cmdGraph()); +} + +void +set_arc_delay(Edge *edge, + TimingArc *arc, + const Corner *corner, + const MinMaxAll *min_max, + float delay) +{ + cmdGraph(); + Sta::sta()->setArcDelay(edge, arc, corner, min_max, delay); +} + +void +set_annotated_slew(Vertex *vertex, + const Corner *corner, + const MinMaxAll *min_max, + const TransRiseFallBoth *tr, + float slew) +{ + cmdGraph(); + Sta::sta()->setAnnotatedSlew(vertex, corner, min_max, tr, slew); +} + +// Remove all delay and slew annotations. +void +remove_delay_slew_annotations() +{ + cmdGraph(); + Sta::sta()->removeDelaySlewAnnotations(); +} + +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) +{ + cmdLinkedNetwork(); + return Sta::sta()->checkTiming(no_input_delay, no_output_delay, + reg_multiple_clks, reg_no_clks, + unconstrained_endpoints, + loops, generated_clks); +} + +bool +crpr_enabled() +{ + return Sta::sta()->crprEnabled(); +} + +void +set_crpr_enabled(bool enabled) +{ + return Sta::sta()->setCrprEnabled(enabled); +} + +const char * +crpr_mode() +{ + switch (Sta::sta()->crprMode()) { + case CrprMode::same_transition: + return "same_transition"; + case CrprMode::same_pin: + return "same_pin"; + default: + return ""; + } +} + +void +set_crpr_mode(const char *mode) +{ + if (stringEq(mode, "same_pin")) + Sta::sta()->setCrprMode(CrprMode::same_pin); + else if (stringEq(mode, "same_transition")) + Sta::sta()->setCrprMode(CrprMode::same_transition); + else + internalError("unknown common clk pessimism mode."); +} + +bool +pocv_enabled() +{ + return Sta::sta()->pocvEnabled(); +} + +void +set_pocv_enabled(bool enabled) +{ +#if !SSTA + if (enabled) + Sta::sta()->report()->error("POCV support requires compilation with SSTA=1.\n"); +#endif + return Sta::sta()->setPocvEnabled(enabled); +} + +float +pocv_sigma_factor() +{ + return Sta::sta()->sigmaFactor(); +} + +void +set_pocv_sigma_factor(float factor) +{ + Sta::sta()->setSigmaFactor(factor); +} + +bool +propagate_gated_clock_enable() +{ + return Sta::sta()->propagateGatedClockEnable(); +} + +void +set_propagate_gated_clock_enable(bool enable) +{ + Sta::sta()->setPropagateGatedClockEnable(enable); +} + +bool +preset_clr_arcs_enabled() +{ + return Sta::sta()->presetClrArcsEnabled(); +} + +void +set_preset_clr_arcs_enabled(bool enable) +{ + Sta::sta()->setPresetClrArcsEnabled(enable); +} + +bool +cond_default_arcs_enabled() +{ + return Sta::sta()->condDefaultArcsEnabled(); +} + +void +set_cond_default_arcs_enabled(bool enabled) +{ + Sta::sta()->setCondDefaultArcsEnabled(enabled); +} + +bool +bidirect_inst_paths_enabled() +{ + return Sta::sta()->bidirectInstPathsEnabled(); +} + +void +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() +{ + return Sta::sta()->recoveryRemovalChecksEnabled(); +} + +void +set_recovery_removal_checks_enabled(bool enabled) +{ + Sta::sta()->setRecoveryRemovalChecksEnabled(enabled); +} + +bool +gated_clk_checks_enabled() +{ + return Sta::sta()->gatedClkChecksEnabled(); +} + +void +set_gated_clk_checks_enabled(bool enabled) +{ + Sta::sta()->setGatedClkChecksEnabled(enabled); +} + +bool +dynamic_loop_breaking() +{ + return Sta::sta()->dynamicLoopBreaking(); +} + +void +set_dynamic_loop_breaking(bool enable) +{ + Sta::sta()->setDynamicLoopBreaking(enable); +} + +bool +use_default_arrival_clock() +{ + return Sta::sta()->useDefaultArrivalClock(); +} + +void +set_use_default_arrival_clock(bool enable) +{ + return Sta::sta()->setUseDefaultArrivalClock(enable); +} + +bool +propagate_all_clocks() +{ + return Sta::sta()->propagateAllClocks(); +} + +void +set_propagate_all_clocks(bool prop) +{ + Sta::sta()->setPropagateAllClocks(prop); +} + +//////////////////////////////////////////////////////////////// + +PathEndSeq * +find_path_ends(ExceptionFrom *from, + ExceptionThruSeq *thrus, + ExceptionTo *to, + bool unconstrained, + Corner *corner, + const MinMaxAll *delay_min_max, + int group_count, + int endpoint_count, + bool unique_pins, + 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) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + PathEndSeq *ends = sta->findPathEnds(from, thrus, to, unconstrained, + corner, delay_min_max, + group_count, endpoint_count, unique_pins, + slack_min, slack_max, + sort_by_slack, + groups->size() ? groups : nullptr, + 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) +{ + Sta::sta()->reportPathEnd(end); +} + +void +report_path_end2(PathEnd *end, + PathEnd *prev_end) +{ + Sta::sta()->reportPathEnd(end, prev_end); +} + +void +set_report_path_format(ReportPathFormat format) +{ + Sta::sta()->setReportPathFormat(format); +} + +void +set_report_path_field_order(StringSeq *field_names) +{ + Sta::sta()->setReportPathFieldOrder(field_names); + delete field_names; +} + +void +set_report_path_fields(bool report_input_pin, + bool report_net, + bool report_cap, + bool report_slew) +{ + Sta::sta()->setReportPathFields(report_input_pin, + report_net, + report_cap, + report_slew); +} + +void +set_report_path_field_properties(const char *field_name, + 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()->print("Error: unknown report path field %s\n", 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()->print("Error: unknown report path field %s\n", field_name); +} + +void +set_report_path_digits(int digits) +{ + Sta::sta()->setReportPathDigits(digits); +} + +void +set_report_path_no_split(bool no_split) +{ + Sta::sta()->setReportPathNoSplit(no_split); +} + +void +delete_path_ref(PathRef *path) +{ + delete path; +} + +void +remove_constraints() +{ + Sta::sta()->removeConstraints(); +} + +void +report_path_cmd(PathRef *path) +{ + Sta::sta()->reportPath(path); +} + +void +report_clk_skew(ClockSet *clks, + const Corner *corner, + const SetupHold *setup_hold, + int digits) +{ + cmdLinkedNetwork(); + Sta::sta()->reportClkSkew(clks, corner, setup_hold, digits); + delete clks; +} + +TmpPinSet * +startpoints() +{ + return findStartpoints(); +} + +TmpPinSet * +endpoints() +{ + return findEndpoints(); +} + +TmpPinSet * +group_path_pins(const char *group_path_name) +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->sdc(); + if (sdc->isGroupPathName(group_path_name)) + return sta->findGroupPathPins(group_path_name); + else + return nullptr; +} + +//////////////////////////////////////////////////////////////// + +MinPulseWidthCheckSeq & +min_pulse_width_violations(const Corner *corner) +{ + cmdLinkedNetwork(); + return Sta::sta()->minPulseWidthViolations(corner); +} + +MinPulseWidthCheckSeq & +min_pulse_width_check_pins(PinSeq *pins, + const Corner *corner) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + MinPulseWidthCheckSeq &checks = sta->minPulseWidthChecks(pins, corner); + delete pins; + return checks; +} + +MinPulseWidthCheckSeq & +min_pulse_width_checks(const Corner *corner) +{ + cmdLinkedNetwork(); + return Sta::sta()->minPulseWidthChecks(corner); +} + +MinPulseWidthCheck * +min_pulse_width_check_slack(const Corner *corner) +{ + cmdLinkedNetwork(); + 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) +{ + Sta::sta()->reportMpwCheck(check, verbose); +} + +//////////////////////////////////////////////////////////////// + +MinPeriodCheckSeq & +min_period_violations() +{ + cmdLinkedNetwork(); + return Sta::sta()->minPeriodViolations(); +} + +MinPeriodCheck * +min_period_check_slack() +{ + cmdLinkedNetwork(); + 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) +{ + Sta::sta()->reportCheck(check, verbose); +} + +//////////////////////////////////////////////////////////////// + +MaxSkewCheckSeq & +max_skew_violations() +{ + cmdLinkedNetwork(); + return Sta::sta()->maxSkewViolations(); +} + +MaxSkewCheck * +max_skew_check_slack() +{ + cmdLinkedNetwork(); + return Sta::sta()->maxSkewSlack(); +} + +void +report_max_skew_checks(MaxSkewCheckSeq *checks, + bool verbose) +{ + Sta::sta()->reportChecks(checks, verbose); +} + +void +report_max_skew_check(MaxSkewCheck *check, + bool verbose) +{ + Sta::sta()->reportCheck(check, verbose); +} + +//////////////////////////////////////////////////////////////// + +void +find_timing_cmd(bool full) +{ + cmdLinkedNetwork(); + Sta::sta()->updateTiming(full); +} + +void +find_requireds() +{ + cmdLinkedNetwork(); + Sta::sta()->findRequireds(); +} + +void +find_delays() +{ + cmdLinkedNetwork(); + Sta::sta()->findDelays(); +} + +Slack +total_negative_slack_cmd(const MinMax *min_max) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + return sta->totalNegativeSlack(min_max); +} + +Slack +total_negative_slack_corner_cmd(const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + return sta->totalNegativeSlack(corner, min_max); +} + +Slack +worst_slack_cmd(const MinMax *min_max) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + Slack worst_slack; + Vertex *worst_vertex; + sta->worstSlack(min_max, worst_slack, worst_vertex); + return worst_slack; +} + +Vertex * +worst_slack_vertex(const MinMax *min_max) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + Slack worst_slack; + Vertex *worst_vertex; + sta->worstSlack(min_max, worst_slack, worst_vertex); + return worst_vertex;; +} + +Slack +worst_slack_corner(const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + Slack worst_slack; + Vertex *worst_vertex; + sta->worstSlack(corner, min_max, worst_slack, worst_vertex); + return worst_slack; +} + +PathRef * +vertex_worst_arrival_path(Vertex *vertex, + const MinMax *min_max) +{ + Sta *sta = Sta::sta(); + PathRef path; + sta->vertexWorstArrivalPath(vertex, min_max, path); + if (!path.isNull()) + return new PathRef(path); + else + return nullptr; +} + +PathRef * +vertex_worst_arrival_path_tr(Vertex *vertex, + const TransRiseFall *tr, + MinMax *min_max) +{ + Sta *sta = Sta::sta(); + PathRef path; + sta->vertexWorstArrivalPath(vertex, tr, min_max, path); + if (!path.isNull()) + return new PathRef(path); + else + return nullptr; +} + +PathRef * +vertex_worst_slack_path(Vertex *vertex, + const MinMax *min_max) +{ + Sta *sta = Sta::sta(); + PathRef path; + sta->vertexWorstSlackPath(vertex, min_max, path); + if (!path.isNull()) + return new PathRef(path); + else + return nullptr; +} + +TmpString * +report_delay_calc_cmd(Edge *edge, + TimingArc *arc, + const Corner *corner, + const MinMax *min_max, + int digits) +{ + cmdLinkedNetwork(); + return Sta::sta()->reportDelayCalc(edge, arc, corner, min_max, digits); +} + +Pin * +pin_min_slew_limit_slack(const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + return Sta::sta()->pinMinSlewLimitSlack(corner, min_max); +} + +PinSeq * +pin_slew_limit_violations(const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + return Sta::sta()->pinSlewLimitViolations(corner, min_max); +} + +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); +} + +//////////////////////////////////////////////////////////////// + +TmpFloatSeq * +design_power(const Corner *corner) +{ + cmdLinkedNetwork(); + PowerResult total, sequential, combinational, macro, pad; + Sta::sta()->power(corner, total, sequential, combinational, macro, pad); + FloatSeq *floats = new FloatSeq; + pushPowerResultFloats(total, floats); + pushPowerResultFloats(sequential, floats); + pushPowerResultFloats(combinational, floats); + pushPowerResultFloats(macro, floats); + pushPowerResultFloats(pad, floats); + return floats; +} + +TmpFloatSeq * +instance_power(Instance *inst, + const Corner *corner) +{ + cmdLinkedNetwork(); + PowerResult power; + Sta::sta()->power(inst, corner, power); + FloatSeq *floats = new FloatSeq; + floats->push_back(power.internal()); + floats->push_back(power.switching()); + floats->push_back(power.leakage()); + floats->push_back(power.total()); + return floats; +} + +void +set_power_global_activity(float activity, + float duty) +{ + Sta::sta()->power()->setGlobalActivity(activity, duty); +} + +void +set_power_input_activity(float activity, + float duty) +{ + return Sta::sta()->power()->setInputActivity(activity, duty); +} + +void +set_power_input_port_activity(const Port *input_port, + float activity, + float duty) +{ + return Sta::sta()->power()->setInputPortActivity(input_port, activity, duty); +} + +void +set_power_pin_activity(const Pin *pin, + float activity, + float duty) +{ + return Sta::sta()->power()->setPinActivity(pin, activity, duty, + PwrActivityOrigin::user); +} + +//////////////////////////////////////////////////////////////// + +EdgeSeq * +disabled_edges_sorted() +{ + cmdLinkedNetwork(); + return Sta::sta()->disabledEdgesSorted(); +} + +void +write_sdc_cmd(const char *filename, + bool compatible, + bool no_timestamp, + int digits) +{ + cmdLinkedNetwork(); + Sta::sta()->writeSdc(filename, compatible, no_timestamp, digits); +} + +void +write_path_spice_cmd(PathRef *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 *sta = Sta::sta(); + writePathSpice(path, spice_filename, subckt_filename, + lib_subckt_filename, model_filename, + power_name, gnd_name, sta); +} + +bool +liberty_supply_exists(const char *supply_name) +{ + auto network = Sta::sta()->network(); + auto lib = network->defaultLibertyLibrary(); + return lib && lib->supplyExists(supply_name); +} + +float +unit_scale(const char *unit_name) +{ + Unit *unit = Sta::sta()->units()->find(unit_name); + if (unit) + return unit->scale(); + else + return 1.0F; +} + +bool +fuzzy_equal(float value1, + float value2) +{ + return fuzzyEqual(value1, value2); +} + +char +pin_sim_logic_value(const Pin *pin) +{ + return logicValueString(Sta::sta()->simLogicValue(pin)); +} + +char +pin_case_logic_value(const Pin *pin) +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->sdc(); + LogicValue value = LogicValue::unknown; + bool exists; + sdc->caseLogicValue(pin, value, exists); + return logicValueString(value); +} + +char +pin_logic_value(const Pin *pin) +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->sdc(); + LogicValue value = LogicValue::unknown; + bool exists; + sdc->logicValue(pin, value, exists); + return logicValueString(value); +} + +SlowDrvrIterator * +slow_driver_iterator() +{ + return Sta::sta()->slowDrvrIterator(); +} + +bool +timing_arc_disabled(Edge *edge, + TimingArc *arc) +{ + Graph *graph = Sta::sta()->graph(); + return !searchThru(edge, arc, graph); +} + +ClockGroups * +make_clock_groups(const char *name, + bool logically_exclusive, + bool physically_exclusive, + bool asynchronous, + bool allow_paths, + const char *comment) +{ + return Sta::sta()->makeClockGroups(name, logically_exclusive, + physically_exclusive, asynchronous, + allow_paths, comment); +} + +void +clock_groups_make_group(ClockGroups *clk_groups, + ClockSet *clks) +{ + Sta::sta()->makeClockGroup(clk_groups, clks); +} + +void +unset_clock_groups_logically_exclusive(const char *name) +{ + Sta::sta()->removeClockGroupsLogicallyExclusive(name); +} + +void +unset_clock_groups_physically_exclusive(const char *name) +{ + Sta::sta()->removeClockGroupsPhysicallyExclusive(name); +} + +void +unset_clock_groups_asynchronous(const char *name) +{ + Sta::sta()->removeClockGroupsAsynchronous(name); +} + +// Debugging function. +bool +same_clk_group(Clock *clk1, + Clock *clk2) +{ + Sta *sta = Sta::sta(); + Sdc *sdc = sta->sdc(); + return sdc->sameClockGroupExplicit(clk1, clk2); +} + +void +set_clock_sense_cmd(PinSet *pins, + ClockSet *clks, + bool positive, + bool negative, + bool stop_propagation) +{ + ClockSense sense; + if (positive) + sense = ClockSense::positive; + else if (negative) + sense = ClockSense::negative; + else if (stop_propagation) + sense = ClockSense::stop; + else + internalError("unknown clock sense"); + Sta::sta()->setClockSense(pins, clks, sense); +} + +bool +timing_role_is_check(TimingRole *role) +{ + return role->isTimingCheck(); +} + +//////////////////////////////////////////////////////////////// + +TmpPinSet * +find_fanin_pins(PinSeq *to, + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + PinSet *fanin = sta->findFaninPins(to, flat, startpoints_only, + inst_levels, pin_levels, + thru_disabled, thru_constants); + delete to; + return fanin; +} + +TmpInstanceSet * +find_fanin_insts(PinSeq *to, + bool flat, + bool startpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + InstanceSet *fanin = sta->findFaninInstances(to, flat, startpoints_only, + inst_levels, pin_levels, + thru_disabled, thru_constants); + delete to; + return fanin; +} + +TmpPinSet * +find_fanout_pins(PinSeq *from, + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + PinSet *fanout = sta->findFanoutPins(from, flat, endpoints_only, + inst_levels, pin_levels, + thru_disabled, thru_constants); + delete from; + return fanout; +} + +TmpInstanceSet * +find_fanout_insts(PinSeq *from, + bool flat, + bool endpoints_only, + int inst_levels, + int pin_levels, + bool thru_disabled, + bool thru_constants) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + InstanceSet *fanout = sta->findFanoutInstances(from, flat, endpoints_only, + inst_levels, pin_levels, + thru_disabled, thru_constants); + delete from; + return fanout; +} + +TmpPinSet * +net_load_pins(Net *net) +{ + Network *network = cmdLinkedNetwork(); + PinSet *pins = new PinSet; + NetConnectedPinIterator *pin_iter = network->connectedPinIterator(net); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (network->isLoad(pin)) + pins->insert(pin); + } + delete pin_iter; + return pins; +} + +TmpPinSet * +net_driver_pins(Net *net) +{ + Network *network = cmdLinkedNetwork(); + PinSet *pins = new PinSet; + NetConnectedPinIterator *pin_iter = network->connectedPinIterator(net); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + if (network->isDriver(pin)) + pins->insert(pin); + } + delete pin_iter; + return pins; +} + +//////////////////////////////////////////////////////////////// + +void +report_loops() +{ + Sta *sta = Sta::sta(); + Network *network = cmdLinkedNetwork(); + Graph *graph = cmdGraph(); + Report *report = sta->report(); + GraphLoopSeq *loops = sta->graphLoops(); + GraphLoopSeq::Iterator loop_iter(loops); + while (loop_iter.hasNext()) { + GraphLoop *loop = loop_iter.next(); + loop->report(report, network, graph); + report->print("\n"); + } +} + +// Includes top instance. +int +network_instance_count() +{ + Network *network = cmdNetwork(); + return network->instanceCount(); +} + +int +network_pin_count() +{ + Network *network = cmdNetwork(); + return network->pinCount(); +} + +int +network_net_count() +{ + Network *network = cmdNetwork(); + return network->netCount(); +} + +int +network_leaf_instance_count() +{ + Network *network = cmdNetwork(); + return network->leafInstanceCount(); +} + +int +network_leaf_pin_count() +{ + Network *network = cmdNetwork(); + return network->leafPinCount(); +} + +int +graph_vertex_count() +{ + return cmdGraph()->vertexCount(); +} + +int +graph_edge_count() +{ + return cmdGraph()->edgeCount(); +} + +int +graph_arc_count() +{ + return cmdGraph()->arcCount(); +} + +int +tag_group_count() +{ + return Sta::sta()->tagGroupCount(); +} + +void +report_tag_groups() +{ + Sta::sta()->search()->reportTagGroups(); +} + +void +report_tag_arrivals_cmd(Vertex *vertex) +{ + Sta::sta()->search()->reportArrivals(vertex); +} + +void +report_arrival_count_histogram() +{ + Sta::sta()->search()->reportArrivalCountHistogram(); +} + +int +tag_count() +{ + return Sta::sta()->tagCount(); +} + +void +report_tags() +{ + Sta::sta()->search()->reportTags(); +} + +void +report_clk_infos() +{ + Sta::sta()->search()->reportClkInfos(); +} + +int +clk_info_count() +{ + return Sta::sta()->clkInfoCount(); +} + +int +arrival_count() +{ + return Sta::sta()->arrivalCount(); +} + +void +delete_all_memory() +{ + deleteAllMemory(); +} + +Tcl_Interp * +tcl_interp() +{ + return Sta::sta()->tclInterp(); +} + +// Initialize sta after delete_all_memory. +void +init_sta() +{ + initSta(); +} + +void +clear_sta() +{ + Sta::sta()->clear(); +} + +void +make_sta(Tcl_Interp *interp) +{ + Sta *sta = new Sta; + Sta::setSta(sta); + sta->makeComponents(); + sta->setTclInterp(interp); +} + +void +clear_network() +{ + Sta *sta = Sta::sta(); + sta->network()->clear(); +} + +// Elapsed run time (in seconds). +double +elapsed_run_time() +{ + return elapsedRunTime(); +} + +// User run time (in seconds). +double +user_run_time() +{ + return userRunTime(); +} + +// User run time (in seconds). +unsigned long +cputime() +{ + return static_cast(userRunTime() + .5); +} + +// Peak memory usage in bytes. +unsigned long +mem() +{ + return memoryUsage(); +} + +int +processor_count() +{ + return processorCount(); +} + +int +thread_count() +{ + return Sta::sta()->threadCount(); +} + +void +set_thread_count(int count) +{ + Sta::sta()->setThreadCount(count); +} + +void +arrivals_invalid() +{ + Sta *sta = Sta::sta(); + sta->arrivalsInvalid(); +} + +%} // inline + +//////////////////////////////////////////////////////////////// +// +// Object Methods +// +//////////////////////////////////////////////////////////////// + +%extend Library { +Cell * +find_cell(const char *name) +{ + return cmdNetwork()->findCell(self, name); +} + +TmpCellSeq * +find_cells_matching(const char *pattern, + bool regexp, + bool nocase) +{ + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + TmpCellSeq *cells = new TmpCellSeq; + cmdNetwork()->findCellsMatching(self, &matcher, cells); + return cells; +} + +} // Library methods + +%extend LibertyLibrary { + +LibertyCell * +find_liberty_cell(const char *name) +{ + return self->findLibertyCell(name); +} + +TmpLibertyCellSeq * +find_liberty_cells_matching(const char *pattern, + bool regexp, + bool nocase) +{ + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + // TmpLibertyCellSeq deletes temporary CellSeq after conversion to tcl list. + TmpLibertyCellSeq *cells = new TmpLibertyCellSeq; + self->findLibertyCellsMatching(&matcher, cells); + return cells; +} + +Wireload * +find_wireload(const char *model_name) +{ + return self->findWireload(model_name); +} + +WireloadSelection * +find_wireload_selection(const char *selection_name) +{ + return self->findWireloadSelection(selection_name); +} + +OperatingConditions * +find_operating_conditions(const char *op_cond_name) +{ + return self->findOperatingConditions(op_cond_name); +} + +OperatingConditions * +default_operating_conditions() +{ + return self->defaultOperatingConditions(); +} + +} // LibertyLibrary methods + +%extend LibraryIterator { +bool has_next() { return self->hasNext(); } +Library *next() { return self->next(); } +void finish() { delete self; } +} // LibraryIterator methods + +%extend LibertyLibraryIterator { +bool has_next() { return self->hasNext(); } +LibertyLibrary *next() { return self->next(); } +void finish() { delete self; } +} // LibertyLibraryIterator methods + +%extend Cell { +Library *library() { return cmdNetwork()->library(self); } +LibertyCell *liberty_cell() { return cmdNetwork()->libertyCell(self); } +bool is_leaf() { return cmdNetwork()->isLeaf(self); } +CellPortIterator * +port_iterator() { return cmdNetwork()->portIterator(self); } + +Port * +find_port(const char *name) +{ + return cmdNetwork()->findPort(self, name); +} + +TmpPortSeq * +find_ports_matching(const char *pattern, + bool regexp, + bool nocase) +{ + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + TmpPortSeq *ports = new TmpPortSeq; + cmdNetwork()->findPortsMatching(self, &matcher, ports); + return ports; +} + +} // Cell methods + +%extend LibertyCell { +bool is_leaf() { return self->isLeaf(); } +LibertyLibrary *liberty_library() { return self->libertyLibrary(); } +Cell *cell() { return reinterpret_cast(self); } +LibertyPort * +find_liberty_port(const char *name) +{ + return self->findLibertyPort(name); +} + +TmpLibertyPortSeq * +find_liberty_ports_matching(const char *pattern, + bool regexp, + bool nocase) +{ + PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp()); + TmpLibertyPortSeq *ports = new TmpLibertyPortSeq; + self->findLibertyPortsMatching(&matcher, ports); + return ports; +} + +LibertyCellPortIterator * +liberty_port_iterator() { return new LibertyCellPortIterator(self); } + +LibertyCellTimingArcSetIterator * +timing_arc_set_iterator() { return new LibertyCellTimingArcSetIterator(self); } + +} // LibertyCell methods + +%extend CellPortIterator { +bool has_next() { return self->hasNext(); } +Port *next() { return self->next(); } +void finish() { delete self; } +} // CellPortIterator methods + +%extend LibertyCellPortIterator { +bool has_next() { return self->hasNext(); } +LibertyPort *next() { return self->next(); } +void finish() { delete self; } +} // LibertyCellPortIterator methods + +%extend Port { +const char *bus_name() { return cmdNetwork()->busName(self); } +Cell *cell() { return cmdNetwork()->cell(self); } +LibertyPort *liberty_port() { return cmdNetwork()->libertyPort(self); } +bool is_bus() { return cmdNetwork()->isBus(self); } +PortMemberIterator * +member_iterator() { return cmdNetwork()->memberIterator(self); } + +} // Port methods + +%extend LibertyPort { +const char *bus_name() { return self->busName(); } +Cell *cell() { return self->cell(); } +bool is_bus() { return self->isBus(); } +LibertyPortMemberIterator * +member_iterator() { return new LibertyPortMemberIterator(self); } + +const char * +function() +{ + FuncExpr *func = self->function(); + if (func) + return func->asString(); + else + return nullptr; +} + +const char * +tristate_enable() +{ + FuncExpr *enable = self->tristateEnable(); + if (enable) + return enable->asString(); + else + return nullptr; +} + +float +capacitance(const TransRiseFall *tr, + const MinMax *min_max) +{ + Sta *sta = Sta::sta(); + OperatingConditions *op_cond = sta->operatingConditions(min_max); + return self->capacitance(tr, min_max, op_cond, op_cond); +} + +} // LibertyPort methods + +%extend OperatingConditions { +float process() { return self->process(); } +float voltage() { return self->voltage(); } +float temperature() { return self->temperature(); } +} + +%extend PortMemberIterator { +bool has_next() { return self->hasNext(); } +Port *next() { return self->next(); } +void finish() { delete self; } +} // PortMemberIterator methods + +%extend LibertyPortMemberIterator { +bool has_next() { return self->hasNext(); } +LibertyPort *next() { return self->next(); } +void finish() { delete self; } +} // LibertyPortMemberIterator methods + +%extend TimingArcSet { +LibertyPort *from() { return self->from(); } +LibertyPort *to() { return self->to(); } +TimingRole *role() { return self->role(); } +const char *sdf_cond() { return self->sdfCond(); } + +const char * +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); +} + +} // TimingArcSet methods + +%extend LibertyCellTimingArcSetIterator { +bool has_next() { return self->hasNext(); } +TimingArcSet *next() { return self->next(); } +void finish() { delete self; } +} + +%extend TimingArc { +LibertyPort *from() { return self->from(); } +LibertyPort *to() { return self->to(); } +Transition *from_trans() { return self->fromTrans(); } +const char *from_trans_name() { return self->fromTrans()->asRiseFall()->name(); } +Transition *to_trans() { return self->toTrans(); } +const char *to_trans_name() { return self->toTrans()->asRiseFall()->name(); } +TimingRole *role() { return self->role(); } +} // TimingArc methods + +%extend TimingArcSetArcIterator { +bool has_next() { return self->hasNext(); } +TimingArc *next() { return self->next(); } +void finish() { delete self; } +} + +%extend Instance { +Instance *parent() { return cmdLinkedNetwork()->parent(self); } +Cell *cell() { return cmdLinkedNetwork()->cell(self); } +LibertyCell *liberty_cell() { return cmdLinkedNetwork()->libertyCell(self); } +bool is_leaf() { return cmdLinkedNetwork()->isLeaf(self); } +InstanceChildIterator * +child_iterator() { return cmdLinkedNetwork()->childIterator(self); } +InstancePinIterator * +pin_iterator() { return cmdLinkedNetwork()->pinIterator(self); } +InstanceNetIterator * +net_iterator() { return cmdLinkedNetwork()->netIterator(self); } +Pin * +find_pin(const char *name) +{ + return cmdLinkedNetwork()->findPin(self, name); +} +} // Instance methods + +%extend InstanceChildIterator { +bool has_next() { return self->hasNext(); } +Instance *next() { return self->next(); } +void finish() { delete self; } +} // InstanceChildIterator methods + +%extend LeafInstanceIterator { +bool has_next() { return self->hasNext(); } +Instance *next() { return self->next(); } +void finish() { delete self; } +} // LeafInstanceIterator methods + +%extend InstancePinIterator { +bool has_next() { return self->hasNext(); } +Pin *next() { return self->next(); } +void finish() { delete self; } +} // InstancePinIterator methods + +%extend InstanceNetIterator { +bool has_next() { return self->hasNext(); } +Net *next() { return self->next(); } +void finish() { delete self; } +} // InstanceNetIterator methods + +%extend Pin { +const char *port_name() { return cmdLinkedNetwork()->portName(self); } +Instance *instance() { return cmdLinkedNetwork()->instance(self); } +Net *net() { return cmdLinkedNetwork()->net(self); } +Port *port() { return cmdLinkedNetwork()->port(self); } +Term *term() { return cmdLinkedNetwork()->term(self); } +LibertyPort *liberty_port() { return cmdLinkedNetwork()->libertyPort(self); } +bool is_driver() { return cmdLinkedNetwork()->isDriver(self); } +bool is_load() { return cmdLinkedNetwork()->isLoad(self); } +bool is_leaf() { return cmdLinkedNetwork()->isLeaf(self); } +bool is_hierarchical() { return cmdLinkedNetwork()->isHierarchical(self); } +bool is_top_level_port() { return cmdLinkedNetwork()->isTopLevelPort(self); } +PinConnectedPinIterator *connected_pin_iterator() +{ return cmdLinkedNetwork()->connectedPinIterator(self); } + +Vertex ** +vertices() +{ + Vertex *vertex, *vertex_bidirect_drvr; + static Vertex *vertices[3]; + + cmdGraph()->pinVertices(self, vertex, vertex_bidirect_drvr); + vertices[0] = vertex; + vertices[1] = vertex_bidirect_drvr; + vertices[2] = nullptr; + return vertices; +} + +float +capacitance(const TransRiseFall *tr, + const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + float pin_cap, wire_cap; + Sta::sta()->connectedCap(self, tr, corner, min_max, pin_cap, wire_cap); + return pin_cap + wire_cap; +} + +float +pin_capacitance(const TransRiseFall *tr, + const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + float pin_cap, wire_cap; + Sta::sta()->connectedCap(self, tr, corner, min_max, pin_cap, wire_cap); + return pin_cap; +} + +float +wire_capacitance(const TransRiseFall *tr, + const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + float pin_cap, wire_cap; + Sta::sta()->connectedCap(self, tr, corner, min_max, pin_cap, wire_cap); + return wire_cap; +} + +} // Pin methods + +%extend PinConnectedPinIterator { +bool has_next() { return self->hasNext(); } +Pin *next() { return self->next(); } +void finish() { delete self; } +} // PinConnectedPinIterator methods + +%extend Term { +Net *net() { return cmdLinkedNetwork()->net(self); } +Pin *pin() { return cmdLinkedNetwork()->pin(self); } +} // Term methods + +%extend Net { +Instance *instance() { return cmdLinkedNetwork()->instance(self); } +Net *highest_connected_net() +{ return cmdLinkedNetwork()->highestConnectedNet(self); } +NetPinIterator *pin_iterator() { return cmdLinkedNetwork()->pinIterator(self);} +NetTermIterator *term_iterator() {return cmdLinkedNetwork()->termIterator(self);} +NetConnectedPinIterator *connected_pin_iterator() +{ return cmdLinkedNetwork()->connectedPinIterator(self); } +bool is_power() { return cmdLinkedNetwork()->isPower(self);} +bool is_ground() { return cmdLinkedNetwork()->isGround(self);} + +float +capacitance(const TransRiseFall *tr, + const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + float pin_cap, wire_cap; + Sta::sta()->connectedCap(self, tr, corner, min_max, pin_cap, wire_cap); + return pin_cap + wire_cap; +} + +float +pin_capacitance(const TransRiseFall *tr, + const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + float pin_cap, wire_cap; + Sta::sta()->connectedCap(self, tr, corner, min_max, pin_cap, wire_cap); + return pin_cap; +} + +float +wire_capacitance(const TransRiseFall *tr, + const Corner *corner, + const MinMax *min_max) +{ + cmdLinkedNetwork(); + float pin_cap, wire_cap; + Sta::sta()->connectedCap(self, tr, corner, min_max, pin_cap, wire_cap); + return wire_cap; +} + +TmpPortSeq * +ports() +{ + Network *network = cmdLinkedNetwork(); + PortSeq *ports = new PortSeq; + if (network->isTopInstance(network->instance(self))) { + NetTermIterator *term_iter = network->termIterator(self); + while (term_iter->hasNext()) { + Term *term = term_iter->next(); + Port *port = network->port(network->pin(term)); + ports->push_back(port); + } + delete term_iter; + } + return ports; +} + +} // Net methods + +%extend NetPinIterator { +bool has_next() { return self->hasNext(); } +Pin *next() { return self->next(); } +void finish() { delete self; } +} // NetPinIterator methods + +%extend NetTermIterator { +bool has_next() { return self->hasNext(); } +Term *next() { return self->next(); } +void finish() { delete self; } +} // NetTermIterator methods + +%extend NetConnectedPinIterator { +bool has_next() { return self->hasNext(); } +Pin *next() { return self->next(); } +void finish() { delete self; } +} // NetConnectedPinIterator methods + +%extend Clock { +float period() { return self->period(); } +FloatSeq *waveform() { return self->waveform(); } +float time(TransRiseFall *tr) { return self->edge(tr)->time(); } +ClockPinIterator *pin_iterator() { return new ClockPinIterator(self); } +bool is_generated() { return self->isGenerated(); } +bool waveform_valid() { return self->waveformValid(); } +bool is_virtual() { return self->isVirtual(); } +bool is_propagated() { return self->isPropagated(); } +PinSet *sources() { return self->pins(); } + +float +slew(const TransRiseFall *tr, + const MinMax *min_max) +{ + return self->slew(tr, min_max); +} + +} + +%extend ClockEdge { +Clock *clock() { return self->clock(); } +TransRiseFall *transition() { return self->transition(); } +float time() { return self->time(); } +} + +%extend ClockIterator { +bool has_next() { return self->hasNext(); } +Clock *next() { return self->next(); } +void finish() { delete self; } +} + +%extend ClockPinIterator { +bool has_next() { return self->hasNext(); } +Pin *next() { return self->next(); } +void finish() { delete self; } +} + +%extend Vertex { +Pin *pin() { return self->pin(); } +bool is_bidirect_driver() { return self->isBidirectDriver(); } +int level() { return Sta::sta()->vertexLevel(self); } +int tag_group_index() { return self->tagGroupIndex(); } + +TmpFloatSeq * +slews(TransRiseFall *tr) +{ + Sta *sta = Sta::sta(); + TmpFloatSeq *floats = new FloatSeq; + DcalcAnalysisPtIterator ap_iter(sta); + while (ap_iter.hasNext()) { + DcalcAnalysisPt *dcalc_ap = ap_iter.next(); + floats->push_back(delayAsFloat(sta->vertexSlew(self, tr, dcalc_ap))); + } + return floats; +} + +Slew +slew(const TransRiseFall *tr, + const MinMax *min_max) +{ + Sta *sta = Sta::sta(); + return sta->vertexSlew(self, tr, min_max); +} + +VertexOutEdgeIterator * +out_edge_iterator() +{ + return new VertexOutEdgeIterator(self, Sta::sta()->graph()); +} + +VertexInEdgeIterator * +in_edge_iterator() +{ + return new VertexInEdgeIterator(self, Sta::sta()->graph()); +} + +TmpFloatSeq * +arrivals_clk(const TransRiseFall *tr, + Clock *clk, + const TransRiseFall *clk_tr) +{ + Sta *sta = Sta::sta(); + TmpFloatSeq *floats = new FloatSeq; + const ClockEdge *clk_edge = nullptr; + if (clk) + clk_edge = clk->edge(clk_tr); + for (auto path_ap : sta->corners()->pathAnalysisPts()) { + floats->push_back(delayAsFloat(sta->vertexArrival(self, tr, clk_edge, + path_ap))); + } + return floats; +} + +TmpStringSeq * +arrivals_clk_delays(const TransRiseFall *tr, + Clock *clk, + const TransRiseFall *clk_tr, + int digits) +{ + Sta *sta = Sta::sta(); + StringSeq *arrivals = new StringSeq; + const ClockEdge *clk_edge = nullptr; + if (clk) + clk_edge = clk->edge(clk_tr); + for (auto path_ap : sta->corners()->pathAnalysisPts()) { + arrivals->push_back(delayAsString(sta->vertexArrival(self, tr, clk_edge, + path_ap), + sta, digits)); + } + return arrivals; +} + +TmpFloatSeq * +requireds_clk(const TransRiseFall *tr, + Clock *clk, + const TransRiseFall *clk_tr) +{ + Sta *sta = Sta::sta(); + TmpFloatSeq *floats = new FloatSeq; + const ClockEdge *clk_edge = nullptr; + if (clk) + clk_edge = clk->edge(clk_tr); + for (auto path_ap : sta->corners()->pathAnalysisPts()) { + floats->push_back(delayAsFloat(sta->vertexRequired(self, tr, clk_edge, + path_ap))); + } + return floats; +} + +TmpStringSeq * +requireds_clk_delays(const TransRiseFall *tr, + Clock *clk, + const TransRiseFall *clk_tr, + int digits) +{ + Sta *sta = Sta::sta(); + StringSeq *requireds = new StringSeq; + const ClockEdge *clk_edge = nullptr; + if (clk) + clk_edge = clk->edge(clk_tr); + for (auto path_ap : sta->corners()->pathAnalysisPts()) { + requireds->push_back(delayAsString(sta->vertexRequired(self, tr, clk_edge, + path_ap), + sta, digits)); + } + return requireds; +} + +TmpFloatSeq * +slacks(TransRiseFall *tr) +{ + Sta *sta = Sta::sta(); + TmpFloatSeq *floats = new FloatSeq; + for (auto path_ap : sta->corners()->pathAnalysisPts()) { + floats->push_back(delayAsFloat(sta->vertexSlack(self, tr, path_ap))); + } + return floats; +} + +// Slack with respect to a clock rise/fall edge. +TmpFloatSeq * +slacks_clk(const TransRiseFall *tr, + Clock *clk, + const TransRiseFall *clk_tr) +{ + Sta *sta = Sta::sta(); + TmpFloatSeq *floats = new FloatSeq; + const ClockEdge *clk_edge = nullptr; + if (clk) + clk_edge = clk->edge(clk_tr); + for (auto path_ap : sta->corners()->pathAnalysisPts()) { + floats->push_back(delayAsFloat(sta->vertexSlack(self, tr, clk_edge, + path_ap))); + } + return floats; +} + +TmpStringSeq * +slacks_clk_delays(const TransRiseFall *tr, + Clock *clk, + const TransRiseFall *clk_tr, + int digits) +{ + Sta *sta = Sta::sta(); + StringSeq *slacks = new StringSeq; + const ClockEdge *clk_edge = nullptr; + if (clk) + clk_edge = clk->edge(clk_tr); + for (auto path_ap : sta->corners()->pathAnalysisPts()) { + slacks->push_back(delayAsString(sta->vertexSlack(self, tr, clk_edge, + path_ap), + sta, digits)); + } + return slacks; +} + +VertexPathIterator * +path_iterator(const TransRiseFall *tr, + const MinMax *min_max) +{ + return Sta::sta()->vertexPathIterator(self, tr, min_max); +} + +bool +has_downstream_clk_pin() +{ + return self->hasDownstreamClkPin(); +} + +bool +is_clock() +{ + Sta *sta = Sta::sta(); + Search *search = sta->search(); + return search->isClock(self); +} + +} // Vertex methods + +%extend Edge { +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(); } +TimingRole *role() { return self->role(); } +const char *sense() { return timingSenseString(self->sense()); } +TimingArcSetArcIterator * +timing_arc_iterator() { return new TimingArcSetArcIterator(self->timingArcSet()); } +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_cond_default() +{ return Sta::sta()->isDisabledCondDefault(self); } +TmpPinSet * +disabled_constant_pins() { return Sta::sta()->disabledConstantPins(self); } +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 timingSenseString(Sta::sta()->simTimingSense(self));} + +TmpFloatSeq * +arc_delays(TimingArc *arc) +{ + Sta *sta = Sta::sta(); + TmpFloatSeq *floats = new FloatSeq; + DcalcAnalysisPtIterator ap_iter(sta); + while (ap_iter.hasNext()) { + DcalcAnalysisPt *dcalc_ap = ap_iter.next(); + floats->push_back(delayAsFloat(sta->arcDelay(self, arc, dcalc_ap))); + } + return floats; +} + +TmpStringSeq * +arc_delay_strings(TimingArc *arc, + int digits) +{ + Sta *sta = Sta::sta(); + StringSeq *delays = new StringSeq; + DcalcAnalysisPtIterator ap_iter(sta); + while (ap_iter.hasNext()) { + DcalcAnalysisPt *dcalc_ap = ap_iter.next(); + delays->push_back(delayAsString(sta->arcDelay(self, arc, dcalc_ap), + sta, digits)); + } + return delays; +} + +bool +delay_annotated(TimingArc *arc, + const Corner *corner, + const MinMax *min_max) +{ + DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); + return Sta::sta()->arcDelayAnnotated(self, arc, dcalc_ap); +} + +float +arc_delay(TimingArc *arc, + const Corner *corner, + const MinMax *min_max) +{ + DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); + return delayAsFloat(Sta::sta()->arcDelay(self, arc, dcalc_ap)); +} + +const char * +cond() +{ + FuncExpr *cond = self->timingArcSet()->cond(); + if (cond) + return cond->asString(); + else + return nullptr; +} + +const char * +mode_name() +{ + return self->timingArcSet()->modeName(); +} + +const char * +mode_value() +{ + return self->timingArcSet()->modeValue(); +} + +const char * +latch_d_to_q_en() +{ + if (self->role() == TimingRole::latchDtoQ()) { + Sta *sta = Sta::sta(); + const Network *network = sta->cmdNetwork(); + const Graph *graph = sta->graph(); + Pin *from_pin = self->from(graph)->pin(); + Instance *inst = network->instance(from_pin); + LibertyCell *lib_cell = network->libertyCell(inst); + TimingArcSet *d_q_set = self->timingArcSet(); + LibertyPort *enable_port; + FuncExpr *enable_func; + TransRiseFall *enable_tr; + lib_cell->latchEnable(d_q_set, enable_port, enable_func, enable_tr); + const char *en_name = enable_port->name(); + return stringPrintTmp("%s %s", en_name, enable_tr->asString()); + + } + return ""; +} + +} // Edge methods + +%extend VertexIterator { +bool has_next() { return self->hasNext(); } +Vertex *next() { return self->next(); } +void finish() { delete self; } +} + +%extend VertexInEdgeIterator { +bool has_next() { return self->hasNext(); } +Edge *next() { return self->next(); } +void finish() { delete self; } +} + +%extend VertexOutEdgeIterator { +bool has_next() { return self->hasNext(); } +Edge *next() { return self->next(); } +void finish() { delete self; } +} + +%extend PathEnd { +bool is_unconstrained() { return self->isUnconstrained(); } +bool is_check() { return self->isCheck(); } +bool is_latch_check() { return self->isLatchCheck(); } +bool is_data_check() { return self->isDataCheck(); } +bool is_output_delay() { return self->isOutputDelay(); } +bool is_path_delay() { return self->isPathDelay(); } +bool is_gated_clock() { return self->isGatedClock(); } +Vertex *vertex() { return self->vertex(Sta::sta()); } +PathRef *path() { return &self->pathRef(); } +TransRiseFall *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()); } +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()); } +Clock *target_clk() { return self->targetClk(Sta::sta()); } +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 common_clk_pessimism() { return self->commonClkPessimism(Sta::sta()); } +TransRiseFall *target_clk_end_trans() +{ return const_cast(self->targetClkEndTrans(Sta::sta())); } + +} + +%extend MinPulseWidthCheckSeqIterator { +bool has_next() { return self->hasNext(); } +MinPulseWidthCheck *next() { return self->next(); } +void finish() { delete self; } +} // MinPulseWidthCheckSeqIterator methods + +%extend PathRef { +float +arrival() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->arrival(sta)); +} + +float +required() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->required(sta)); +} + +float +slack() +{ + Sta *sta = Sta::sta(); + return delayAsFloat(self->slack(sta)); +} + +Pin * +pin() +{ + Sta *sta = Sta::sta(); + return self->pin(sta); +} + +const char * +tag() +{ + Sta *sta = Sta::sta(); + return self->tag(sta)->asString(sta); +} + +// mea_opt3 +TmpPinSeq * +pins() +{ + Sta *sta = Sta::sta(); + PinSeq *pins = new PinSeq; + PathRef path1(self); + while (!path1.isNull()) { + pins->push_back(path1.vertex(sta)->pin()); + PathRef prev_path; + path1.prevPath(sta, prev_path); + path1.init(prev_path); + } + return pins; +} + +} + +%extend VertexPathIterator { +bool has_next() { return self->hasNext(); } +PathRef * +next() +{ + Path *path = self->next(); + return new PathRef(path); +} + +void finish() { delete self; } +} + +%extend SlowDrvrIterator { +bool has_next() { return self->hasNext(); } +Instance *next() { return self->next(); } +void +finish() +{ + delete self->container(); + delete self; +} + +} + +%extend Corner { +const char *name() { return self->name(); } +} + +// Local Variables: +// mode:c++ +// End: diff --git a/tcl/Util.tcl b/tcl/Util.tcl new file mode 100644 index 0000000..d1bbf8a --- /dev/null +++ b/tcl/Util.tcl @@ -0,0 +1,446 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 sta namespace is used for all commands defined by the sta. +# Use define_cmd_args to define command arguments for the help +# command, and export the command name to the global namespace. +# Global variables must be defined as +# set global_var init_value +# File local variables must be defined as +# variable sta_var init_value + +namespace eval sta { + +# Parse arg_var for keyword/values pairs and flags. +# $key_var(key) -> key_value +# $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} } { + upvar 1 $arg_var args + upvar 1 $key_var key_value + upvar 1 $flag_var flag_present + set args_rtn {} + while { $args != "" } { + set arg [lindex $args 0] + 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 "$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 "$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 "$cmd $arg is not a known keyword or flag." + } else { + lappend args_rtn $arg + } + } + } + } + } else { + lappend args_rtn $arg + } + set args [lrange $args 1 end] + } + set args $args_rtn +} + +# Check for keyword args in arg_var. +proc check_for_key_args { cmd arg_var } { + upvar 1 $arg_var args + set args_rtn {} + while { $args != "" } { + set arg [lindex $args 0] + if { [is_keyword_arg $arg] } { + sta_warn "$cmd $arg is not a known keyword or flag." + } else { + lappend args_rtn $arg + } + set args [lrange $args 1 end] + } + set args $args_rtn +} + +proc is_keyword_arg { arg } { + if { [string length $arg] >= 2 \ + && [string index $arg 0] == "-" \ + && [string is alpha [string index $arg 1]] } { + return 1 + } else { + return 0 + } +} + +################################################################ + +# Define a procedure that checks the args for redirection using unix +# shell redirection syntax. +# 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} }" ] + eval $proc_body +} + +proc parse_redirect_args { arg_var } { + upvar 1 $arg_var args + set argc [llength $args] + if { $argc >= 1 } { + set last_arg [lindex $args [expr $argc - 1]] + # arg >>filename + if { [string range $last_arg 0 1] == ">>" } { + set redirect_file [file nativename [string range $last_arg 2 end]] + set args [lrange $args 0 [expr $argc - 2]] + redirect_file_append_begin $redirect_file + return 1 + # arg >filename + } elseif { [string range $last_arg 0 0] == ">" } { + set redirect_file [file nativename [string range $last_arg 1 end]] + set args [lrange $args 0 [expr $argc - 2]] + redirect_file_begin $redirect_file + return 1 + } + } + if { $argc >= 2 } { + set next_last_arg [lindex $args [expr $argc - 2]] + # arg > filename + if { $next_last_arg == ">" } { + set redirect_file [file nativename [lindex $args end]] + set args [lrange $args 0 [expr $argc - 3]] + redirect_file_begin $redirect_file + return 1 + # arg >> filename + } elseif { $next_last_arg == ">>" } { + set redirect_file [file nativename [lindex $args end]] + set args [lrange $args 0 [expr $argc - 3]] + redirect_file_append_begin $redirect_file + return 1 + } + } + return 0 +} + +################################################################ + +proc define_cmd_args { cmd arglist } { + variable cmd_args + + set cmd_args($cmd) $arglist + namespace export $cmd +} + +# Hidden commands are exported to the global namespace but are not +# shown by the "help" command. +proc define_hidden_cmd_args { cmd arglist } { + namespace export $cmd +} + +# This is used in lieu of command completion to make sdc commands +# like get_ports be abbreviated get_port. +proc define_cmd_alias { alias cmd } { + eval "proc $alias { args } { eval [concat $cmd \$args] }" + namespace export $alias +} + +proc cmd_usage_error { cmd } { + variable cmd_args + + if [info exists cmd_args($cmd)] { + sta_error "Usage: $cmd $cmd_args($cmd)" + } else { + sta_error "Usage: $cmd argument error" + } +} + +################################################################ + +define_cmd_args "help" {[pattern]} + +proc_redirect help { + variable cmd_args + + set arg_count [llength $args] + if { $arg_count == 0 } { + set pattern "*" + } elseif { $arg_count == 1 } { + set pattern [lindex $args 0] + } else { + cmd_usage_error "help" + } + set matches [array names cmd_args $pattern] + if { $matches != {} } { + foreach cmd [lsort $matches] { + show_cmd_args $cmd + } + } else { + sta_warn "no commands match '$pattern'." + } +} + +proc show_cmd_args { cmd } { + variable cmd_args + + set max_col 80 + set indent 2 + set indent_str " " + puts -nonewline $cmd + set col [string length $cmd] + set arglist $cmd_args($cmd) + # Break the arglist up into max_col length lines. + while {1} { + if {[regexp {(^ *)([a-zA-Z0-9>_\|\-]+|\[.*\])(.*)} \ + $arglist ignore space arg rest]} { + set arg_length [string length $arg] + if { $col + $arg_length < $max_col } { + puts -nonewline " $arg" + set col [expr $col + $arg_length + 1] + } else { + puts "" + puts -nonewline "$indent_str $arg" + set col [expr $indent + $arg_length + 1] + } + set arglist $rest + } else { + puts "" + break + } + } +} + +################################################################ + +proc sta_warn { msg } { + variable sdc_file + variable sdc_line + if { [info exists sdc_file] } { + puts "Warning: [file tail $sdc_file], $sdc_line $msg" + } else { + puts "Warning: $msg" + } +} + +proc sta_error { msg } { + variable sdc_file + variable sdc_line + if { [info exists sdc_file] } { + error "Error: [file tail $sdc_file], $sdc_line $msg" + } else { + error "Error: $msg" + } +} + +proc sta_warn_error { warn_error msg } { + if { $warn_error == "warn" } { + sta_warn $msg + } else { + sta_error $msg + } +} + +# Defined by StaTcl.i +define_cmd_args "elapsed_run_time" {} +define_cmd_args "user_run_time" {} + +# Write run time statistics to filename. +proc write_stats { filename } { + if { ![catch {open $filename w} stream] } { + puts $stream "[elapsed_run_time] [user_run_time] [mem]" + close $stream + } +} + +################################################################ + +# Begin/end logging all output to a file. +# Defined by StaTcl.i +define_cmd_args "log_begin" {filename} +define_cmd_args "log_end" {} + +# set_debug is NOT in the global namespace +# because it isn't intended for nosy users. + +################################################################ + +proc check_argc_eq0 { cmd arglist } { + if { $arglist != {} } { + sta_error "$cmd positional arguments not supported." + } +} + +proc check_argc_eq1 { cmd arglist } { + if { [llength $arglist] != 1 } { + sta_error "$cmd requires one positional argument." + } +} + +proc check_argc_eq0or1 { cmd arglist } { + set argc [llength $arglist] + if { $argc != 0 && $argc != 1 } { + sta_error "$cmd requires zero or one positional arguments." + } +} + +proc check_argc_eq2 { cmd arglist } { + if { [llength $arglist] != 2 } { + sta_error "$cmd requires two positional arguments." + } +} + +proc check_argc_eq1or2 { cmd arglist } { + set argc [llength $arglist] + if { $argc != 1 && $argc != 2 } { + sta_error "$cmd requires one or two positional arguments." + } +} + +proc check_argc_eq3 { cmd arglist } { + if { [llength $arglist] != 3 } { + sta_error "$cmd requires three positional arguments." + } +} + +proc check_argc_eq4 { cmd arglist } { + if { [llength $arglist] != 4 } { + sta_error "$cmd requires four positional arguments." + } +} + +################################################################ + +proc check_float { cmd_arg arg } { + if {![string is double $arg]} { + sta_error "$cmd_arg '$arg' is not a float." + } +} + +proc check_positive_float { cmd_arg arg } { + if {!([string is double $arg] && $arg >= 0.0)} { + sta_error "$cmd_arg '$arg' is not a positive float." + } +} + +proc check_integer { cmd_arg arg } { + if {!([string is integer $arg])} { + sta_error "$cmd_arg '$arg' is not an integer." + } +} + +proc check_positive_integer { cmd_arg arg } { + if {!([string is integer $arg] && $arg >= 0)} { + sta_error "$cmd_arg '$arg' is not a positive integer." + } +} + +proc check_cardinal { cmd_arg arg } { + if {!([string is integer $arg] && $arg >= 1)} { + sta_error "$cmd_arg '$arg' is not an integer greater than or equal to one." + } +} + +proc check_percent { cmd_arg arg } { + if {!([string is double $arg] && $arg >= 0.0 && $arg <= 100.0)} { + sta_error "$cmd_arg '$arg' is not between 0 and 100." + } +} + +# sta namespace end +} + +################################################################ + +# The builtin Tcl "source" and "unknown" commands are redefined by sta. +# This rename provides a mechanism to refer to the original TCL +# command. +rename source builtin_source + +# This seems to be a common build problem. +# It happens with incomplete installs of libtcl that can't find the +# corresponding init.tcl file. +if { [info procs unknown] == {} } { + puts "TCL installation problem. proc unknown not defined. Check tcl_libraries $tcl_libraries for init.tcl" + proc unknown { args } { return -code error "invalid command name" } +} else { + rename unknown builtin_unknown +} + +# Numeric expressions eval to themselves so braces aren't required +# around bus names like foo[2] or foo[*]. +proc sta_unknown { args } { + global errorCode errorInfo + + set name [lindex $args 0] + if { [llength $args] == 1 \ + && ([string is integer $name] || [string equal $name "*"]) } { + return "\[$args\]" + } else { + # Implement command name abbreviation from init.tcl/unknown. + # Remove restrictions in that version that prevent it from + # running in non-interactive interpreters. + + set ret [catch {set cmds [info commands $name*]} msg] + if {[string equal $name "::"]} { + set name "" + } + if {$ret != 0} { + return -code $ret -errorcode $errorCode \ + "error in unknown while checking if \"$name\" is a unique command abbreviation: $msg" + } + if {[llength $cmds] == 1} { + return [uplevel 1 [lreplace $args 0 0 $cmds]] + } + if {[llength $cmds]} { + if {[string equal $name ""]} { + return -code error "empty command name \"\"" + } else { + return -code error \ + "ambiguous command name \"$name\": [lsort $cmds]" + } + } else { + return [uplevel 1 builtin_unknown $args] + } + } +} + +namespace unknown sta_unknown diff --git a/tcl/Variables.tcl b/tcl/Variables.tcl new file mode 100644 index 0000000..46facd2 --- /dev/null +++ b/tcl/Variables.tcl @@ -0,0 +1,193 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +namespace eval sta { + +################################################################ +# +# Variables +# +################################################################ + +trace variable ::link_make_black_boxes "rw" \ + sta::trace_link_make_black_boxes + +proc trace_link_make_black_boxes { name1 name2 op } { + trace_boolean_var $op ::link_make_black_boxes \ + link_make_black_boxes set_link_make_black_boxes +} + +# Default digits to print after decimal point for reporting commands. +set ::sta_report_default_digits 2 + +trace variable ::sta_report_default_digits "rw" \ + sta::trace_report_default_digits + +proc trace_report_default_digits { name1 name2 op } { + global sta_report_default_digits + + if { $op == "w" } { + if { !([string is integer $sta_report_default_digits] \ + && $sta_report_default_digits >= 0) } { + sta_error "sta_report_default_digits must be a positive integer." + } + } +} + +trace variable ::sta_crpr_enabled "rw" \ + sta::trace_crpr_enabled + +proc trace_crpr_enabled { name1 name2 op } { + trace_boolean_var $op ::sta_crpr_enabled \ + crpr_enabled set_crpr_enabled +} + +trace variable ::sta_crpr_mode "rw" \ + sta::trace_crpr_mode + +proc trace_crpr_mode { name1 name2 op } { + global sta_crpr_mode + + if { $op == "r" } { + set sta_crpr_mode [crpr_mode] + } elseif { $op == "w" } { + if { $sta_crpr_mode == "same_pin" || $sta_crpr_mode == "same_transition" } { + set_crpr_mode $sta_crpr_mode + } else { + sta_error "sta_crpr_mode must be pin or transition." + } + } +} + +trace variable ::sta_cond_default_arcs_enabled "rw" \ + sta::trace_cond_default_arcs_enabled + +proc trace_cond_default_arcs_enabled { name1 name2 op } { + trace_boolean_var $op ::sta_cond_default_arcs_enabled \ + cond_default_arcs_enabled set_cond_default_arcs_enabled +} + +trace variable ::sta_gated_clock_checks_enabled "rw" \ + sta::trace_gated_clk_checks_enabled + +proc trace_gated_clk_checks_enabled { name1 name2 op } { + trace_boolean_var $op ::sta_gated_clock_checks_enabled \ + gated_clk_checks_enabled set_gated_clk_checks_enabled +} + +trace variable ::sta_internal_bidirect_instance_paths_enabled "rw" \ + sta::trace_internal_bidirect_instance_paths_enabled + +proc trace_internal_bidirect_instance_paths_enabled { name1 name2 op } { + trace_boolean_var $op ::sta_internal_bidirect_instance_paths_enabled \ + 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" \ + sta::trace_clock_through_tristate_enabled + +proc trace_clock_through_tristate_enabled { name1 name2 op } { + trace_boolean_var $op ::sta_clock_through_tristate_enabled \ + clk_thru_tristate_enabled set_clk_thru_tristate_enabled +} + +trace variable ::sta_preset_clear_arcs_enabled "rw" \ + sta::trace_preset_clr_arcs_enabled + +proc trace_preset_clr_arcs_enabled { name1 name2 op } { + trace_boolean_var $op ::sta_preset_clear_arcs_enabled \ + preset_clr_arcs_enabled set_preset_clr_arcs_enabled +} + +trace variable ::sta_disable_recovery_removal_checks "rw" \ + sta::trace_disable_recovery_removal_checks + +proc trace_disable_recovery_removal_checks { name1 name2 op } { + trace_bool_var $op ::sta_disable_recovery_removal_checks \ + recovery_removal_checks_enabled set_recovery_removal_checks_enabled +} + +trace variable ::sta_dynamic_loop_breaking "rw" \ + sta::trace_dynamic_loop_breaking + +proc trace_dynamic_loop_breaking { name1 name2 op } { + trace_boolean_var $op ::sta_dynamic_loop_breaking \ + dynamic_loop_breaking set_dynamic_loop_breaking +} + +trace variable ::sta_input_port_default_clock "rw" \ + sta::trace_input_port_default_clock + +proc trace_input_port_default_clock { name1 name2 op } { + trace_boolean_var $op ::sta_input_port_default_clock \ + use_default_arrival_clock set_use_default_arrival_clock +} + +trace variable ::sta_propagate_all_clocks "rw" \ + sta::trace_propagate_all_clocks + +proc trace_propagate_all_clocks { name1 name2 op } { + trace_boolean_var $op ::sta_propagate_all_clocks \ + propagate_all_clocks set_propagate_all_clocks +} + +trace variable ::sta_propagate_gated_clock_enable "rw" \ + sta::trace_propagate_gated_clock_enable + +proc trace_propagate_gated_clock_enable { name1 name2 op } { + trace_boolean_var $op ::sta_propagate_gated_clock_enable \ + propagate_gated_clock_enable set_propagate_gated_clock_enable +} + +trace variable ::sta_pocv_enabled "rw" \ + sta::trace_pocv_enabled + +proc trace_pocv_enabled { name1 name2 op } { + trace_boolean_var $op ::sta_pocv_enabled \ + pocv_enabled set_pocv_enabled +} + +# Report path numeric field width is digits + extra. +set report_path_field_width_extra 5 + +################################################################ + +proc trace_boolean_var { op var_name get_proc set_proc } { + upvar 1 $var_name var + + if { $op == "r" } { + set var [$get_proc] + } elseif { $op == "w" } { + if { $var == 0 } { + $set_proc 0 + } elseif { $var == 1 } { + $set_proc 1 + } else { + sta_error "$var_name value must be 0 or 1." + } + } +} + +# sta namespace end. +} diff --git a/test/example1.ok b/test/example1.ok new file mode 100644 index 0000000..79f8574 --- /dev/null +++ b/test/example1.ok @@ -0,0 +1,29 @@ +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 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CK (DFF_X1) + 1.10 1.10 v r2/Q (DFF_X1) + 1.10 2.20 v u1/Z (BUF_X1) + 1.10 3.30 v u2/ZN (AND2_X1) + 0.00 3.30 v r3/D (DFF_X1) + 3.30 data arrival time + + 10.00 10.00 clock clk (rise edge) + 0.00 10.00 clock network delay (ideal) + 0.00 10.00 clock reconvergence pessimism + 10.00 ^ r3/CK (DFF_X1) + -0.50 9.50 library setup time + 9.50 data required time +--------------------------------------------------------- + 9.50 data required time + -3.30 data arrival time +--------------------------------------------------------- + 6.20 slack (MET) + + diff --git a/test/example2.ok b/test/example2.ok new file mode 100644 index 0000000..d08def8 --- /dev/null +++ b/test/example2.ok @@ -0,0 +1,29 @@ +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 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CK (DFF_X1) + 0.23 0.23 v r2/Q (DFF_X1) + 0.08 0.32 v u1/Z (BUF_X1) + 0.10 0.42 v u2/ZN (AND2_X1) + 0.00 0.42 v r3/D (DFF_X1) + 0.42 data arrival time + + 10.00 10.00 clock clk (rise edge) + 0.00 10.00 clock network delay (ideal) + 0.00 10.00 clock reconvergence pessimism + 10.00 ^ r3/CK (DFF_X1) + -0.16 9.84 library setup time + 9.84 data required time +--------------------------------------------------------- + 9.84 data required time + -0.42 data arrival time +--------------------------------------------------------- + 9.43 slack (MET) + + diff --git a/test/example3.ok b/test/example3.ok new file mode 100644 index 0000000..0d6c9c4 --- /dev/null +++ b/test/example3.ok @@ -0,0 +1,56 @@ +Startpoint: in1 (input port clocked by clk) +Endpoint: r1 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path Type: min + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 v input external delay + 0.00 0.00 v in1 (in) + 0.00 0.00 v r1/D (DFF_X1) + 0.00 data arrival time + + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 clock reconvergence pessimism + 0.00 ^ r1/CK (DFF_X1) + 0.00 0.00 library hold time + 0.00 data required time +--------------------------------------------------------- + 0.00 data required time + -0.00 data arrival time +--------------------------------------------------------- + 0.00 slack (VIOLATED) + + +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 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CK (DFF_X1) + 0.23 0.23 v r2/Q (DFF_X1) + 0.08 0.32 v u1/Z (BUF_X1) + 0.10 0.42 v u2/ZN (AND2_X1) + 0.00 0.42 v r3/D (DFF_X1) + 0.42 data arrival time + + 10.00 10.00 clock clk (rise edge) + 0.00 10.00 clock network delay (ideal) + 0.00 10.00 clock reconvergence pessimism + 10.00 ^ r3/CK (DFF_X1) + -0.16 9.84 library setup time + 9.84 data required time +--------------------------------------------------------- + 9.84 data required time + -0.42 data arrival time +--------------------------------------------------------- + 9.43 slack (MET) + + diff --git a/test/example4.ok b/test/example4.ok new file mode 100644 index 0000000..a8c4faa --- /dev/null +++ b/test/example4.ok @@ -0,0 +1,29 @@ +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 + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CK (DFF_X1) + 2.58 2.58 ^ r2/Q (DFF_X1) + 2.59 5.17 ^ u1/Z (BUF_X1) + 2.77 7.95 ^ u2/ZN (AND2_X1) + 0.00 7.95 ^ r3/D (DFF_X1) + 7.95 data arrival time + + 10.00 10.00 clock clk (rise edge) + 0.00 10.00 clock network delay (ideal) + 0.00 10.00 clock reconvergence pessimism + 10.00 ^ r3/CK (DFF_X1) + -0.61 9.39 library setup time + 9.39 data required time +--------------------------------------------------------- + 9.39 data required time + -7.95 data arrival time +--------------------------------------------------------- + 1.45 slack (MET) + + diff --git a/test/example5.ok b/test/example5.ok new file mode 100644 index 0000000..8f0e82b --- /dev/null +++ b/test/example5.ok @@ -0,0 +1,88 @@ +Startpoint: in1 (input port clocked by clk) +Endpoint: r1 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path Type: min +Corner: ss + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ input external delay + 0.00 0.00 ^ in1 (in) + 0.00 0.00 ^ r1/D (DFF_X1) + 0.00 data arrival time + + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 clock reconvergence pessimism + 0.00 ^ r1/CK (DFF_X1) + 0.01 0.01 library hold time + 0.01 data required time +--------------------------------------------------------- + 0.01 data required time + -0.00 data arrival time +--------------------------------------------------------- + -0.01 slack (VIOLATED) + + +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 +Corner: ss + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CK (DFF_X1) + 0.26 0.26 v r2/Q (DFF_X1) + 0.09 0.35 v u1/Z (BUF_X1) + 0.11 0.46 v u2/ZN (AND2_X1) + 0.00 0.46 v r3/D (DFF_X1) + 0.46 data arrival time + + 10.00 10.00 clock clk (rise edge) + 0.00 10.00 clock network delay (ideal) + 0.00 10.00 clock reconvergence pessimism + 10.00 ^ r3/CK (DFF_X1) + -0.16 9.84 library setup time + 9.84 data required time +--------------------------------------------------------- + 9.84 data required time + -0.46 data arrival time +--------------------------------------------------------- + 9.38 slack (MET) + + +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 +Corner: tt + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CK (DFF_X1) + 0.09 0.09 v r2/Q (DFF_X1) + 0.03 0.11 v u1/Z (BUF_X1) + 0.03 0.14 v u2/ZN (AND2_X1) + 0.00 0.14 v r3/D (DFF_X1) + 0.14 data arrival time + + 10.00 10.00 clock clk (rise edge) + 0.00 10.00 clock network delay (ideal) + 0.00 10.00 clock reconvergence pessimism + 10.00 ^ r3/CK (DFF_X1) + -0.04 9.96 library setup time + 9.96 data required time +--------------------------------------------------------- + 9.96 data required time + -0.14 data arrival time +--------------------------------------------------------- + 9.82 slack (MET) + + diff --git a/test/find_parent_dir.tcl b/test/find_parent_dir.tcl new file mode 100644 index 0000000..fe77550 --- /dev/null +++ b/test/find_parent_dir.tcl @@ -0,0 +1,34 @@ +#! /bin/sh +# The next line is executed by /bin/sh, but not Tcl \ +exec tclsh $0 ${1+"$@"} + +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +proc find_parent_dir { dir } { + if { $dir == "." } { + return ".." + } else { + set path [file split $dir] + set path_len [llength $path] + if { $path_len == 1 } { + return "." + } else { + set path_len2 [expr $path_len - 2] + return [eval file join [lrange $path 0 $path_len2]] + } + } +} diff --git a/test/regression b/test/regression new file mode 100755 index 0000000..577b7af --- /dev/null +++ b/test/regression @@ -0,0 +1,34 @@ +#!/bin/sh +# The next line is executed by /bin/sh, but not Tcl \ +exec tclsh $0 ${1+"$@"} + +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +# Usage: regression help | test1 [test2...] +# where test is all or the name of a tcl script in $STA/test + +# Directory containing tests. +set test_dir [file dirname [file normalize [info script]]] +source [file join $test_dir find_parent_dir.tcl] +source [file join $test_dir regression_vars.tcl] +source [file join $test_dir regression.tcl] + +regression_main + +# Local Variables: +# mode:tcl +# End: diff --git a/test/regression.tcl b/test/regression.tcl new file mode 100755 index 0000000..e40e492 --- /dev/null +++ b/test/regression.tcl @@ -0,0 +1,508 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +proc regression_main {} { + setup + parse_args + run_tests + show_summary + exit [found_errors] +} + +proc setup {} { + global result_dir diff_file failure_file errors + global use_valgrind valgrind_shared_lib_failure + + set use_valgrind 0 + + if { !([file exists $result_dir] && [file isdirectory $result_dir]) } { + file mkdir $result_dir + } + file delete $diff_file + file delete $failure_file + + set errors(error) 0 + set errors(memory) 0 + set errors(leak) 0 + set errors(race) 0 + set errors(slow) 0 + set errors(fast) 0 + set errors(big) 0 + set errors(small) 0 + set errors(fail) 0 + set errors(no_cmd) 0 + set errors(no_ok) 0 + set valgrind_shared_lib_failure 0 +} + +proc parse_args {} { + global argv app_options tests test_groups cmd_paths + global use_valgrind + global result_dir tests + + while { $argv != {} } { + set arg [lindex $argv 0] + if { $arg == "help" || $arg == "-help" } { + puts {Usage: regression [-help] [-threads threads] [-valgrind] tests...} + puts " -threads max|integer - number of threads to use" + puts " -valgrind - run valgrind (linux memory checker)" + puts " Wildcarding for test names is supported (enclose in \"'s)" + puts " Tests are: all, fast, med, slow, or a test group or test name" + puts "" + puts " If 'limit coredumpsize unlimited' corefiles are saved in $result_dir/test.core" + exit + } 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 + } + lappend app_options "-threads" + lappend app_options $threads + set argv [lrange $argv 2 end] + } elseif { $arg == "-valgrind" } { + set use_valgrind 1 + set argv [lrange $argv 1 end] + } elseif { [llength $argv] == 0 } { + # Default is to run dist tests. + set tests [group_tests dist] + } else { + break + } + } + set tests [expand_tests $argv] +} + +proc expand_tests { argv } { + global test_groups + + set tests {} + foreach arg $argv { + if { [info exists test_groups($arg)] } { + set tests [concat $tests $test_groups($arg)] + } elseif { [string first "*" $arg] != -1 \ + || [string first "?" $arg] != -1 } { + # Find wildcard matches. + foreach test [group_tests "all"] { + if [string match $arg $test] { + lappend tests $test + } + } + } elseif { [lsearch [group_tests "all"] $arg] != -1 } { + lappend tests $arg + } else { + puts "Error: test $arg not found." + } + } + return $tests +} + +proc run_tests {} { + global tests errors app_path + + foreach test $tests { + run_test $test + } + # Macos debug info generated by valgrind. + file delete -force "$app_path.dSYM" +} + +proc run_test { test } { + global result_dir diff_file errors diff_options + + set cmd_file [test_cmd_file $test] + if [file exists $cmd_file] { + set ok_file [test_ok_file $test] + set log_file [test_log_file $test] + foreach file [glob -nocomplain [file join $result_dir $test.*]] { + file delete -force $file + } + puts -nonewline $test + flush stdout + set test_errors [run_test_app $test $cmd_file $log_file] + if { [lindex $test_errors 0] == "ERROR" } { + puts " *ERROR* [lrange $test_errors 1 end]" + append_failure $test + incr errors(error) + + # For some reason seg faults aren't echoed in the log - add them. + if [file exists $log_file] { + set log_ch [open $log_file "a"] + puts $log_ch "$test_errors" + close $log_ch + } + + # Report partial log diff anyway. + if [file exists $ok_file] { + catch [concat exec diff $diff_options $ok_file $log_file \ + >> $diff_file] + } + } else { + set error_msg "" + if { [lsearch $test_errors "MEMORY"] != -1 } { + append error_msg " *MEMORY*" + append_failure $test + incr errors(memory) + } + if { [lsearch $test_errors "LEAK"] != -1 } { + append error_msg " *LEAK*" + append_failure $test + incr errors(leak) + } + if { [lsearch $test_errors "RACE"] != -1 } { + append error_msg " *RACE*" + append_failure $test + incr errors(race) + } + if { [lsearch $test_errors "SLOW"] != -1 } { + append error_msg " *SLOW*" + incr errors(slow) + } + if { [lsearch $test_errors "FAST"] != -1 } { + append error_msg " *FAST*" + incr errors(fast) + } + if { [lsearch $test_errors "BIG"] != -1 } { + append error_msg " *BIG*" + incr errors(big) + } + if { [lsearch $test_errors "SMALL"] != -1 } { + append error_msg " *SMALL*" + incr errors(small) + } + + if [file exists $ok_file] { + # Filter dos '/r's from log file. + set tmp_file [file join $result_dir $test.tmp] + exec tr -d "\r" < $log_file > $tmp_file + file rename -force $tmp_file $log_file + if [catch [concat exec diff $diff_options $ok_file $log_file \ + >> $diff_file]] { + puts " *FAIL*$error_msg" + append_failure $test + incr errors(fail) + } else { + puts " pass$error_msg" + } + } else { + puts " *NO OK FILE*$error_msg" + append_failure $test + incr errors(no_ok) + } + } + } else { + puts "$test *NO CMD FILE*" + incr errors(no_cmd) + } +} + +proc append_failure { test } { + global failure_file + set fail_ch [open $failure_file "a"] + puts $fail_ch $test + close $fail_ch +} + +# Return error. +proc run_test_app { test cmd_file log_file } { + global app_path errorCode use_valgrind + if { $use_valgrind } { + return [run_test_valgrind $test $cmd_file $log_file] + } else { + return [run_test_plain $test $cmd_file $log_file] + } +} + +proc run_test_plain { test cmd_file log_file } { + global app_path app_options result_dir errorCode + + if { ![file exists $app_path] } { + return "ERROR $app_path not found." + } elseif { ![file executable $app_path] } { + return "ERROR $app_path is not executable." + } else { + set save_dir [pwd] + cd [file dirname $cmd_file] + if { [catch [concat exec $app_path $app_options \ + [file tail $cmd_file] >& $log_file]] } { + cd $save_dir + set signal [lindex $errorCode 2] + set error [lindex $errorCode 3] + # Errors strings are not consistent across platforms but signal + # names are. + if { $signal == "SIGSEGV" } { + # Save corefiles to regression results directory. + set pid [lindex $errorCode 1] + set sys_corefile [test_sys_core_file $test $pid] + if { [file exists $sys_corefile] } { + file copy $sys_corefile [test_core_file $test] + } + } + cleanse_logfile $test $log_file + return "ERROR $error" + } + cd $save_dir + cleanse_logfile $test $log_file + return "" + } +} + +proc run_test_valgrind { test cmd_file log_file } { + global app_path app_options valgrind_options result_dir errorCode + + set vg_cmd_file [test_valgrind_cmd_file $test] + set vg_stream [open $vg_cmd_file "w"] + puts $vg_stream "cd [file dirname $cmd_file]" + puts $vg_stream "source [file tail $cmd_file]" + puts $vg_stream "sta::delete_all_memory" + close $vg_stream + + set cmd [concat exec valgrind $valgrind_options \ + $app_path $app_options $vg_cmd_file >& $log_file] + if { [catch $cmd] } { + set error [lindex $errorCode 3] + cleanse_valgrind_logfile $test $log_file + cleanse_logfile $test $log_file + return "ERROR $error" + } else { + cleanse_logfile $test $log_file + return [cleanse_valgrind_logfile $test $log_file] + } +} + +# Error messages can be found in "valgrind/memcheck/mc_errcontext.c". +# +# "Conditional jump or move depends on uninitialised value(s)" +# "%s contains unaddressable byte(s)" +# "%s contains uninitialised or unaddressable byte(s)" +# "Use of uninitialised value of size %d" +# "Invalid read of size %d" +# "Syscall param %s contains uninitialised or unaddressable byte(s)" +# "Unaddressable byte(s) found during client check request" +# "Uninitialised or unaddressable byte(s) found during client check request" +# "Invalid free() / delete / delete[]" +# "Mismatched free() / delete / delete []" +set valgrind_mem_regexp "(depends on uninitialised value)|(contains unaddressable)|(contains uninitialised)|(Use of uninitialised value)|(Invalid read)|(Unaddressable byte)|(Uninitialised or unaddressable)|(Invalid free)|(Mismatched free)" + +# "%d bytes in %d blocks are definitely lost in loss record %d of %d" +# "%d bytes in %d blocks are possibly lost in loss record %d of %d" +#set valgrind_leak_regexp "blocks are (possibly|definitely) lost" +set valgrind_leak_regexp "blocks are definitely lost" + +# Valgrind fails on executables using shared libraries. +set valgrind_shared_lib_failure_regexp "No malloc'd blocks -- no leaks are possible" + +# Scan the log file to separate valgrind notifications and check for +# valgrind errors. +proc cleanse_valgrind_logfile { test log_file } { + global valgrind_mem_regexp valgrind_leak_regexp + global valgrind_shared_lib_failure_regexp + global valgrind_shared_lib_failure + + set tmp_file [test_tmp_file $test] + set valgrind_log_file [test_valgrind_file $test] + file copy -force $log_file $tmp_file + set tmp [open $tmp_file "r"] + set log [open $log_file "w"] + set valgrind [open $valgrind_log_file "w"] + set leaks 0 + set mem_errors 0 + gets $tmp line + while { ![eof $tmp] } { + if {[regexp "^==" $line]} { + puts $valgrind $line + if {[regexp $valgrind_leak_regexp $line]} { + set leaks 1 + } + if {[regexp $valgrind_mem_regexp $line]} { + set mem_errors 1 + } + if {[regexp $valgrind_shared_lib_failure_regexp $line]} { + set valgrind_shared_lib_failure 1 + } + } elseif {[regexp {^--[0-9]+} $line]} { + # Valgrind notification line. + } else { + puts $log $line + } + gets $tmp line + } + close $log + close $tmp + close $valgrind + file delete $tmp_file + + set errors {} + if { $mem_errors } { + lappend errors "MEMORY" + } + if { $leaks } { + lappend errors "LEAK" + } + return $errors +} + +################################################################ + +proc show_summary {} { + global errors tests diff_file result_dir valgrind_shared_lib_failure + global app_path app + + puts "------------------------------------------------------" + set test_count [llength $tests] + if { [found_errors] } { + if { $errors(error) != 0 } { + puts "Errored $errors(error)/$test_count" + } + if { $errors(fail) != 0 } { + puts "Failed $errors(fail)/$test_count" + } + if { $errors(leak) != 0 } { + puts "Memory leaks in $errors(leak)/$test_count" + } + if { $errors(memory) != 0 } { + puts "Memory corruption in $errors(memory)/$test_count" + } + if { $errors(race) != 0 } { + puts "Race errors in $errors(race)/$test_count" + } + if { $errors(no_ok) != 0 } { + puts "No ok file for $errors(no_ok)/$test_count" + } + if { $errors(no_cmd) != 0 } { + puts "No cmd tcl file for $errors(no_cmd)/$test_count" + } + if { $errors(fail) != 0 } { + puts "See $diff_file for differences" + } + } 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 {} { + global errors + + return [expr $errors(error) != 0 || $errors(fail) != 0 \ + || $errors(no_cmd) != 0 || $errors(no_ok) != 0 \ + || $errors(memory) != 0 || $errors(leak) != 0 \ + || $errors(race) != 0] +} + +################################################################ + +proc save_ok_main {} { + global argv + if { $argv == "help" || $argv == "-help" } { + puts {Usage: save_ok [failures] test1 [test2]...} + } elseif { $argv == "failures" } { + global failure_file + if [file exists $failure_file] { + set fail_ch [open $failure_file "r"] + while { ! [eof $fail_ch] } { + set test [gets $fail_ch] + if { $test != "" } { + save_ok $test + } + } + close $fail_ch + } + } else { + foreach test $argv { + save_ok $test + } + } +} + +proc save_ok { test } { + if { [lsearch [group_tests "all"] $test] == -1 } { + puts "Error: test $test 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 + } + } +} + +################################################################ + +proc test_cmd_dir { test } { + global cmd_dirs + + if {[info exists cmd_dirs($test)]} { + return $cmd_dirs($test) + } else { + return "" + } +} + +proc test_cmd_file { test } { + return [file join [test_cmd_dir $test] "$test.tcl"] +} + +proc test_ok_file { test } { + global test_dir + return [file join $test_dir "$test.ok"] +} + +proc test_log_file { test } { + global result_dir + return [file join $result_dir "$test.log"] +} + +proc test_tmp_file { test } { + global result_dir + return [file join $result_dir $test.tmp] +} + +proc test_valgrind_cmd_file { test } { + global result_dir + return [file join $result_dir $test.vg_cmd] +} + +proc test_valgrind_file { test } { + global result_dir + return [file join $result_dir $test.valgrind] +} + +proc test_core_file { test } { + global result_dir + return [file join $result_dir $test.core] +} + +proc test_sys_core_file { test pid } { + global cmd_dirs + + # macos + # return [file join "/cores" "core.$pid"] + + # Suse + return [file join [test_cmd_dir $test] "core"] +} + +################################################################ + +# Local Variables: +# mode:tcl +# End: diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl new file mode 100644 index 0000000..c3484c6 --- /dev/null +++ b/test/regression_vars.tcl @@ -0,0 +1,115 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +# Regression variables. + +# Application program to run tests on. +if { [regexp "CYGWIN" [exec uname -a]] } { + set app "sta.exe" +} else { + set app "sta" +} +set sta_dir [find_parent_dir $test_dir] +set app_path [file join $sta_dir "app" $app] +# Application options. +set app_options "-no_init -no_splash -exit" +# Log files for each test are placed in result_dir. +set result_dir [file join $test_dir "results"] +# Collective diffs. +set diff_file [file join $result_dir "diffs"] +# File containing list of failed tests. +set failure_file [file join $result_dir "failures"] +# Use the DIFF_OPTIONS envar to change the diff options +# (Solaris diff doesn't support this envar) +set diff_options "-c" +if [info exists env(DIFF_OPTIONS)] { + set diff_options $env(DIFF_OPTIONS) +} + +set valgrind_suppress [file join $test_dir valgrind.suppress] +set valgrind_options "--num-callers=20 --leak-check=full --freelist-vol=100000000 --leak-resolution=high --suppressions=$valgrind_suppress" +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. +proc record_test { test cmd_dir } { + global cmd_dirs test_groups + set cmd_dirs($test) $cmd_dir + lappend test_groups(all) $test + return $test +} + +# Record a test in the $STA/examples directory. +proc record_example_tests { tests } { + global test_dir test_groups + set example_dir [file join $test_dir ".." "examples"] + foreach test $tests { + # Prune commented tests from the list. + if { [string index $test 0] != "#" } { + record_test $test $example_dir + } + } +} + +################################################################ + +proc define_test_group { name tests } { + global test_groups + set test_groups($name) $tests +} + +proc group_tests { name } { + global test_groups + return $test_groups($name) +} + +# Clear the test lists. +proc clear_tests {} { + global test_groups + unset test_groups +} + +proc list_delete { list delete } { + set result {} + foreach item $list { + if { [lsearch $delete $item] == -1 } { + lappend result $item + } + } + return $result +} + +################################################################ + +# Regression test lists. + +# Record tests in sta/examples +record_example_tests { + example1 + example2 + example3 + example4 + example5 +} + +define_test_group fast [group_tests all] diff --git a/test/save_ok b/test/save_ok new file mode 100755 index 0000000..d1277d3 --- /dev/null +++ b/test/save_ok @@ -0,0 +1,32 @@ +#! /bin/sh +# The next line is executed by /bin/sh, but not Tcl \ +exec tclsh $0 ${1+"$@"} + +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +# Directory containing tests. +set test_dir [file dirname [info script]] +source [file join $test_dir find_parent_dir.tcl] +set sta_dir [find_parent_dir $test_dir] +source [file join $test_dir regression_vars.tcl] +source [file join $test_dir regression.tcl] + +save_ok_main + +# Local Variables: +# mode:tcl +# End: diff --git a/test/valgrind.suppress b/test/valgrind.suppress new file mode 100644 index 0000000..75bf405 --- /dev/null +++ b/test/valgrind.suppress @@ -0,0 +1,155 @@ +{ + SuSE 12.1 + Memcheck:Leak + fun:malloc + obj:/usr/lib64/libtcl8.5.so +} +{ + SuSE 12.1 + Memcheck:Leak + fun:realloc + obj:/usr/lib64/libtcl8.5.so +} +{ + MACOS 10.6.4 + Memcheck:Leak + fun:malloc + fun:TclpAlloc +} +{ + MACOS 10.6.4 + Memcheck:Leak + fun:malloc + fun:SWIG_Tcl_NewInstanceObj +} +{ + MACOS 10.6.4 + Memcheck:Leak + fun:malloc + fun:Tcl_GetMemoryInfo +} +{ + MACOS 10.6.4 + Memcheck:Leak + fun:malloc_zone_malloc + ... + fun:TclpSetInitialEncodings +} +{ + MACOS 10.6.4 + Memcheck:Leak + fun:malloc_zone_malloc + ... + fun:CFPreferencesCopyAppValue +} +{ + MACOS 10.6.4 + Memcheck:Cond + obj:/usr/lib/libSystem.B.dylib +} +{ + MACOS 10.6.4 + Memcheck:Cond + fun:pthread_rwlock_init +} +{ + MACOS 10.6.4 + Memcheck:Cond + fun:Tcl_GetVersion +} +{ + MACOS 10.6.4 + Memcheck:Cond + fun:TclObjInvoke +} + +{ + MACOS 10.10.1 + Memcheck:Leak + fun:malloc + obj:/usr/lib/libicucore.A.dylib +} +{ + MACOS 10.10.1 + Memcheck:Leak + fun:calloc + obj:/usr/lib/libobjc.A.dylib +} +{ + MACOS 10.10.1 + Memcheck:Leak + fun:malloc_zone_malloc + obj:/usr/lib/libobjc.A.dylib +} +{ + MACOS 10.10.1 + Memcheck:Leak + fun:malloc_zone_calloc + obj:/usr/lib/libobjc.A.dylib +} +{ + MACOS 10.10.1 + Memcheck:Leak + fun:malloc + ... + obj:/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation +} +{ + MACOS 10.10.1 + Memcheck:Leak + fun:malloc_zone_malloc + obj:/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation +} +{ + MACOS 10.10.1 + Memcheck:Cond + fun:Tcl_AppendObjToErrorInfo +} + +{ + Ubuntu 11.10 + Memcheck:Leak + fun:malloc + ... + fun:Tcl_CreateInterp +} +{ + Ubuntu 11.10 + Memcheck:Leak + fun:malloc + ... + fun:Tcl_NewStringObj +} +{ + Ubuntu 11.10 + Memcheck:Leak + fun:malloc + ... + fun:TclSetByteCodeFromAny +} +{ + Ubuntu 11.10 + Memcheck:Leak + fun:malloc + ... + fun:TclObjInterpProcCore +} +{ + Ubuntu 11.10 + Memcheck:Cond + fun:inflateReset2 +} +{ + Ubuntu 11.10 + Memcheck:Leak + fun:malloc + ... + fun:Tcl_NewObj +} +{ + Ubuntu 11.10 + Memcheck:Leak + fun:malloc + ... + fun:TclNRRunCallbacks +} diff --git a/util/Condition.cc b/util/Condition.cc new file mode 100644 index 0000000..3e531f8 --- /dev/null +++ b/util/Condition.cc @@ -0,0 +1,58 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Condition.hh" +#include "ThreadException.hh" +#include "Mutex.hh" + +namespace sta { + +Condition::Condition() +{ + int error = pthread_cond_init(&condition_, NULL); + CheckThreadError(error); +} + +Condition::~Condition() +{ + pthread_cond_destroy(&condition_); +} + +void +Condition::signal() +{ + int error = pthread_cond_signal(&condition_); + CheckThreadError(error); +} + + +void +Condition::broadcast() +{ + int error = pthread_cond_broadcast(&condition_); + CheckThreadError(error); +} + + +void +Condition::wait(Mutex &mutex) +{ + int error = pthread_cond_wait(&condition_, &mutex.mutex_); + CheckThreadError(error); +} + +} // namespace diff --git a/util/Condition.hh b/util/Condition.hh new file mode 100644 index 0000000..366f4a4 --- /dev/null +++ b/util/Condition.hh @@ -0,0 +1,41 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_CONDITION_H +#define STA_CONDITION_H + +#include "Pthread.hh" + +namespace sta { + +class Mutex; + +// C++ wrapper/facade for pthread_cond_t. +class Condition +{ +public: + Condition(); + ~Condition(); + void signal(); + void broadcast(); + void wait(Mutex &mutex); + +private: + pthread_cond_t condition_; +}; + +} // namespace +#endif diff --git a/util/Debug.cc b/util/Debug.cc new file mode 100644 index 0000000..1be9651 --- /dev/null +++ b/util/Debug.cc @@ -0,0 +1,115 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Report.hh" +#include "Debug.hh" + +namespace sta { + +bool debug_on = false; + +Debug::Debug(Report *&report) : + report_(report), + debug_map_(nullptr), + stats_level_(0) +{ +} + +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 +{ + if (debug_map_) { + int dbg_level; + bool exists; + debug_map_->findKey(what, dbg_level, exists); + if (exists) + return dbg_level >= level; + } + return false; +} + +int +Debug::level(const char *what) +{ + if (debug_map_) { + const char *key; + int dbg_level; + bool exists; + debug_map_->findKey(what, key, dbg_level, exists); + if (exists) + return dbg_level; + } + return 0; +} + +void +Debug::setLevel(const char *what, + int level) +{ + if (stringEq(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; + } + // debugCheck map lookup bypass + debug_on = (debug_map_->size() != 0); + } + } + 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_on = true; + } +} + +void +Debug::print(const char *fmt, + ...) const +{ + va_list args; + va_start(args, fmt); + report_->vprintError(fmt, args); + va_end(args); +} + +} // namespace diff --git a/util/Debug.hh b/util/Debug.hh new file mode 100644 index 0000000..20c6be2 --- /dev/null +++ b/util/Debug.hh @@ -0,0 +1,127 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_DEBUG_H +#define STA_DEBUG_H + +#include +#include "DisallowCopyAssign.hh" +#include "Map.hh" +#include "StringUtil.hh" + +namespace sta { + +class Report; +class Pin; + +// Flag that is set when any debug mode is enabled. +// Debug macros bypass Debug::check map lookup unless some debug mode +// is enabled. +extern bool debug_on; + +typedef Map DebugMap; + +class Debug +{ +public: + explicit Debug(Report *&report); + ~Debug(); + bool check(const char *what, + int level) const; + int level(const char *what); + void setLevel(const char *what, + int level); + int statsLevel() const { return stats_level_; } + void print(const char *fmt, + ...) const + __attribute__((format (printf, 2, 3))); + +protected: + Report *&report_; + DebugMap *debug_map_; + int stats_level_; + +private: + DISALLOW_COPY_AND_ASSIGN(Debug); +}; + +// Low overhead predicate. +inline bool +debugCheck(const Debug *debug, + const char *what, + int level) +{ + return debug_on && debug->check(what, level); +} + +// Inlining a varargs function would eval the args, which can +// be expensive, so use macros. + +#define debugPrint0(debug, what, level, msg) \ + if (sta::debug_on && debug->check(what, level)) { \ + debug->print("%s: %s", what, msg); \ + } + +#define debugPrint1(debug, what, level, fmt, arg1) \ + if (sta::debug_on && debug->check(what, level)) { \ + debug->print("%s: ", what); \ + debug->print(fmt, arg1); \ + } + +#define debugPrint2(debug, what, level, fmt, arg1, arg2) \ + if (sta::debug_on && debug->check(what, level)) { \ + debug->print("%s: ", what); \ + debug->print(fmt, arg1, arg2); \ + } + +#define debugPrint3(debug, what, level, fmt, arg1, arg2, arg3) \ + if (sta::debug_on && debug->check(what, level)) { \ + debug->print("%s: ", what); \ + debug->print(fmt, arg1, arg2, arg3); \ + } + +#define debugPrint4(debug, what, level, fmt, arg1, arg2, arg3, arg4) \ + if (sta::debug_on && debug->check(what, level)) { \ + debug->print("%s: ", what); \ + debug->print(fmt, arg1, arg2, arg3, arg4); \ + } + +#define debugPrint5(debug, what, level, fmt, arg1, arg2, arg3, arg4, arg5) \ + if (sta::debug_on && debug->check(what, level)) { \ + debug->print("%s: ", what); \ + debug->print(fmt, arg1, arg2, arg3, arg4, arg5); \ + } + +#define debugPrint6(debug,what,level,fmt,arg1,arg2,arg3,arg4,arg5,arg6) \ + if (sta::debug_on && debug->check(what, level)) { \ + debug->print("%s: ", what); \ + debug->print(fmt, arg1, arg2, arg3, arg4, arg5, arg6); \ + } + +#define debugPrint7(debug,what,level,fmt,arg1,arg2,arg3,arg4,arg5,arg6,arg7) \ + if (sta::debug_on && debug->check(what, level)) { \ + debug->print("%s: ", what); \ + debug->print(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7); \ + } + +#define debugPrint8(debug,what,level,fmt,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8) \ + if (sta::debug_on && debug->check(what, level)) { \ + debug->print("%s: ", what); \ + debug->print(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); \ + } + +} // namespace +#endif diff --git a/util/DisallowCopyAssign.hh b/util/DisallowCopyAssign.hh new file mode 100644 index 0000000..6c5cf39 --- /dev/null +++ b/util/DisallowCopyAssign.hh @@ -0,0 +1,26 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_DISALLOW_COPY_ASSIGN_H +#define STA_DISALLOW_COPY_ASSIGN_H + +// Disallow the copy constructor and operator= functions. +// This should be used in the private declarations for a class. +#define DISALLOW_COPY_AND_ASSIGN(type_name) \ + type_name(const type_name&) = delete; \ + void operator=(const type_name&) = delete + +#endif diff --git a/util/EnumNameMap.hh b/util/EnumNameMap.hh new file mode 100644 index 0000000..71f0a3e --- /dev/null +++ b/util/EnumNameMap.hh @@ -0,0 +1,97 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_ENUM_NAME_MAP_H +#define STA_ENUM_NAME_MAP_H + +#include +#include + +namespace sta { + +using std::string; +using std::map; +using std::initializer_list; +using std::pair; + +// Helper for mapping enum values to strings and back. +template +class EnumNameMap +{ +public: + EnumNameMap(initializer_list> enum_names); + const char *find(ENUM key) const; + ENUM find(string name, + ENUM unknown_key) const; + void find(string name, + // Return values. + ENUM &key, + bool &exists) const; + +private: + map enum_map_; + map name_map_; +}; + +template +EnumNameMap::EnumNameMap(initializer_list> enum_names) : + enum_map_(enum_names) +{ + for (auto iter = enum_map_.begin(); iter != enum_map_.end(); iter++) + name_map_[iter->second] = iter->first; +} + +template +const char * +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; +} + +template +void +EnumNameMap::find(string name, + // Return values. + ENUM &key, + bool &exists) const +{ + auto find_iter = name_map_.find(name); + if (find_iter != name_map_.end()) { + key = find_iter->second; + exists = true; + } + else + exists = false; +} + +template +ENUM +EnumNameMap::find(string name, + ENUM unknown_key) const +{ + auto find_iter = name_map_.find(name); + if (find_iter != name_map_.end()) + return find_iter->second; + else + return unknown_key; +} + +} // namespace +#endif diff --git a/util/Error.cc b/util/Error.cc new file mode 100644 index 0000000..2ba320a --- /dev/null +++ b/util/Error.cc @@ -0,0 +1,75 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include "Machine.hh" +#include "StringUtil.hh" +#include "Error.hh" + +namespace sta { + +StaException::StaException() : + std::exception() +{ +} + +StaExceptionLine::StaExceptionLine(const char *filename, + int line) : + StaException(), + filename_(filename), + line_(line) +{ +} + +InternalError::InternalError(const char *filename, + int line, + const char *msg) : + StaExceptionLine(filename, line), + msg_(msg) +{ +} + +const char * +InternalError::what() const throw() +{ + return stringPrintTmp("Internal error in %s:%d %s.", + filename_, line_, msg_); +} + +FileNotReadable::FileNotReadable(const char *filename) : + filename_(filename) +{ +} + +const char * +FileNotReadable::what() const throw() +{ + return stringPrintTmp("Error: cannot read file %s.", filename_); +} + +FileNotWritable::FileNotWritable(const char *filename) : + filename_(filename) +{ +} + +const char * +FileNotWritable::what() const throw() +{ + return stringPrintTmp("Error: cannot write file %s.", filename_); +} + +} // namespace diff --git a/util/Error.hh b/util/Error.hh new file mode 100644 index 0000000..135a811 --- /dev/null +++ b/util/Error.hh @@ -0,0 +1,90 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_ERROR_H +#define STA_ERROR_H + +#include +#include "DisallowCopyAssign.hh" + +namespace sta { + +// Abstract base class for sta exceptions. +class StaException : public std::exception +{ +public: + StaException(); + virtual ~StaException() THROW_DCL {} + virtual const char *what() const throw() = 0; +}; + +class StaExceptionLine : public StaException +{ +public: + StaExceptionLine(const char *filename, + int line); + +protected: + const char *filename_; + int line_; +}; + +class InternalError : public StaExceptionLine +{ +public: + InternalError(const char *filename, + int line, + const char *msg); + virtual const char *what() const throw(); + +protected: + const char *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, as these +// are added by InternalError::asString(). +#define internalError(msg) \ + throw sta::InternalError(__FILE__, __LINE__, msg) + +#define internalErrorNoThrow(msg) \ + printf("Internal Error: %s:%d %s\n", __FILE__, __LINE__, msg) + +// Failure opening filename for reading. +class FileNotReadable : public StaException +{ +public: + explicit FileNotReadable(const char *filename); + virtual const char *what() const throw(); + +protected: + const char *filename_; +}; + +// Failure opening filename for writing. +class FileNotWritable : public StaException +{ +public: + explicit FileNotWritable(const char *filename); + virtual const char *what() const throw(); + +protected: + const char *filename_; +}; + +} // namespace +#endif diff --git a/util/Fuzzy.cc b/util/Fuzzy.cc new file mode 100644 index 0000000..f6a83eb --- /dev/null +++ b/util/Fuzzy.cc @@ -0,0 +1,77 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include // max +#include // abs +#include "Machine.hh" +#include "MinMax.hh" // INF + +namespace sta { + +using std::max; +using std::abs; + +bool +fuzzyEqual(float v1, + float v2) +{ + return v1 == v2 + || abs(v1 - v2) < 1E-6F * max(abs(v1), abs(v2)); +} + +bool +fuzzyZero(float v) +{ + return v == 0.0 + || abs(v) < 1E-15F; +} + +bool +fuzzyLess(float v1, + float v2) +{ + return v1 < v2 && !fuzzyEqual(v1, v2); +} + +bool +fuzzyLessEqual(float v1, + float v2) +{ + return v1 < v2 || fuzzyEqual(v1, v2); +} + +bool +fuzzyGreater(float v1, + float v2) +{ + return v1 > v2 && !fuzzyEqual(v1, v2); +} + +bool +fuzzyGreaterEqual(float v1, + float v2) +{ + return v1 > v2 || fuzzyEqual(v1, v2); +} + +bool +fuzzyInf(float value) +{ + return fuzzyEqual(value, INF) + || fuzzyEqual(value, -INF); +} + +} // namespace diff --git a/util/Fuzzy.hh b/util/Fuzzy.hh new file mode 100644 index 0000000..e31ec36 --- /dev/null +++ b/util/Fuzzy.hh @@ -0,0 +1,45 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_FUZZY_H +#define STA_FUZZY_H + +namespace sta { + +// "Fuzzy" floating point comparisons that allow some tolerance. + +bool +fuzzyEqual(float v1, + float v2); +bool +fuzzyZero(float v); +bool +fuzzyLess(float v1, + float v2); +bool +fuzzyLessEqual(float v1, + float v2); +bool +fuzzyGreater(float v1, + float v2); +bool +fuzzyGreaterEqual(float v1, + float v2); +bool +fuzzyInf(float value); + +} // namespace +#endif diff --git a/util/Hash.cc b/util/Hash.cc new file mode 100644 index 0000000..28547a6 --- /dev/null +++ b/util/Hash.cc @@ -0,0 +1,33 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "Hash.hh" + +namespace sta { + +size_t +hashString(const char *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]; + return hash; +} + +} // namespace diff --git a/util/Hash.hh b/util/Hash.hh new file mode 100644 index 0000000..86f2fbd --- /dev/null +++ b/util/Hash.hh @@ -0,0 +1,56 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_HASH_H +#define STA_HASH_H + +#include + +namespace sta { + +using std::size_t; + +const size_t hash_init_value = 5381; + +// Dan Bernstein, comp.lang.c. +inline size_t +hashSum(size_t hash, + size_t add) +{ + // hash * 31 ^ add. + return ((hash << 5) + hash) ^ add; +} + +inline void +hashIncr(size_t &hash, + size_t add) +{ + // hash * 31 ^ add. + hash = ((hash << 5) + hash) ^ add; +} + +inline size_t +nextMersenne(size_t n) +{ + return (n + 1) * 2 - 1; +} + +// Sadly necessary until c++ std::hash works for char *. +size_t +hashString(const char *str); + +} // namespace +#endif diff --git a/util/HashMap.hh b/util/HashMap.hh new file mode 100644 index 0000000..33572cc --- /dev/null +++ b/util/HashMap.hh @@ -0,0 +1,570 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_HASHMAP_H +#define STA_HASHMAP_H + +#include // size_t +#include "Hash.hh" + +namespace sta { + +template class HashMapBucket; + + +template +class PtrHash +{ +public: + Hash operator()(const OBJECT obj) const { return hashPtr(obj); } +}; + +template +class PtrEqual +{ +public: + bool operator()(const OBJECT obj1, + const OBJECT obj2) const + { return obj1 == obj2; } +}; + +template , class EQUAL = PtrEqual > +class HashMap +{ +public: + HashMap(); + explicit HashMap(size_t capacity, + bool auto_resize); + explicit HashMap(size_t capacity, + bool auto_resize, + HASH hash, + EQUAL equal); + ~HashMap(); + // Number of objects in the table. + size_t size() const { return size_; } + // Number of hash buckets. + size_t capacity() const { return capacity_; } + // Multi-threaded findKey is safe during resize. + void resize(size_t capacity); + void insert(KEY key, + VALUE value); + VALUE findKey(KEY key); + VALUE findKey(KEY key) const; + void findKey(KEY key, + VALUE &value, + bool &exists) const; + void findKey(KEY key, + KEY &map_key, + VALUE &value, + bool &exists) const; + bool hasKey(KEY key) const; + void eraseKey(KEY key); + bool empty() const; + void clear(); + void deleteContentsClear(); + void deleteArrayContentsClear(); + int longestBucketLength() const; + Hash longestBucketHash() const; + int bucketLength(Hash hash) const; + + void + deleteContents() + { + Iterator iter(this); + while (iter.hasNext()) + delete iter.next(); + } + + void + deleteArrayContents() + { + Iterator iter(this); + while (iter.hasNext()) + delete [] iter.next(); + } + + // Java style container itererator + // Map::Iterator iter(map); + // while (iter.hasNext()) { + // Value *v = iter.next(); + // } + class Iterator + { + public: + Iterator() : container_(NULL) {} + explicit Iterator(HashMap *container) + { init(container); } + explicit Iterator(HashMap &container) + { init(container); } + void init(HashMap *container) + { container_ = container; + hash_= 0; + next_ = NULL; + if (container_) + findNext(); + } + void init(HashMap &container) + { container_ = &container; + hash_= 0; + next_ = NULL; + if (container_) + findNext(); + } + bool hasNext() { return container_ && next_ != NULL; } + VALUE next() { + HashMapBucket *next = next_; + findNext(); + return next->value(); + } + void next(KEY &key, VALUE &value) { + HashMapBucket *next = next_; + findNext(); + key = next->key(); + value = next->value(); + } + HashMap *container() { return container_; } + + private: + void + findNext() + { + if (next_) + next_ = next_->next(); + while (next_ == NULL + && hash_ < container_->capacity()) + next_ = container_->table_[hash_++]; + } + HashMap *container_; + size_t hash_; + HashMapBucket *next_; + }; + + class ConstIterator + { + public: + ConstIterator() : container_(NULL) {} + explicit ConstIterator(const HashMap *container) + { init(container); } + explicit ConstIterator(const HashMap &container) + { init(container); } + void init(const HashMap *container) + { container_ = container; hash_= 0; next_ = NULL; findNext(); } + void init(HashMap &container) + { container_ = &container; hash_= 0; next_ = NULL; findNext(); } + bool hasNext() { return container_ && next_ != NULL; } + VALUE next() { + HashMapBucket *next = next_; + findNext(); + return next->value(); + } + void next(// Return values. + KEY &key, + VALUE &value) { + HashMapBucket *next = next_; + findNext(); + key = next->key(); + value = next->value(); + } + HashMap *container() { return container_; } + + private: + void + findNext() + { + if (next_) + next_ = next_->next(); + while (next_ == NULL + && hash_ < container_->capacity()) + next_ = container_->table_[hash_++]; + } + const HashMap *container_; + size_t hash_; + HashMapBucket *next_; + }; + +protected: + void initTable(); + static const int default_capacity = (2 << 6) - 1; + + // Table size. + size_t capacity_; + bool auto_resize_; + HASH hash_; + EQUAL equal_; + size_t size_; + HashMapBucket **table_; + HashMap *tmp_; +}; + +template +class HashMapBucket +{ +public: + HashMapBucket(KEY key, + VALUE value, + HashMapBucket *next); + KEY key() const { return key_; } + VALUE &value() { return value_; } + void set(KEY key, VALUE value) { key_ = key; value_ = value; } + HashMapBucket *next() const { return next_; } + void setNext(HashMapBucket *next) { next_ = next; } + +private: + KEY key_; + VALUE value_; + HashMapBucket *next_; +}; + +template +HashMapBucket::HashMapBucket(KEY key, + VALUE value, + HashMapBucket *next) : + key_(key), + value_(value), + next_(next) +{ +} + +//////////////////////////////////////////////////////////////// + +template +HashMap::HashMap() : + capacity_(default_capacity), + auto_resize_(true), + hash_(HASH()), + equal_(EQUAL()) +{ + initTable(); +} + +template +HashMap::HashMap(size_t capacity, + bool auto_resize) : + auto_resize_(auto_resize), + capacity_(capacity), + hash_(HASH()), + equal_(EQUAL()) +{ + initTable(); +} + +template +HashMap::HashMap(size_t capacity, + bool auto_resize, + HASH hash, + EQUAL equal) : + capacity_(capacity), + auto_resize_(auto_resize), + hash_(hash), + equal_(equal) +{ + initTable(); +} + +template +void +HashMap::initTable() +{ + size_ = 0; + tmp_ = NULL; + table_ = new HashMapBucket*[capacity_]; + for (size_t i = 0; i < capacity_; i++) + table_[i] = NULL; +} + +template +HashMap::~HashMap() +{ + for (size_t hash = 0; hash < capacity_; hash++) { + HashMapBucket *next; + for (HashMapBucket *bucket = table_[hash]; + bucket; + bucket = next) { + next = bucket->next(); + delete bucket; + } + } + delete [] table_; + delete tmp_; +} + +template +bool +HashMap::hasKey(KEY key) const +{ + size_t hash = hash_(key) % capacity_; + HashMapBucket *head = table_[hash]; + for (HashMapBucket *bucket = head; bucket; bucket = bucket->next()) { + if (equal_(bucket->key(), key)) { + return true; + } + } + return false; +} + +template +VALUE +HashMap::findKey(KEY key) +{ + size_t hash = hash_(key) % capacity_; + HashMapBucket *head = table_[hash]; + for (HashMapBucket *bucket = head; bucket; bucket = bucket->next()) { + KEY bucket_key = bucket->key(); + if (equal_(bucket_key, key)) { + return bucket->value(); + } + } + return NULL; +} + +template +VALUE +HashMap::findKey(KEY key) const +{ + size_t hash = hash_(key) % capacity_; + HashMapBucket *head = table_[hash]; + for (HashMapBucket *bucket = head; bucket; bucket = bucket->next()) { + KEY bucket_key = bucket->key(); + if (equal_(bucket_key, key)) { + return bucket->value(); + } + } + return NULL; +} + +template +void +HashMap::findKey(KEY key, + VALUE &value, + bool &exists) const +{ + size_t hash = hash_(key) % capacity_; + HashMapBucket *head = table_[hash]; + for (HashMapBucket *bucket = head; bucket; bucket = bucket->next()) { + KEY bucket_key = bucket->key(); + if (equal_(bucket_key, key)) { + value = bucket->value(); + exists = true; + return; + } + } + exists = false; +} + +template +void +HashMap::findKey(KEY key, + KEY &map_key, + VALUE &value, + bool &exists) const +{ + size_t hash = hash_(key) % capacity_; + HashMapBucket *head = table_[hash]; + for (HashMapBucket *bucket = head; bucket; bucket = bucket->next()) { + KEY bucket_key = bucket->key(); + if (equal_(bucket_key, key)) { + map_key = bucket->key(); + value = bucket->value(); + exists = true; + return; + } + } + exists = false; +} + +template +void +HashMap::insert(KEY key, + VALUE value) +{ + size_t hash = hash_(key) % capacity_; + HashMapBucket *head = table_[hash]; + for (HashMapBucket *bucket = head; bucket; bucket = bucket->next()) { + if (equal_(bucket->key(), key)) { + bucket->set(key, value); + return; + } + } + HashMapBucket *bucket = + new HashMapBucket(key, value, head); + table_[hash] = bucket; + size_++; + if (size_ > capacity_ + && auto_resize_) + resize(nextMersenne(capacity_)); +} + +template +void +HashMap::resize(size_t capacity) +{ + if (capacity != capacity_) { + if (size_ == 0) { + // Table is empty. + capacity_ = capacity; + delete [] table_; + delete tmp_; + initTable(); + } + else { + delete tmp_; + // Copy entries to tmp. + tmp_ = new HashMap(capacity, auto_resize_, + hash_, equal_); + for (size_t hash = 0; hash < capacity_; hash++) { + for (HashMapBucket *bucket = table_[hash]; + bucket; + bucket = bucket->next()) + tmp_->insert(bucket->key(), bucket->value()); + } + + size_t prev_capacity = capacity_; + HashMapBucket **prev_table = table_; + + // Switch over. + table_ = tmp_->table_; + capacity_ = capacity; + + tmp_->capacity_ = prev_capacity; + tmp_->table_ = prev_table; + } + } +} + +template +void +HashMap::eraseKey(KEY key) +{ + size_t hash = hash_(key) % capacity_; + HashMapBucket *head = table_[hash]; + HashMapBucket *prev = NULL; + for (HashMapBucket *bucket = head; bucket; bucket = bucket->next()) { + if (equal_(bucket->key(), key)) { + if (prev) + prev->setNext(bucket->next()); + else + table_[hash] = bucket->next(); + delete bucket; + size_--; + break; + } + prev = bucket; + } +} + +template +void +HashMap::deleteContentsClear() +{ + if (size_ > 0) { + for (size_t hash = 0; hash < capacity_; hash++) { + HashMapBucket *next; + for (HashMapBucket *bucket = table_[hash]; + bucket; + bucket = next) { + delete bucket->value(); + next = bucket->next(); + delete bucket; + } + table_[hash] = NULL; + } + size_ = 0; + } +} + +template +void +HashMap::deleteArrayContentsClear() +{ + if (size_ > 0) { + for (size_t hash = 0; hash < capacity_; hash++) { + HashMapBucket *next; + for (HashMapBucket *bucket = table_[hash]; + bucket; + bucket = next) { + delete [] bucket->value(); + next = bucket->next(); + delete bucket; + } + table_[hash] = NULL; + } + size_ = 0; + } +} + +template +void +HashMap::clear() +{ + if (size_ > 0) { + for (size_t hash = 0; hash < capacity_; hash++) { + HashMapBucket *next; + for (HashMapBucket *bucket = table_[hash]; + bucket; + bucket = next) { + next = bucket->next(); + delete bucket; + } + table_[hash] = NULL; + } + size_ = 0; + } +} + +template +bool +HashMap::empty() const +{ + return size_ == 0; +} + +template +int +HashMap::longestBucketLength() const +{ + return bucketLength(longestBucketHash()); +} + +template +Hash +HashMap::longestBucketHash() const +{ + int longest = 0; + Hash longest_hash = 0; + for (size_t hash = 0; hash < capacity_; hash++) { + int length = bucketLength(hash); + if (length > longest) { + longest = length; + longest_hash = hash; + } + } + return longest_hash; +} + +template +int +HashMap::bucketLength(Hash hash) const +{ + int length = 0; + for (HashMapBucket *bucket = table_[hash]; + bucket; + bucket = bucket->next()) + length++; + return length; +} + +} // namespace +#endif diff --git a/util/HashSet.hh b/util/HashSet.hh new file mode 100644 index 0000000..e429707 --- /dev/null +++ b/util/HashSet.hh @@ -0,0 +1,455 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_HASHSET_H +#define STA_HASHSET_H + +#include // size_t +#include "Hash.hh" + +namespace sta { + +template class HashSetBucket; + +template +class HashSet +{ +public: + HashSet(); + explicit HashSet(size_t capacity, + bool auto_resize); + explicit HashSet(size_t capacity, + bool auto_resize, + HASH hash, + EQUAL equal); + ~HashSet(); + // Number of objects in the table. + size_t size() const { return size_; } + // Number of hash buckets. + size_t capacity() const { return capacity_; } + // Multi-threaded findKey is safe during resize. + void resize(size_t capacity); + void insert(KEY key); + KEY findKey(KEY key); + KEY findKey(KEY key) const; + bool hasKey(KEY key); + void erase(KEY key); + bool empty() const; + void clear(); + void deleteContentsClear(); + int longestBucketLength() const; + size_t longestBucketHash() const; + int bucketLength(size_t hash) const; + + void + deleteContents() + { + Iterator iter(this); + while (iter.hasNext()) + delete iter.next(); + } + + // Java style container itererator + // Map::Iterator iter(map); + // while (iter.hasNext()) { + // Value *v = iter.next(); + // } + class Iterator + { + public: + Iterator() : container_(nullptr) {} + explicit Iterator(HashSet *container) + { init(container); } + explicit Iterator(HashSet &container) + { init(container); } + void init(HashSet *container) + { container_ = container; + hash_= 0; + next_ = nullptr; + if (container_) + findNext(); + } + void init(HashSet &container) + { container_ = &container; + hash_= 0; + next_ = nullptr; + if (container_) + findNext(); + } + bool hasNext() { return container_ && next_ != nullptr; } + KEY next() { + HashSetBucket *next = next_; + findNext(); + return next->key(); + } + HashSet *container() { return container_; } + + private: + void + findNext() + { + if (next_) + next_ = next_->next(); + while (next_ == nullptr + && hash_ < container_->capacity()) + next_ = container_->table_[hash_++]; + } + HashSet *container_; + size_t hash_; + HashSetBucket *next_; + }; + + class ConstIterator + { + public: + ConstIterator() : container_(nullptr) {} + explicit ConstIterator(const HashSet *container) + { init(container); } + explicit ConstIterator(const HashSet &container) + { init(container); } + void init(const HashSet *container) + { container_ = container; hash_= 0; next_ = nullptr; findNext(); } + void init(HashSet &container) + { container_ = &container; hash_= 0; next_ = nullptr; findNext(); } + bool hasNext() { return container_ && next_ != nullptr; } + KEY next() { + HashSetBucket *next = next_; + findNext(); + return next->key(); + } + HashSet *container() { return container_; } + + private: + void + findNext() + { + if (next_) + next_ = next_->next(); + while (next_ == nullptr + && hash_ < container_->capacity()) + next_ = container_->table_[hash_++]; + } + const HashSet *container_; + size_t hash_; + HashSetBucket *next_; + }; + +protected: + void initTable(); + static const int default_capacity = (2 << 6) - 1; + + // Table size. + size_t capacity_; + bool auto_resize_; + HASH hash_; + EQUAL equal_; + size_t size_; + HashSetBucket **table_; + HashSet *tmp_; +}; + +template +class HashSetBucket +{ +public: + HashSetBucket(KEY key, HashSetBucket *next); + KEY key() const { return key_; } + void setKey(KEY key) { key_ = key; } + HashSetBucket *next() const { return next_; } + void setNext(HashSetBucket *next) { next_ = next; } + +private: + KEY key_; + HashSetBucket *next_; +}; + +template +HashSetBucket::HashSetBucket(KEY key, + HashSetBucket *next) : + key_(key), + next_(next) +{ +} + +//////////////////////////////////////////////////////////////// + +template +HashSet::HashSet() : + capacity_(default_capacity), + auto_resize_(true), + hash_(HASH()), + equal_(EQUAL()) +{ + initTable(); +} + +template +HashSet::HashSet(size_t capacity, + bool auto_resize) : + capacity_(capacity), + auto_resize_(auto_resize), + hash_(HASH()), + equal_(EQUAL()) +{ + initTable(); +} + +template +HashSet::HashSet(size_t capacity, + bool auto_resize, + HASH hash, + EQUAL equal) : + capacity_(capacity), + auto_resize_(auto_resize), + hash_(hash), + equal_(equal) +{ + initTable(); +} + +template +void +HashSet::initTable() +{ + size_ = 0; + tmp_ = nullptr; + table_ = new HashSetBucket*[capacity_]; + for (size_t i = 0; i < capacity_; i++) + table_[i] = nullptr; +} + +template +HashSet::~HashSet() +{ + for (size_t hash = 0; hash < capacity_; hash++) { + HashSetBucket *next; + for (HashSetBucket *bucket = table_[hash]; + bucket; + bucket = next) { + next = bucket->next(); + delete bucket; + } + } + delete [] table_; + delete tmp_; +} + +template +KEY +HashSet::findKey(KEY key) const +{ + size_t hash = hash_(key) % capacity_; + HashSetBucket *head = table_[hash]; + for (HashSetBucket *bucket = head; bucket; bucket = bucket->next()) { + KEY bucket_key = bucket->key(); + if (equal_(bucket_key, key)) { + return bucket_key; + } + } + return nullptr; +} + +template +KEY +HashSet::findKey(KEY key) +{ + size_t hash = hash_(key) % capacity_; + HashSetBucket *head = table_[hash]; + for (HashSetBucket *bucket = head; bucket; bucket = bucket->next()) { + KEY bucket_key = bucket->key(); + if (equal_(bucket_key, key)) { + return bucket_key; + } + } + return nullptr; +} + +template +bool +HashSet::hasKey(KEY key) +{ + size_t hash = hash_(key) % capacity_; + HashSetBucket *head = table_[hash]; + for (HashSetBucket *bucket = head; bucket; bucket = bucket->next()) { + KEY bucket_key = bucket->key(); + if (equal_(bucket_key, key)) { + return true; + } + } + return false; +} + +template +void +HashSet::insert(KEY key) +{ + size_t hash = hash_(key) % capacity_; + HashSetBucket *head = table_[hash]; + for (HashSetBucket *bucket = head; bucket; bucket = bucket->next()) { + if (equal_(bucket->key(), key)) { + bucket->setKey(key); + return; + } + } + HashSetBucket *bucket = new HashSetBucket(key, head); + table_[hash] = bucket; + size_++; + if (size_ > capacity_ + && auto_resize_) + resize(nextMersenne(capacity_)); +} + +template +void +HashSet::resize(size_t capacity) +{ + if (capacity != capacity_) { + if (size_ == 0) { + // Table is empty. + capacity_ = capacity; + delete [] table_; + delete tmp_; + initTable(); + } + else { + delete tmp_; + // Copy entries to tmp. + tmp_ = new HashSet(capacity, auto_resize_, + hash_, equal_); + for (size_t hash = 0; hash < capacity_; hash++) { + for (HashSetBucket *bucket = table_[hash]; + bucket; + bucket = bucket->next()) + tmp_->insert(bucket->key()); + } + + size_t prev_capacity = capacity_; + HashSetBucket **prev_table = table_; + + // Switch over. + table_ = tmp_->table_; + capacity_ = capacity; + + tmp_->capacity_ = prev_capacity; + tmp_->table_ = prev_table; + } + } +} + +template +void +HashSet::erase(KEY key) +{ + size_t hash = hash_(key) % capacity_; + HashSetBucket *head = table_[hash]; + HashSetBucket *prev = nullptr; + for (HashSetBucket *bucket = head; bucket; bucket = bucket->next()) { + if (equal_(bucket->key(), key)) { + if (prev) + prev->setNext(bucket->next()); + else + table_[hash] = bucket->next(); + delete bucket; + size_--; + break; + } + prev = bucket; + } +} + +template +void +HashSet::deleteContentsClear() +{ + if (size_ > 0) { + for (size_t hash = 0; hash < capacity_; hash++) { + HashSetBucket *next; + for (HashSetBucket *bucket = table_[hash]; + bucket; + bucket = next) { + delete bucket->key(); + next = bucket->next(); + delete bucket; + } + table_[hash] = nullptr; + } + size_ = 0; + } +} + +template +void +HashSet::clear() +{ + if (size_ > 0) { + for (size_t hash = 0; hash < capacity_; hash++) { + HashSetBucket *next; + for (HashSetBucket *bucket = table_[hash]; + bucket; + bucket = next) { + next = bucket->next(); + delete bucket; + } + table_[hash] = nullptr; + } + size_ = 0; + } +} + +template +bool +HashSet::empty() const +{ + return size_ == 0; +} + +template +int +HashSet::longestBucketLength() const +{ + return bucketLength(longestBucketHash()); +} + +template +size_t +HashSet::longestBucketHash() const +{ + int longest = 0; + size_t longest_hash = 0; + for (size_t hash = 0; hash < capacity_; hash++) { + int length = bucketLength(hash); + if (length > longest) { + longest = length; + longest_hash = hash; + } + } + return longest_hash; +} + +template +int +HashSet::bucketLength(size_t hash) const +{ + int length = 0; + for (HashSetBucket *bucket = table_[hash]; + bucket; + bucket = bucket->next()) + length++; + return length; +} + +} // namespace +#endif diff --git a/util/Iterator.hh b/util/Iterator.hh new file mode 100644 index 0000000..007e7fe --- /dev/null +++ b/util/Iterator.hh @@ -0,0 +1,37 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_ITERATOR_H +#define STA_ITERATOR_H + +namespace sta { + +// Java style container iterator. +// Iterator iter(container); +// while (iter.hasNext()) { +// Object *obj = iter.next(); +// } +template +class Iterator +{ +public: + virtual ~Iterator() {} + virtual bool hasNext() = 0; + virtual OBJ next() = 0; +}; + +} // namespace +#endif diff --git a/util/Machine.cc b/util/Machine.cc new file mode 100644 index 0000000..2ec2dff --- /dev/null +++ b/util/Machine.cc @@ -0,0 +1,178 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#if defined(_WINDOWS) || defined(_WIN32) + +#include +#include // GetSystemInfo +#include "Machine.hh" +#include "StaConfig.hh" + +namespace sta { + +// Windows returns -1 if the string does not fit rather than the +// required string length as the standard specifies. +int +vsnprint(char *str, size_t size, const char *fmt, va_list args) +{ + // Copy args before using them because consumption is destructive. + va_list args_copy1; + va_copy(args_copy1, args); + int length = vsnprintf(str, size, fmt, args); + + while (length < 0) { + size *= 2; + char *buffer = new char[size]; + va_list args_copy2; + va_copy(args_copy2, args_copy1); + length = vsnprintf(buffer, size, fmt, args_copy2); + delete [] buffer; + } + return length; +} + +int +processorCount() +{ +#if HAVE_PTHREAD_H + SYSTEM_INFO info; + GetSystemInfo(&info); + return info.dwNumberOfProcessors; +#else + return 1; +#endif +} + +void +initElapsedTime() +{ +} + +double +elapsedRunTime() +{ + return 0.0; +} + +double +userRunTime() +{ + return 0.0; +} + +double +systemRunTime() +{ + return 0.0; +} + +size_t +memoryUsage() +{ + return 0; +} + +} + +#else // _WINDOWS + +#include +#include +#include +#include +#include +#include "Machine.hh" +#include "StaConfig.hh" +#include "StringUtil.hh" + +namespace sta { + +static struct timeval elapsed_begin_time_; + +int +processorCount() +{ +#if HAVE_PTHREAD_H + return sysconf(_SC_NPROCESSORS_CONF); +#else + return 1; +#endif +} + +void +initElapsedTime() +{ + struct timezone tz; + gettimeofday(&elapsed_begin_time_, &tz); +} + +double +elapsedRunTime() +{ + static struct timeval time; + struct timezone tz; + gettimeofday(&time, &tz); + return time.tv_sec - elapsed_begin_time_.tv_sec + + (time.tv_usec - elapsed_begin_time_.tv_usec) * 1E-6; +} + +double +userRunTime() +{ + struct rusage rusage; + getrusage(RUSAGE_SELF, &rusage); + return rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec * 1e-6; +} + +double +systemRunTime() +{ + struct rusage rusage; + getrusage(RUSAGE_SELF, &rusage); + return rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec * 1e-6; +} + +// rusage->ru_maxrss is not set in linux so read it from /proc. +size_t +memoryUsage() +{ + string proc_filename; + stringPrint(proc_filename, "/proc/%d/status", getpid()); + size_t memory = 0; + FILE *status = fopen(proc_filename.c_str(), "r"); + if (status) { + const size_t line_length = 128; + char line[line_length]; + while (fgets(line, line_length, status) != nullptr) { + char *field = strtok(line, " \t"); + if (stringEq(field, "VmRSS:")) { + char *size = strtok(nullptr, " \t"); + if (size) { + char *ignore; + // VmRSS is in kilobytes. + memory = strtol(size, &ignore, 10) * 1000; + break; + } + } + } + fclose(status); + } + return memory; +} + +} + +#endif // !_WINDOWS diff --git a/util/Machine.hh b/util/Machine.hh new file mode 100644 index 0000000..9aaad47 --- /dev/null +++ b/util/Machine.hh @@ -0,0 +1,114 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_MACHINE_H +#define STA_MACHINE_H + +// This header contains global os/port specific definitions. +// It should be included in every source file after any system include +// files and before any STA include files. + +// Pragma placeholder for non-gcc compilers. +#ifndef __GNUC__ + #define __attribute__(x) +#endif // __GNUC__ + +// Requires #include if referenced. +#define INT_DIGITS std::numeric_limits::digits10 + +#ifdef _MSC_VER + // Microcruft Visual C++ + // Obtuse warning codes enabled by pragma. + // 4018 = signed, unsigned mismatch + // 4032 = unexpected type promotions + // 4100 = unused argument + // 4132 = uninitialized const + // 4189 = unused local variable + // 4201 = unnamed struct + // 4222 = static class member declared static at file scope + // 4234 = use of keyword reserved for future use + // 4245 = negative const converted to unsigned + // 4355 = use of 'this' in invalid context + // 4505 = function not used + // 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 ) +#endif // _MSC_VER + +#if defined(_WINDOWS) || defined(_WIN32) + // Export class definitions to DLLs. + #define DllExport __declspec(dllexport) + // intptr_t is defined in stddef.h, included below. + #include + #define va_copy(d,s) ((d)=(s)) + #define strcasecmp _stricmp + #define strncasecmp strncmp + #define strtof(nptr,endptr) static_cast(strtod(nptr,endptr)) + #define strtoull _strtoui64 + // Flex doesn't check for unistd.h. + #define YY_NO_UNISTD_H + // Visual c++ version of std::exception destructor missing throw() + // declaration. + #define THROW_DCL + namespace sta { + int vsnprint(char *str, size_t size, const char *fmt, va_list args); + } +#else + #define DllExport + #include // intptr_t + #define vsnprint vsnprintf + #define THROW_DCL throw() +#endif // _WINDOWS + +#include + +namespace sta { + +int +processorCount(); + +// Init elapsed (wall) time. +void +initElapsedTime(); + +// Elapsed/wall time (in seconds). +double +elapsedRunTime(); + +// User run time (in seconds). +double +userRunTime(); + +// System run time (in seconds). +double +systemRunTime(); + +// Memory usage in bytes. +size_t +memoryUsage(); + +#if __WORDSIZE == 64 + #define hashPtr(ptr) (reinterpret_cast(ptr) >> 3) +#else + #define hashPtr(ptr) (reinterpret_cast(ptr) >> 2) +#endif + +} + +#endif // STA_MACHINE_H diff --git a/util/Makefile.am b/util/Makefile.am new file mode 100644 index 0000000..62b7eed --- /dev/null +++ b/util/Makefile.am @@ -0,0 +1,83 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +lib_LTLIBRARIES = libutil.la + +include_HEADERS = \ + Condition.hh \ + Debug.hh \ + DisallowCopyAssign.hh \ + Error.hh \ + Fuzzy.hh \ + Hash.hh \ + HashSet.hh \ + HashMap.hh \ + Iterator.hh \ + Machine.hh \ + Map.hh \ + MinMax.hh \ + Mutex.hh \ + ObjectIndex.hh \ + PatternMatch.hh \ + Pthread.hh \ + Pool.hh \ + ReadWriteLock.hh \ + Report.hh \ + ReportStd.hh \ + ReportTcl.hh \ + Set.hh \ + Stats.hh \ + StringSeq.hh \ + StringSet.hh \ + StringUtil.hh \ + Thread.hh \ + ThreadException.hh \ + ThreadForEach.hh \ + ThreadPool.hh \ + ThreadWorker.hh \ + TokenParser.hh \ + UnorderedMap.hh \ + Vector.hh \ + Zlib.hh + +libutil_la_SOURCES = \ + Condition.cc \ + Debug.cc \ + Error.cc \ + Fuzzy.cc \ + Machine.cc \ + MinMax.cc \ + Mutex.cc \ + PatternMatch.cc \ + Pthread.cc \ + ReadWriteLock.cc \ + Report.cc \ + ReportStd.cc \ + ReportTcl.cc \ + Stats.cc \ + StringSeq.cc \ + StringSet.cc \ + StringUtil.cc \ + Thread.cc \ + ThreadException.cc \ + ThreadPool.cc \ + ThreadWorker.cc \ + TokenParser.cc + +libs: $(lib_LTLIBRARIES) + +xtags: $(SOURCES) $(HEADERS) + etags -a -o ../TAGS $(SOURCES) $(HEADERS) diff --git a/util/Map.hh b/util/Map.hh new file mode 100644 index 0000000..43b0a7e --- /dev/null +++ b/util/Map.hh @@ -0,0 +1,176 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_MAP_H +#define STA_MAP_H + +#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 + 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 +#endif diff --git a/util/MinMax.cc b/util/MinMax.cc new file mode 100644 index 0000000..81561a2 --- /dev/null +++ b/util/MinMax.cc @@ -0,0 +1,177 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "StringUtil.hh" +#include "MinMax.hh" + +namespace sta { + +const float INF = 1E+30F; + +static bool +compareMin(float value1, + float value2) +{ + return value1 < value2; +} + +static bool +compareMax(float value1, + float value2) +{ + return value1 > value2; +} + +//////////////////////////////////////////////////////////////// + +MinMax MinMax::min_("min", 0, INF, compareMin); +MinMax MinMax::max_("max", 1, -INF, compareMax); +const std::array MinMax::range_{&min_, &max_}; +const std::array MinMax::range_index_{min_.index(), max_.index()}; + +MinMax::MinMax(const char *name, + int index, + float init_value, + bool (*compare)(float value1, float value2)) : + name_(name), + index_(index), + init_value_(init_value), + compare_(compare) +{ +} + +MinMaxAll * +MinMax::asMinMaxAll() const +{ + if (this == &min_) + return MinMaxAll::min(); + else + return MinMaxAll::max(); +} + +MinMax * +MinMax::opposite() const +{ + if (this == &max_) + return &min_; + else + return &max_; +} + +MinMax * +MinMax::find(const char *min_max) +{ + if (stringEq(min_max, "min") + || stringEq(min_max, "early")) + return &min_; + else if (stringEq(min_max, "max") + || stringEq(min_max, "late")) + return &max_; + else + return nullptr; +} + +MinMax * +MinMax::find(int index) +{ + if (index == min_.index()) + return &min_; + else if (index == max_.index()) + return &max_; + else + return nullptr; +} + +bool +MinMax::compare(float value1, + float value2) const +{ + return compare_(value1, value2); +} + +//////////////////////////////////////////////////////////////// + +MinMaxAll MinMaxAll::min_("min", 0, {MinMax::min()}, {MinMax::min()->index()}); +MinMaxAll MinMaxAll::max_("max", 1, {MinMax::max()}, {MinMax::max()->index()}); +MinMaxAll MinMaxAll::all_("all", 2, {MinMax::min(), MinMax::max()}, + {MinMax::min()->index(), MinMax::max()->index()}); + +MinMaxAll::MinMaxAll(const char *name, + int index, + std::vector range, + std::vector range_index) : + name_(name), + index_(index), + range_(range), + range_index_(range_index) +{ +} + +MinMax * +MinMaxAll::asMinMax() const +{ + if (this == &min_) + return MinMax::min(); + else + return MinMax::max(); +} + +bool +MinMaxAll::matches(const MinMax *min_max) const +{ + return this == &all_ || asMinMax() == min_max; +} + +bool +MinMaxAll::matches(const MinMaxAll *min_max) const +{ + return this == &all_ || this == min_max; +} + +MinMaxAll * +MinMaxAll::find(const char *min_max) +{ + if (stringEq(min_max, "min") + || stringEq(min_max, "early")) + return &min_; + else if (stringEq(min_max, "max") + || stringEq(min_max, "late")) + return &max_; + else if (stringEq(min_max, "all") + || stringEq(min_max, "min_max") + || stringEq(min_max, "minmax")) + return &all_; + else + return nullptr; +} + +//////////////////////////////////////////////////////////////// + +MinMaxIterator::MinMaxIterator(const MinMaxAll *min_max) +{ + if (min_max == MinMaxAll::all()) { + index_ = 0; + index_max_ = MinMax::index_max; + } + else { + index_ = min_max->asMinMax()->index(); + index_max_ = index_; + } +} + +} // namespace diff --git a/util/MinMax.hh b/util/MinMax.hh new file mode 100644 index 0000000..47f458e --- /dev/null +++ b/util/MinMax.hh @@ -0,0 +1,151 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_MIN_MAX_H +#define STA_MIN_MAX_H + +#include +#include +#include "DisallowCopyAssign.hh" +#include "Iterator.hh" + +namespace sta { + +class MinMax; +class MinMaxAll; + +// Use typedefs to make early/late functional equivalents to min/max. +typedef MinMax EarlyLate; +typedef MinMaxAll EarlyLateAll; + +// Large value used for min/max initial values. +extern const float INF; + +class MinMax +{ +public: + static void init(); + static void destroy(); + // Singleton accessors. + static MinMax *min() { return &min_; } + static MinMax *max() { return &max_; } + static EarlyLate *early() { return &min_; } + static 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_; } + const char *asString() const { return name_; } + int index() const { return index_; } + float initValue() const { return init_value_; } + // Max value1 > value2, Min value1 < value2. + bool compare(float value1, + float value2) const; + MinMaxAll *asMinMaxAll() 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 MinMax *find(const char *min_max); + // Find transition from index. + static MinMax *find(int index); + static const int index_max = 1; + static const int index_count = 2; + static const int index_bit_count = 1; + +private: + DISALLOW_COPY_AND_ASSIGN(MinMax); + MinMax(const char *name, + int index, + float init_value, + bool (*compare)(float value1, + float value2)); + + const char *name_; + int index_; + float init_value_; + bool (*compare_)(float value1, + float value2); + + static MinMax min_; + static MinMax max_; + static const std::array range_; + static const std::array range_index_; +}; + +// Min/Max/All, where "All" means use both min and max. +class MinMaxAll +{ +public: + // Singleton accessors. + static MinMaxAll *min() { return &min_; } + static MinMaxAll *early() { return &min_; } + static MinMaxAll *max() { return &max_; } + static MinMaxAll *late() { return &max_; } + static MinMaxAll *all() { return &all_; } + const char *asString() const { return name_; } + int index() const { return index_; } + MinMax *asMinMax() const; + bool matches(const MinMax *min_max) const; + bool matches(const MinMaxAll *min_max) const; + static MinMaxAll *find(const char *min_max); + // for (auto min_max : min_max->range()) {} + const std::vector &range() const { return range_; } + // for (auto mm_index : min_max->rangeIndex()) {} + const std::vector &rangeIndex() const { return range_index_; } + +private: + DISALLOW_COPY_AND_ASSIGN(MinMaxAll); + MinMaxAll(const char *name, + int index, + std::vector range, + std::vector range_index); + + const char *name_; + int index_; + const std::vector range_; + const std::vector range_index_; + + static MinMaxAll min_; + static MinMaxAll max_; + static MinMaxAll all_; +}; + +//////////////////////////////////////////////////////////////// + +// Obsolete. Use range iteration. +class MinMaxIterator : public Iterator +{ +public: + MinMaxIterator() : index_(0), index_max_(MinMax::index_max) {} + explicit MinMaxIterator(const MinMaxAll *min_max); + bool hasNext() { return index_ <= index_max_; } + MinMax *next() + { return (index_++ == 0) ? MinMax::min() : MinMax::max(); } + +private: + DISALLOW_COPY_AND_ASSIGN(MinMaxIterator); + + int index_; + int index_max_; +}; + +typedef MinMaxIterator EarlyLateIterator; + +} // namespace +#endif diff --git a/util/Mutex.cc b/util/Mutex.cc new file mode 100644 index 0000000..48e5526 --- /dev/null +++ b/util/Mutex.cc @@ -0,0 +1,40 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Mutex.hh" + +namespace sta { + +Mutex::Mutex() +{ + int error = pthread_mutex_init(&mutex_, NULL); + CheckThreadError(error); +} + +Mutex::Mutex(const Mutex&) +{ + int error = pthread_mutex_init(&mutex_, NULL); + CheckThreadError(error); +} + + +Mutex::~Mutex() +{ + pthread_mutex_destroy(&mutex_); +} + +} // namespace diff --git a/util/Mutex.hh b/util/Mutex.hh new file mode 100644 index 0000000..fa46119 --- /dev/null +++ b/util/Mutex.hh @@ -0,0 +1,27 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_MUTEX_H +#define STA_MUTEX_H + +#include + +namespace sta { + +typedef std::unique_lock UniqueLock; + +} // namespace +#endif diff --git a/util/ObjectIndex.hh b/util/ObjectIndex.hh new file mode 100644 index 0000000..9187217 --- /dev/null +++ b/util/ObjectIndex.hh @@ -0,0 +1,25 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_OBJECT_INDEX_H +#define STA_OBJECT_INDEX_H + +namespace sta { + +typedef unsigned ObjectIndex; + +} // Namespace +#endif diff --git a/util/PatternMatch.cc b/util/PatternMatch.cc new file mode 100644 index 0000000..2df559e --- /dev/null +++ b/util/PatternMatch.cc @@ -0,0 +1,198 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include +#include +#include "Machine.hh" +#include "StringUtil.hh" +#include "PatternMatch.hh" + +namespace sta { + +using std::string; + +PatternMatch::PatternMatch(const char *pattern, + bool is_regexp, + bool nocase, + Tcl_Interp *interp) : + pattern_(pattern), + is_regexp_(is_regexp), + nocase_(nocase), + interp_(interp), + regexp_(nullptr) +{ + if (is_regexp_) + compileRegexp(); +} + +PatternMatch::PatternMatch(const char *pattern) : + pattern_(pattern), + is_regexp_(false), + nocase_(false), + interp_(nullptr), + regexp_(nullptr) +{ +} + +PatternMatch::PatternMatch(const char *pattern, + const PatternMatch *inherit_from) : + pattern_(pattern), + 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; + anchored_pattern += '^'; + anchored_pattern += pattern_; + anchored_pattern += '$'; + Tcl_Obj *pattern_obj = Tcl_NewStringObj(anchored_pattern.c_str(), + anchored_pattern.size()); + regexp_ = Tcl_GetRegExpFromObj(interp_, pattern_obj, flags); + if (regexp_ == nullptr && interp_) + throw RegexpCompileError(pattern_); +} + +static bool +regexpWildcards(const char *pattern) +{ + return strpbrk(pattern, ".+*?[]") != nullptr; +} + +bool +PatternMatch::hasWildcards() const +{ + if (is_regexp_) + return regexpWildcards(pattern_); + else + return patternWildcards(pattern_); +} + +bool +PatternMatch::match(const char *str) const +{ + if (regexp_) + return Tcl_RegExpExec(nullptr, regexp_, str, str) == 1; + else + return patternMatch(pattern_, str); +} + +bool +PatternMatch::matchNoCase(const char *str) const +{ + if (regexp_) + return Tcl_RegExpExec(0, regexp_, str, str) == 1; + else + return patternMatchNoCase(pattern_, str, nocase_); +} + +//////////////////////////////////////////////////////////////// + +RegexpCompileError::RegexpCompileError(const char *pattern) : + StaException() +{ + const char *msg = "Error: TCL failed to compile regular expression '%s'."; + error_ = stringPrintTmp(msg, pattern); +} + +const char * +RegexpCompileError::what() const throw() +{ + return error_; +} + +//////////////////////////////////////////////////////////////// + +bool +patternMatch(const char *pattern, + const char *str) +{ + const char *p = pattern; + const char *s = str; + + while (*p && *s && (*s == *p || *p == '?')) { + p++; + s++; + } + if (*p == '\0' && *s == '\0') + return true; + else if (*p == '*') { + if (p[1] == '\0') + return true; + while (*s) { + if (patternMatch(p + 1, s)) + return true; + s++; + } + } + return false; +} + +inline +bool equalCase(char s, + char p, + bool nocase) +{ + return nocase + ? tolower(s) == tolower(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++; + } + if (*p == '\0' && *s == '\0') + return true; + else if (*p == '*') { + if (p[1] == '\0') + return true; + while (*s) { + if (patternMatchNoCase(p + 1, s, nocase)) + return true; + s++; + } + } + return false; +} + +bool +patternWildcards(const char *pattern) +{ + return strpbrk(pattern, "*?") != 0; +} + +} // namespace diff --git a/util/PatternMatch.hh b/util/PatternMatch.hh new file mode 100644 index 0000000..04802e3 --- /dev/null +++ b/util/PatternMatch.hh @@ -0,0 +1,94 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PATTERN_MATCH_H +#define STA_PATTERN_MATCH_H + +#include "DisallowCopyAssign.hh" +#include "Error.hh" + +// Don't require all of tcl.h. +typedef struct Tcl_RegExp_ *Tcl_RegExp; +typedef struct Tcl_Interp Tcl_Interp; + +namespace sta { + +using ::Tcl_RegExp; +using ::Tcl_Interp; + +class PatternMatch +{ +public: + // If regexp is false, use unix glob style matching. + // If regexp is true, use TCL regular expression matching. + // 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); + // Use unix glob style matching. + PatternMatch(const char *pattern); + PatternMatch(const char *pattern, + const PatternMatch *inherit_from); + bool match(const char *str) const; + bool matchNoCase(const char *str) const; + const char *pattern() const { return pattern_; } + bool isRegexp() const { return is_regexp_; } + bool nocase() const { return nocase_; } + Tcl_Interp *tclInterp() const { return interp_; } + bool hasWildcards() const; + +private: + DISALLOW_COPY_AND_ASSIGN(PatternMatch); + void compileRegexp(); + + const char *pattern_; + bool is_regexp_; + bool nocase_; + Tcl_Interp *interp_; + Tcl_RegExp regexp_; +}; + +// Error thrown by Pattern constructor. +class RegexpCompileError : public StaException +{ +public: + explicit RegexpCompileError(const char *error); + virtual ~RegexpCompileError() throw() {} + virtual const char *what() const throw(); + +private: + const char *error_; +}; + +// Simple pattern match +// '*' matches zero or more characters +// '?' matches any character +bool +patternMatch(const char *pattern, + const char *str); +bool +patternMatchNoCase(const char *pattern, + const char *str, + bool nocase); +// Predicate to find out if there are wildcard characters in the pattern. +bool +patternWildcards(const char *pattern); + +} // namespace +#endif diff --git a/util/Pool.hh b/util/Pool.hh new file mode 100644 index 0000000..bb17992 --- /dev/null +++ b/util/Pool.hh @@ -0,0 +1,340 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_POOL_H +#define STA_POOL_H + +#include +#include // max +#include "DisallowCopyAssign.hh" +#include "Error.hh" +#include "Vector.hh" +#include "ObjectIndex.hh" + +namespace sta { + +using std::max; + +typedef Vector DeletedListHeads; + +template +class PoolBlock +{ +public: + explicit PoolBlock(ObjectIndex size, + ObjectIndex begin_index); + OBJ *makeObject(); + OBJ *makeObjects(ObjectIndex count); + ObjectIndex index(const OBJ *object); + OBJ *find(ObjectIndex index); + PoolBlock *nextBlock() const { return next_block_; } + void setNextBlock(PoolBlock *next); + ObjectIndex size() const { return objects_.size(); } + +private: + DISALLOW_COPY_AND_ASSIGN(PoolBlock); + + std::vector objects_; + ObjectIndex begin_index_; + ObjectIndex next_free_; + PoolBlock *next_block_; + static constexpr unsigned min_initial_size_ = 100; +}; + +template +PoolBlock::PoolBlock(ObjectIndex size, + ObjectIndex begin_index) : + objects_(size), + begin_index_(begin_index), + next_free_(0), + next_block_(nullptr) +{ +} + +template +OBJ * +PoolBlock::makeObject() +{ + if (next_free_ < objects_.size()) + return &objects_[next_free_++]; + else + return nullptr; +} + +template +OBJ * +PoolBlock::makeObjects(ObjectIndex count) +{ + if ((next_free_ + count - 1) < objects_.size()) { + OBJ *object = &objects_[next_free_]; + next_free_ += count; + return object; + } + else + return nullptr; +} + +template +ObjectIndex +PoolBlock::index(const OBJ *object) +{ + if (object >= &objects_[0] && object < &objects_[objects_.size()]) + // Index==0 is reserved. + return begin_index_ + object - &objects_[0] + 1; + else + return 0; +} + +template +OBJ * +PoolBlock::find(ObjectIndex index) +{ + // Index==0 is reserved. + ObjectIndex index1 = index - 1; + if (index1 >= begin_index_ + && index1 < begin_index_ + objects_.size()) + return &objects_[index1 - begin_index_]; + else + return nullptr; +} + +template +void +PoolBlock::setNextBlock(PoolBlock *next) +{ + next_block_ = next; +} + +//////////////////////////////////////////////////////////////// + +template +class Pool +{ +public: + Pool(); + explicit Pool(ObjectIndex size); + explicit Pool(ObjectIndex size, + float growth_factor); + ~Pool(); + OBJ *makeObject(); + OBJ *makeObjects(ObjectIndex count); + void deleteObject(OBJ *object); + void deleteObject(ObjectIndex index); + void deleteObjects(OBJ *objects, + ObjectIndex count); + void deleteObjects(ObjectIndex index, + ObjectIndex count); + // Index=0 is reserved for the nullptr object pointer. + ObjectIndex index(const OBJ *object) const; + OBJ *find(ObjectIndex index) const; + ObjectIndex size() const { return size_; } + void clear(); + +protected: + PoolBlock *makeBlock(ObjectIndex block_size); + OBJ *findDeletedObject(ObjectIndex count); + void deleteObjects(OBJ *object, + ObjectIndex index, + ObjectIndex count); + + ObjectIndex size_; + float growth_factor_; + PoolBlock *blocks_; + PoolBlock *last_block_; + // Index of deleted objects that are the head of a list indexed by + // object array count. + DeletedListHeads deleted_list_heads_; +}; + +template +Pool::Pool(ObjectIndex size) : + Pool(size, .2F) +{ +} + +template +Pool::Pool(ObjectIndex size, + float growth_factor) : + size_(0), + growth_factor_(growth_factor), + blocks_(nullptr), + last_block_(nullptr) +{ + makeBlock(size); +} + +template +Pool::~Pool() +{ + PoolBlock *next; + for (PoolBlock *block = blocks_; block; block = next) { + next = block->nextBlock(); + delete block; + } +} + +template +OBJ * +Pool::makeObject() +{ + OBJ *object = findDeletedObject(1); + if (object == nullptr) { + object = last_block_->makeObject(); + if (object == nullptr) { + ObjectIndex block_size=static_cast(size_*growth_factor_+2); + PoolBlock *block = makeBlock(block_size); + object = block->makeObject(); + } + } + return object; +} + +template +OBJ * +Pool::findDeletedObject(ObjectIndex count) +{ + if (deleted_list_heads_.size() > count) { + ObjectIndex index = deleted_list_heads_[count]; + if (index) { + OBJ *object = find(index); + ObjectIndex next_index = *reinterpret_cast(object); + deleted_list_heads_[count] = next_index; + return object; + } + } + return nullptr; +} + +template +OBJ * +Pool::makeObjects(ObjectIndex count) +{ + OBJ *objects = findDeletedObject(count); + if (objects == nullptr) { + objects = last_block_->makeObjects(count); + if (objects == nullptr) { + ObjectIndex block_size=max(static_cast(size_*growth_factor_+2), + count); + PoolBlock *block = makeBlock(block_size); + objects = block->makeObjects(count); + } + } + return objects; +} + +template +PoolBlock * +Pool::makeBlock(ObjectIndex block_size) +{ + PoolBlock *block = new PoolBlock(block_size, size_); + // Add new block to end of block list so searches start with + // the first block. + if (last_block_) + last_block_->setNextBlock(block); + else + blocks_ = block; + last_block_ = block; + size_ += block_size; + return block; +} + +template +ObjectIndex +Pool::index(const OBJ *object) const +{ + if (object) { + for (PoolBlock *block = blocks_; block; block = block->nextBlock()) { + ObjectIndex index = block->index(object); + if (index > 0) + return index; + } + internalError("object index not found in pool"); + return 0; + } + else + return 0; +} + +template +OBJ * +Pool::find(ObjectIndex index) const +{ + if (index) { + for (PoolBlock *block = blocks_; block; block = block->nextBlock()) { + OBJ *object = block->find(index); + if (object) + return object; + } + internalError("object index not found in pool"); + return nullptr; + } + else + return nullptr; +} + +template +void +Pool::deleteObject(OBJ *object) +{ + deleteObjects(object, index(object), 1); +} + +template +void +Pool::deleteObject(ObjectIndex index) +{ + deleteObjects(find(index), index, 1); +} + +template +void +Pool::deleteObjects(OBJ *objects, + ObjectIndex count) +{ + deleteObjects(objects, index(objects), count); +} + +template +void +Pool::deleteObjects(ObjectIndex index, + ObjectIndex count) +{ + deleteObjects(find(index), index, count); +} + +template +void +Pool::deleteObjects(OBJ *object, + ObjectIndex index, + ObjectIndex count) +{ + if (deleted_list_heads_.size() <= count) + deleted_list_heads_.resize(count + 1); + ObjectIndex next_index = deleted_list_heads_[count]; + *reinterpret_cast(object) = next_index; + deleted_list_heads_[count] = index; +} + +template +void +Pool::clear() +{ + last_block_ = blocks_; + last_block_->clear(); +} + +} // Namespace +#endif diff --git a/util/Pthread.cc b/util/Pthread.cc new file mode 100644 index 0000000..972254b --- /dev/null +++ b/util/Pthread.cc @@ -0,0 +1,149 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "StaConfig.hh" + +#if !PTHREADS + +#include "Pthread.hh" + +namespace sta { + +int +pthread_create(pthread_t *, + const pthread_attr_t *, + void *(*) (void *), + void *) +{ + return 0; +} + +int +pthread_join(pthread_t, + void **) +{ + return 0; +} + +int +pthread_attr_init(pthread_attr_t *) +{ + return 0; +} + +int +pthread_attr_setscope(pthread_attr_t *, + int) +{ + return 0; +} + +int +pthread_cond_init(pthread_cond_t *, + const pthread_condattr_t *) +{ + return 0; +} + +int +pthread_cond_destroy(pthread_cond_t *) +{ + return 0; +} + +int +pthread_cond_signal(pthread_cond_t *) +{ + return 0; +} + +int +pthread_cond_broadcast(pthread_cond_t *) +{ + return 0; +} + +int +pthread_cond_wait(pthread_cond_t *, + pthread_mutex_t *) +{ + return 0; +} + +int +pthread_mutex_init(pthread_mutex_t *, + const pthread_mutexattr_t *) +{ + return 0; +} + +int +pthread_mutex_destroy(pthread_mutex_t *) +{ + return 0; +} + +int +pthread_mutex_lock(pthread_mutex_t *) +{ + return 0; +} + +int +pthread_mutex_trylock(pthread_mutex_t *) +{ + return 0; +} + +int +pthread_mutex_unlock(pthread_mutex_t *) +{ + return 0; +} + +int +pthread_rwlock_init(pthread_rwlock_t *, + const pthread_rwlockattr_t *) +{ + return 0; +} + +int +pthread_rwlock_destroy(pthread_rwlock_t *) +{ + return 0; +} + +int +pthread_rwlock_rdlock(pthread_rwlock_t *) +{ + return 0; +} + +int +pthread_rwlock_wrlock(pthread_rwlock_t *) +{ + return 0; +} + +int +pthread_rwlock_unlock(pthread_rwlock_t *) +{ + return 0; +} + +} // namespace +#endif diff --git a/util/Pthread.hh b/util/Pthread.hh new file mode 100644 index 0000000..a7de84c --- /dev/null +++ b/util/Pthread.hh @@ -0,0 +1,92 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_PTHREAD_H +#define STA_PTHREAD_H + +#include "StaConfig.hh" + +#if PTHREADS + #include + #define STA_PTHREAD_SCOPE_SYSTEM PTHREAD_SCOPE_SYSTEM +#else + +// Define standins for pthread API. + +namespace sta { + +#define STA_PTHREAD_SCOPE_SYSTEM 0 + +typedef int pthread_cond_t; +typedef int pthread_condattr_t; +typedef int pthread_mutex_t; +typedef int pthread_mutexattr_t; +typedef int pthread_rwlock_t; +typedef int pthread_rwlockattr_t; +typedef int pthread_t; +typedef int pthread_attr_t; + +int +pthread_create(pthread_t *, + const pthread_attr_t *, + void *(*) (void *), void *); +int +pthread_join(pthread_t, void **); +int +pthread_attr_init(pthread_attr_t *); +int +pthread_attr_setscope(pthread_attr_t *, int); + +int +pthread_cond_init(pthread_cond_t *, + const pthread_condattr_t *); +int +pthread_cond_destroy(pthread_cond_t *); +int +pthread_cond_signal(pthread_cond_t *); +int +pthread_cond_broadcast(pthread_cond_t *); +int +pthread_cond_wait(pthread_cond_t *, + pthread_mutex_t *); + +int +pthread_mutex_init(pthread_mutex_t *, + const pthread_mutexattr_t *); +int +pthread_mutex_destroy(pthread_mutex_t *); +int +pthread_mutex_lock(pthread_mutex_t *); +int +pthread_mutex_trylock(pthread_mutex_t *); +int +pthread_mutex_unlock(pthread_mutex_t *); + +int +pthread_rwlock_init(pthread_rwlock_t *, + const pthread_rwlockattr_t *); +int +pthread_rwlock_destroy(pthread_rwlock_t *); +int +pthread_rwlock_rdlock(pthread_rwlock_t *); +int +pthread_rwlock_wrlock(pthread_rwlock_t *); +int +pthread_rwlock_unlock(pthread_rwlock_t *); + +} // namespace +#endif // PTHREAD +#endif // STA_PTHREAD_H diff --git a/util/ReadWriteLock.cc b/util/ReadWriteLock.cc new file mode 100644 index 0000000..1d4042e --- /dev/null +++ b/util/ReadWriteLock.cc @@ -0,0 +1,33 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "ReadWriteLock.hh" + +namespace sta { + +ReadWriteLock::ReadWriteLock() +{ + int error = pthread_rwlock_init(&rw_lock_, NULL); + CheckThreadError(error); +} + +ReadWriteLock::~ReadWriteLock() +{ + pthread_rwlock_destroy(&rw_lock_); +} + +} // namespace diff --git a/util/ReadWriteLock.hh b/util/ReadWriteLock.hh new file mode 100644 index 0000000..2bf5df5 --- /dev/null +++ b/util/ReadWriteLock.hh @@ -0,0 +1,66 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_READ_WRITE_LOCK_H +#define STA_READ_WRITE_LOCK_H + +#include "DisallowCopyAssign.hh" +#include "Error.hh" +#include "Pthread.hh" +#include "Mutex.hh" +#include "Condition.hh" + +namespace sta { + +// C++ wrapper/facade for pthread_rwlock_t. +class ReadWriteLock +{ +public: + ReadWriteLock(); + ~ReadWriteLock(); + void readLock(); + void writeLock(); + void unlock(); + +private: + DISALLOW_COPY_AND_ASSIGN(ReadWriteLock); + + pthread_rwlock_t rw_lock_; +}; + +inline void +ReadWriteLock::readLock() +{ + int error = pthread_rwlock_rdlock(&rw_lock_); + CheckThreadError(error); +} + +inline void +ReadWriteLock::writeLock() +{ + int error = pthread_rwlock_wrlock(&rw_lock_); + CheckThreadError(error); +} + +inline void +ReadWriteLock::unlock() +{ + int error = pthread_rwlock_unlock(&rw_lock_); + CheckThreadError(error); +} + +} // namespace +#endif diff --git a/util/Report.cc b/util/Report.cc new file mode 100644 index 0000000..797b6e8 --- /dev/null +++ b/util/Report.cc @@ -0,0 +1,295 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include // min +#include "Machine.hh" +#include "Error.hh" +#include "Report.hh" + +namespace sta { + +using std::min; + +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() +{ + delete [] buffer_; +} + +void +Report::printToBuffer(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); + buffer_length_ = vsnprint(buffer_, buffer_size_, fmt, args); + if (buffer_length_ >= buffer_size_) { + delete [] buffer_; + buffer_size_ = buffer_length_ * 2; + buffer_ = new char[buffer_size_]; + buffer_length_ = vsnprint(buffer_, buffer_size_, fmt, args_copy); + } + va_end(args_copy); +} + +size_t +Report::printString(const char *buffer, size_t length) +{ + size_t ret = length; + if (redirect_to_string_) + redirectStringPrint(buffer, length); + else { + if (redirect_stream_) + ret = min(ret, fwrite(buffer, sizeof(char), length, redirect_stream_)); + else + ret = min(ret, printConsole(buffer, length)); + if (log_stream_) + ret = min(ret, fwrite(buffer, sizeof(char), length, log_stream_)); + } + return ret; +} + +void +Report::print(const string *str) +{ + printString(str->c_str(), str->size()); +} + +void +Report::print(const string &str) +{ + printString(str.c_str(), str.size()); +} + +void +Report::vprint(const char *fmt, va_list args) +{ + printToBuffer(fmt, args); + printString(buffer_, buffer_length_); +} + +void +Report::print(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vprint(fmt, args); + va_end(args); +} + +size_t +Report::printError(const char *buffer, size_t length) +{ + size_t ret = length; + if (redirect_to_string_) + redirectStringPrint(buffer, length); + else { + if (redirect_stream_) + ret = min(ret, fwrite(buffer, sizeof(char), length, redirect_stream_)); + else + ret = min(ret, printErrorConsole(buffer, length)); + if (log_stream_) + ret = min(ret, fwrite(buffer, sizeof(char), length, log_stream_)); + } + return ret; +} + +void +Report::vprintError(const char *fmt, va_list args) +{ + printToBuffer(fmt, args); + printError(buffer_, buffer_length_); +} + +void +Report::printError(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vprintError(fmt, args); + va_end(args); +} + +void +Report::printDebug(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vprint(fmt, args); + va_end(args); +} + +void +Report::vprintDebug(const char *fmt, va_list args) +{ + vprint(fmt, args); +} + +void +Report::error(const char *fmt, ...) +{ + printError("Error: "); + va_list args; + va_start(args, fmt); + vprintError(fmt, args); + va_end(args); +} + +void +Report::verror(const char *fmt, va_list args) +{ + printError("Error: "); + vprintError(fmt, args); +} + +void +Report::fileError(const char *filename, int line, const char *fmt, ...) +{ + printError("Error: %s, line %d ", filename, line); + va_list args; + va_start(args, fmt); + vprintError(fmt, args); + va_end(args); +} + +void +Report::vfileError(const char *filename, int line, const char *fmt, + va_list args) +{ + printError("Error: %s, line %d ", filename, line); + vprintError(fmt, args); +} + +void +Report::printWarn(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vprintError(fmt, args); + va_end(args); +} + +void +Report::vprintWarn(const char *fmt, va_list args) +{ + vprintError(fmt, args); +} + +void +Report::warn(const char *fmt, ...) +{ + printWarn("Warning: "); + va_list args; + va_start(args, fmt); + vprintWarn(fmt, args); + va_end(args); +} + +void +Report::vwarn(const char *fmt, va_list args) +{ + printWarn("Warning: "); + vprintWarn(fmt, args); +} + +void +Report::fileWarn(const char *filename, int line, const char *fmt, ...) +{ + printWarn("Warning: %s, line %d ", filename, line); + va_list args; + va_start(args, fmt); + vprintWarn(fmt, args); + va_end(args); +} + +void +Report::vfileWarn(const char *filename, int line, const char *fmt, + va_list args) +{ + printWarn("Warning: %s, line %d ", filename, line); + vprintWarn(fmt, args); +} + +void +Report::logBegin(const char *filename) +{ + log_stream_ = fopen(filename, "w"); + if (log_stream_ == nullptr) + throw FileNotWritable(filename); +} + +void +Report::logEnd() +{ + if (log_stream_) + fclose(log_stream_); + log_stream_ = nullptr; +} + +void +Report::redirectFileBegin(const char *filename) +{ + redirect_stream_ = fopen(filename, "w"); + if (redirect_stream_ == nullptr) + throw FileNotWritable(filename); +} + +void +Report::redirectFileAppendBegin(const char *filename) +{ + redirect_stream_ = fopen(filename, "a"); + if (redirect_stream_ == nullptr) + throw FileNotWritable(filename); +} + +void +Report::redirectFileEnd() +{ + if (redirect_stream_) + fclose(redirect_stream_); + redirect_stream_ = nullptr; +} + +void +Report::redirectStringBegin() +{ + redirect_to_string_ = true; + redirect_string_.clear(); +} + +const char * +Report::redirectStringEnd() +{ + redirect_to_string_ = false; + return redirect_string_.c_str(); +} + +void +Report::redirectStringPrint(const char *buffer, size_t length) +{ + redirect_string_.append(buffer, length); +} + +} // namespace diff --git a/util/Report.hh b/util/Report.hh new file mode 100644 index 0000000..e6f5281 --- /dev/null +++ b/util/Report.hh @@ -0,0 +1,122 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_REPORT_H +#define STA_REPORT_H + +#include +#include +#include +#include +#include "DisallowCopyAssign.hh" + +namespace sta { + +using std::string; + +// 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. +class Report +{ +public: + Report(); + virtual ~Report(); + + // Primitives to print output. + // Return the number of characters written. + virtual size_t printString(const char *buffer, size_t length); + virtual void print(const char *fmt, ...); + virtual void vprint(const char *fmt, va_list args); + void print(const string *str); + void print(const string &str); + + // Print to debug stream (same as output stream). + virtual void printDebug(const char *fmt, ...) + __attribute__((format (printf, 2, 3))); + virtual void vprintDebug(const char *fmt, va_list args); + + // Print to error stream. + // Return the number of characters written. + virtual size_t printError(const char *buffer, size_t length); + virtual void printError(const char *fmt, ...) + __attribute__((format (printf, 2, 3))); + virtual void vprintError(const char *fmt, va_list args); + + // Report error. + virtual void error(const char *fmt, ...) + __attribute__((format (printf, 2, 3))); + virtual void verror(const char *fmt, va_list args); + // Report error in a file. + virtual void fileError(const char *filename, int line, const char *fmt, ...) + __attribute__((format (printf, 4, 5))); + virtual void vfileError(const char *filename, int line, const char *fmt, + va_list args); + + // Print to warning stream (same as error stream). + virtual void printWarn(const char *fmt, ...) + __attribute__((format (printf, 2, 3))); + virtual void vprintWarn(const char *fmt, va_list args); + // Report warning. + virtual void warn(const char *fmt, ...) + __attribute__((format (printf, 2, 3))); + virtual void vwarn(const char *fmt, va_list args); + // Report warning in a file. + virtual void fileWarn(const char *filename, int line, const char *fmt, ...) + __attribute__((format (printf, 4, 5))); + virtual void vfileWarn(const char *filename, int line, const char *fmt, + va_list args); + + // Log output to filename until logEnd is called. + virtual void logBegin(const char *filename); + virtual void logEnd(); + + // Redirect output to filename until redirectFileEnd is called. + virtual void redirectFileBegin(const char *filename); + // Redirect append output to filename until redirectFileEnd is called. + virtual void redirectFileAppendBegin(const char *filename); + virtual void redirectFileEnd(); + // Redirect output to a string until redirectStringEnd is called. + virtual void redirectStringBegin(); + virtual const char *redirectStringEnd(); + virtual void setTclInterp(Tcl_Interp *) {} + +protected: + // Primitive to print output on the console. + // Return the number of characters written. + virtual size_t printConsole(const char *buffer, size_t length) = 0; + // Primitive to print error, warning and debug output. + // Return the number of characters written. + virtual size_t printErrorConsole(const char *buffer, size_t length) = 0; + void printToBuffer(const char *fmt, va_list args); + void redirectStringPrint(const char *buffer, size_t length); + + FILE *log_stream_; + FILE *redirect_stream_; + bool redirect_to_string_; + string redirect_string_; + // Buffer to support printf style arguments. + size_t buffer_size_; + char *buffer_; + // Length of string in buffer. + size_t buffer_length_; + +private: + DISALLOW_COPY_AND_ASSIGN(Report); +}; + +} // namespace +#endif diff --git a/util/ReportStd.cc b/util/ReportStd.cc new file mode 100644 index 0000000..4cc4e91 --- /dev/null +++ b/util/ReportStd.cc @@ -0,0 +1,63 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "Report.hh" +#include "ReportStd.hh" + +namespace sta { + +// Output streams that talk to stdout and stderr streams. +class ReportStd : public Report +{ +public: + ReportStd(); + +protected: + virtual size_t printConsole(const char *buffer, size_t length); + virtual size_t printErrorConsole(const char *buffer, size_t length); + +private: + DISALLOW_COPY_AND_ASSIGN(ReportStd); +}; + +Report * +makeReportStd() +{ + return new ReportStd; +} + +ReportStd::ReportStd() : + Report() +{ +} + +size_t +ReportStd::printConsole(const char *buffer, size_t length) +{ + return fwrite(buffer, sizeof(char), length, stdout); +} + +size_t +ReportStd::printErrorConsole(const char *buffer, size_t length) +{ + return fwrite(buffer, sizeof(char), length, stderr); +} + +} // namespace diff --git a/util/ReportStd.hh b/util/ReportStd.hh new file mode 100644 index 0000000..681f475 --- /dev/null +++ b/util/ReportStd.hh @@ -0,0 +1,30 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_REPORT_STD_H +#define STA_REPORT_STD_H + +namespace sta { + +class Report; + +// Make output streams that talk to stdout and stderr. +// This is useful for applications that do not link tcl. +Report * +makeReportStd(); + +} // namespace +#endif diff --git a/util/ReportTcl.cc b/util/ReportTcl.cc new file mode 100644 index 0000000..9925cf6 --- /dev/null +++ b/util/ReportTcl.cc @@ -0,0 +1,458 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include "Machine.hh" +#include "ReportTcl.hh" + +namespace sta { + +using ::Tcl_Channel; +using ::Tcl_GetChannelType; +using ::Tcl_ChannelType; +using ::ClientData; +using ::Tcl_GetChannelInstanceData; +using ::Tcl_DriverOutputProc; +using ::Tcl_ChannelOutputProc; + +extern "C" { + +// Tcl8.4 adds const's to Tcl_ChannelType but earlier versions +// don't have them. +#ifndef CONST84 +#define CONST84 +#endif + +static int +encapOutputProc(ClientData instanceData, CONST84 char *buf, int toWrite, + int *errorCodePtr); +static int +encapErrorOutputProc(ClientData instanceData, CONST84 char *buf, int toWrite, + int *errorCodePtr); +static int +encapCloseProc(ClientData instanceData, Tcl_Interp *interp); +static int +encapSetOptionProc(ClientData instanceData, Tcl_Interp *interp, + CONST84 char *optionName, CONST84 char *value); +static int +encapGetOptionProc(ClientData instanceData, Tcl_Interp *interp, + CONST84 char *optionName, Tcl_DString *dsPtr); +static int +encapInputProc(ClientData instanceData, char *buf, int bufSize, + int *errorCodePtr); +static int +encapSeekProc(ClientData instanceData, long offset, int seekMode, + int *errorCodePtr); +static void +encapWatchProc(ClientData instanceData, int mask); +static int +encapGetHandleProc(ClientData instanceData, int direction, + ClientData *handlePtr); +static int +encapBlockModeProc(ClientData instanceData, int mode); +} // extern "C" + +#ifdef TCL_CHANNEL_VERSION_5 +Tcl_ChannelType tcl_encap_type_stdout = { + const_cast("file"), + TCL_CHANNEL_VERSION_4, + encapCloseProc, + encapInputProc, + encapOutputProc, + encapSeekProc, + encapSetOptionProc, + encapGetOptionProc, + encapWatchProc, + encapGetHandleProc, + nullptr, // close2Proc + encapBlockModeProc, + nullptr, // flushProc + nullptr, // handlerProc + nullptr, // wideSeekProc + nullptr, // threadActionProc + nullptr // truncateProc +}; + +Tcl_ChannelType tcl_encap_type_stderr = { + const_cast("file"), + TCL_CHANNEL_VERSION_4, + encapCloseProc, + encapInputProc, + encapErrorOutputProc, + encapSeekProc, + encapSetOptionProc, + encapGetOptionProc, + encapWatchProc, + encapGetHandleProc, + nullptr, // close2Proc + encapBlockModeProc, + nullptr, // flushProc + nullptr, // handlerProc + nullptr, // wideSeekProc + nullptr, // threadActionProc + nullptr // truncateProc +}; + +#else +#ifdef TCL_CHANNEL_VERSION_4 +// Tcl 8.4.12 +Tcl_ChannelType tcl_encap_type_stdout = { + const_cast("file"), + TCL_CHANNEL_VERSION_4, + encapCloseProc, + encapInputProc, + encapOutputProc, + encapSeekProc, + encapSetOptionProc, + encapGetOptionProc, + encapWatchProc, + encapGetHandleProc, + nullptr, // close2Proc + encapBlockModeProc, + nullptr, // flushProc + nullptr, // handlerProc + nullptr, // wideSeekProc + nullptr // threadActionProc +}; + +Tcl_ChannelType tcl_encap_type_stderr = { + const_cast("file"), + TCL_CHANNEL_VERSION_4, + encapCloseProc, + encapInputProc, + encapErrorOutputProc, + encapSeekProc, + encapSetOptionProc, + encapGetOptionProc, + encapWatchProc, + encapGetHandleProc, + nullptr, // close2Proc + encapBlockModeProc, + nullptr, // flushProc + nullptr, // handlerProc + nullptr, // wideSeekProc + nullptr // threadActionProc +}; + +#else +#ifdef TCL_CHANNEL_VERSION_3 +// Tcl 8.4 +Tcl_ChannelType tcl_encap_type_stdout = { + const_cast("file"), + TCL_CHANNEL_VERSION_3, + encapCloseProc, + encapInputProc, + encapOutputProc, + encapSeekProc, + encapSetOptionProc, + encapGetOptionProc, + encapWatchProc, + encapGetHandleProc, + nullptr, // close2Proc + encapBlockModeProc, + nullptr, // flushProc + nullptr, // handlerProc + nullptr // wideSeekProc +}; + +Tcl_ChannelType tcl_encap_type_stderr = { + const_cast("file"), + TCL_CHANNEL_VERSION_3, + encapCloseProc, + encapInputProc, + encapErrorOutputProc, + encapSeekProc, + encapSetOptionProc, + encapGetOptionProc, + encapWatchProc, + encapGetHandleProc, + nullptr, // close2Proc + encapBlockModeProc, + nullptr, // flushProc + nullptr, // handlerProc + nullptr // wideSeekProc +}; + +#else +#ifdef TCL_CHANNEL_VERSION_2 + +// Tcl 8.3.2 +Tcl_ChannelType tcl_encap_type_stdout = { + const_cast("file"), + TCL_CHANNEL_VERSION_2, + encapCloseProc, + encapInputProc, + encapOutputProc, + encapSeekProc, + encapSetOptionProc, + encapGetOptionProc, + encapWatchProc, + encapGetHandleProc, + nullptr, // close2Proc + encapBlockModeProc, + nullptr, // flushProc + nullptr // handlerProc +}; + +Tcl_ChannelType tcl_encap_type_stderr = { + const_cast("file"), + TCL_CHANNEL_VERSION_2, + encapCloseProc, + encapInputProc, + encapErrorOutputProc, + encapSeekProc, + encapSetOptionProc, + encapGetOptionProc, + encapWatchProc, + encapGetHandleProc, + nullptr, // close2Proc + encapBlockModeProc, + nullptr, // flushProc + nullptr // handlerProc +}; + +#else + +// Tcl 8.2 +Tcl_ChannelType tcl_encap_type_stdout = { + const_cast("file"), + encapBlockModeProc, + encapCloseProc, + encapInputProc, + encapOutputProc, + encapSeekProc, + encapSetOptionProc, + encapGetOptionProc, + encapWatchProc, + encapGetHandleProc, + nullptr // close2Proc +}; + +Tcl_ChannelType tcl_encap_type_stderr = { + const_cast("file"), + encapBlockModeProc, + encapCloseProc, + encapInputProc, + encapErrorOutputProc, + encapSeekProc, + encapSetOptionProc, + encapGetOptionProc, + encapWatchProc, + encapGetHandleProc, + nullptr // close2Proc +}; + +#endif +#endif +#endif +#endif + +//////////////////////////////////////////////////////////////// + +ReportTcl::ReportTcl() : + Report(), + interp_(nullptr), + tcl_stdout_(nullptr), + tcl_stderr_(nullptr), + tcl_encap_stdout_(nullptr), + tcl_encap_stderr_(nullptr) +{ +} + +ReportTcl::~ReportTcl() +{ + tcl_encap_stdout_ = nullptr; + tcl_encap_stderr_ = nullptr; + Tcl_UnstackChannel(interp_, tcl_stdout_); + Tcl_UnstackChannel(interp_, tcl_stderr_); +} + +// Encapsulate the Tcl stdout and stderr channels to print to the +// report object so that the output from Tcl puts and errors can be +// logged and redirected. +void +ReportTcl::setTclInterp(Tcl_Interp *interp) +{ + interp_ = interp; + tcl_stdout_ = Tcl_GetStdChannel(TCL_STDOUT); + tcl_stderr_ = Tcl_GetStdChannel(TCL_STDERR); + tcl_encap_stdout_ = Tcl_StackChannel(interp, &tcl_encap_type_stdout, this, + TCL_WRITABLE, tcl_stdout_); + tcl_encap_stderr_ = Tcl_StackChannel(interp, &tcl_encap_type_stderr, this, + TCL_WRITABLE, tcl_stderr_); +} + +size_t +ReportTcl::printConsole(const char *buffer, size_t length) +{ + return printTcl(tcl_stdout_, buffer, length); +} + +size_t +ReportTcl::printErrorConsole(const char *buffer, size_t length) +{ + return printTcl(tcl_stderr_, buffer, length); +} + +size_t +ReportTcl::printTcl(Tcl_Channel channel, const char *buffer, size_t length) +{ + const Tcl_ChannelType *ch_type = Tcl_GetChannelType(channel); + Tcl_DriverOutputProc *output_proc = Tcl_ChannelOutputProc(ch_type); + int error_code; + ClientData clientData = Tcl_GetChannelInstanceData(channel); + return output_proc(clientData, const_cast(buffer), + static_cast(length), + &error_code); +} + +// 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) +{ + Tcl_Flush(tcl_encap_stdout_); + Tcl_Flush(tcl_encap_stderr_); + Report::logBegin(filename); +} + +void +ReportTcl::logEnd() +{ + if (tcl_encap_stdout_) + Tcl_Flush(tcl_encap_stdout_); + if (tcl_encap_stderr_) + Tcl_Flush(tcl_encap_stderr_); + Report::logEnd(); +} + +void +ReportTcl::redirectFileBegin(const char *filename) +{ + Tcl_Flush(tcl_encap_stdout_); + Tcl_Flush(tcl_encap_stderr_); + Report::redirectFileBegin(filename); +} + +void +ReportTcl::redirectFileAppendBegin(const char *filename) +{ + Tcl_Flush(tcl_encap_stdout_); + Tcl_Flush(tcl_encap_stderr_); + Report::redirectFileAppendBegin(filename); +} + +void +ReportTcl::redirectFileEnd() +{ + if (tcl_encap_stdout_) + Tcl_Flush(tcl_encap_stdout_); + if (tcl_encap_stderr_) + Tcl_Flush(tcl_encap_stderr_); + Report::redirectFileEnd(); +} + +void +ReportTcl::redirectStringBegin() +{ + if (tcl_encap_stdout_) + Tcl_Flush(tcl_encap_stdout_); + if (tcl_encap_stderr_) + Tcl_Flush(tcl_encap_stderr_); + Report::redirectStringBegin(); +} + +const char * +ReportTcl::redirectStringEnd() +{ + if (tcl_encap_stdout_) + Tcl_Flush(tcl_encap_stdout_); + if (tcl_encap_stderr_) + Tcl_Flush(tcl_encap_stderr_); + return Report::redirectStringEnd(); +} + +//////////////////////////////////////////////////////////////// + +static int +encapOutputProc(ClientData instanceData, CONST84 char *buf, int toWrite, + int *) +{ + ReportTcl *report = reinterpret_cast(instanceData); + return static_cast(report->printString(buf, toWrite)); +} + +static int +encapErrorOutputProc(ClientData instanceData, CONST84 char *buf, int toWrite, + int *) +{ + ReportTcl *report = reinterpret_cast(instanceData); + return static_cast(report->printString(buf, toWrite)); +} + +static int +encapInputProc(ClientData, char *, int, int *) +{ + return -1; +} + +static int +encapCloseProc(ClientData instanceData, Tcl_Interp *) +{ + ReportTcl *report = reinterpret_cast(instanceData); + report->logEnd(); + report->redirectFileEnd(); + report->redirectStringEnd(); + return 0; +} + +static int +encapSetOptionProc(ClientData, Tcl_Interp *, CONST84 char *, CONST84 char *) +{ + return 0; +} + +static int +encapGetOptionProc(ClientData, Tcl_Interp *, CONST84 char *, Tcl_DString *) +{ + return 0; +} + +static int +encapSeekProc(ClientData, long, int, int *) +{ + return -1; +} + +static void +encapWatchProc(ClientData, int) +{ +} + +static int +encapGetHandleProc(ClientData, int, ClientData *) +{ + return TCL_ERROR; +} + +static int +encapBlockModeProc(ClientData, int) +{ + return 0; +} + +} // namespace diff --git a/util/ReportTcl.hh b/util/ReportTcl.hh new file mode 100644 index 0000000..594d069 --- /dev/null +++ b/util/ReportTcl.hh @@ -0,0 +1,69 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_REPORT_TCL_H +#define STA_REPORT_TCL_H + +#include +#include "DisallowCopyAssign.hh" +#include "Report.hh" + +namespace sta { + +// Output streams that talk to TCL channels. +// This directs all output on the Report object to tcl stdout and stderr +// channels. +// Tcl output channels are encapsulated to print to the Report object +// that supports redirection and logging as well as printing to the +// underlying channel. +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(); + // This must be called after the Tcl interpreter has been constructed. + // It makes the encapsulated channels. + virtual void setTclInterp(Tcl_Interp *interp); + +protected: + virtual size_t printConsole(const char *buffer, size_t length); + virtual size_t printErrorConsole(const char *buffer, size_t length); + +private: + DISALLOW_COPY_AND_ASSIGN(ReportTcl); + Tcl_ChannelType *makeEncapChannelType(Tcl_Channel channel, + char *channel_name, + Tcl_DriverOutputProc output_proc); + size_t printTcl(Tcl_Channel channel, const char *buffer, size_t length); + + Tcl_Interp *interp_; + // The original tcl channels. + Tcl_Channel tcl_stdout_; + Tcl_Channel tcl_stderr_; + // Encapsulated channels that print on this object. + Tcl_Channel tcl_encap_stdout_; + Tcl_Channel tcl_encap_stderr_; +}; + +} // namespace +#endif diff --git a/util/Set.hh b/util/Set.hh new file mode 100644 index 0000000..d0736f4 --- /dev/null +++ b/util/Set.hh @@ -0,0 +1,230 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_SET_H +#define STA_SET_H + +#include +#include +#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(std::set *set1, + std::set *set2); + + // 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(std::set *set1, + std::set *set2) +{ + if (set1 && !set1->empty() + && set2 && !set2->empty()) { + const std::set *small = set1; + const std::set *big = set2; + if (small->size() > big->size()) { + small = set2; + big = set1; + } + auto iter1 = big->begin(); + auto last1 = big->end(); + auto iter2 = small->begin(); + auto last2 = small->end(); + if (static_cast(small->size() + big->size()) < (small->size() * log(static_cast(big->size())))) { + while (iter1 != last1 && iter2 != last2) { + if (*iter1 < *iter2) + ++iter1; + else if (*iter2 < *iter1) + ++iter2; + else + return true; + } + } + else { + for (/* empty */; iter2 != last2; ++iter2) { + const KEY key2 = *iter2; + if (big->find(key2) != last1) + 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 +#endif diff --git a/util/StaConfig.hh.cmake b/util/StaConfig.hh.cmake new file mode 100644 index 0000000..395df2a --- /dev/null +++ b/util/StaConfig.hh.cmake @@ -0,0 +1,9 @@ +#define STA_VERSION "${STA_VERSION}" + +#define STA_GIT_SHA1 "${STA_GIT_SHA1}" + +#define ZLIB ${ZLIB} + +#define CUDD ${CUDD} + +#define SSTA ${SSTA} diff --git a/util/Stats.cc b/util/Stats.cc new file mode 100644 index 0000000..3192401 --- /dev/null +++ b/util/Stats.cc @@ -0,0 +1,52 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "StringUtil.hh" +#include "Debug.hh" +#include "Stats.hh" + +namespace sta { + +Stats::Stats(Debug *debug) : + debug_(debug) +{ + if (debug->statsLevel() > 0) { + elapsed_begin_ = elapsedRunTime(); + user_begin_ = userRunTime(); + system_begin_ = systemRunTime(); + memory_begin_ = memoryUsage(); + } +} + +void +Stats::report(const char *step) +{ + if (debug_->statsLevel() > 0) { + double elapsed_end = elapsedRunTime(); + double user_end = userRunTime(); + double memory_begin = static_cast(memory_begin_); + double memory_end = static_cast(memoryUsage()); + double memory_delta = memory_end - memory_begin; + debug_->print("stats: %5.1f/%5.1fe %5.1f/%5.1fu %5.1f/%5.1fMB %s\n", + elapsed_end - elapsed_begin_, elapsed_end, + user_end - user_begin_, user_end, + memory_delta * 1e-6, memory_end * 1e-6, + step); + } +} + +} // namespace diff --git a/util/Stats.hh b/util/Stats.hh new file mode 100644 index 0000000..029d799 --- /dev/null +++ b/util/Stats.hh @@ -0,0 +1,45 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_STATS_H +#define STA_STATS_H + +#include // size_t +#include "DisallowCopyAssign.hh" + +namespace sta { + +class Debug; + +// Show run time and memory statistics if the "stats" debug flag is on. +class Stats +{ +public: + explicit Stats(Debug *debug); + void report(const char *step); + +private: + DISALLOW_COPY_AND_ASSIGN(Stats); + + double elapsed_begin_; + double user_begin_; + double system_begin_; + size_t memory_begin_; + Debug *debug_; +}; + +} // namespace +#endif diff --git a/util/StringSeq.cc b/util/StringSeq.cc new file mode 100644 index 0000000..5a5c96c --- /dev/null +++ b/util/StringSeq.cc @@ -0,0 +1,32 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#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/StringSeq.hh b/util/StringSeq.hh new file mode 100644 index 0000000..5189102 --- /dev/null +++ b/util/StringSeq.hh @@ -0,0 +1,31 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_STRING_SEQ_H +#define STA_STRING_SEQ_H + +#include "StringUtil.hh" +#include "Vector.hh" + +namespace sta { + +typedef Vector StringSeq; + +void +deleteContents(StringSeq *strings); + +} // namespace +#endif diff --git a/util/StringSet.cc b/util/StringSet.cc new file mode 100644 index 0000000..bc8307f --- /dev/null +++ b/util/StringSet.cc @@ -0,0 +1,42 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "StringSet.hh" + +namespace sta { + +void +deleteContents(StringSet *strings) +{ + StringSet::Iterator iter(strings); + while (iter.hasNext()) { + const char *string = iter.next(); + stringDelete(string); + } +} + +void +deleteContents(StringSetEq *strings) +{ + StringSetEq::Iterator iter(strings); + while (iter.hasNext()) { + const char *string = iter.next(); + stringDelete(string); + } +} + +} // namespace diff --git a/util/StringSet.hh b/util/StringSet.hh new file mode 100644 index 0000000..13a2188 --- /dev/null +++ b/util/StringSet.hh @@ -0,0 +1,34 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_STRING_SET_H +#define STA_STRING_SET_H + +#include "StringUtil.hh" +#include "Set.hh" + +namespace sta { + +typedef Set StringSet; +typedef Set StringSetEq; + +void +deleteContents(StringSet *strings); +void +deleteContents(StringSetEq *strings); + +} // namespace +#endif diff --git a/util/StringUtil.cc b/util/StringUtil.cc new file mode 100644 index 0000000..f0d98d6 --- /dev/null +++ b/util/StringUtil.cc @@ -0,0 +1,251 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include +#include +#include "Machine.hh" +#include "Mutex.hh" +#include "StringUtil.hh" + +namespace sta { + +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) +{ + for (const char *s = str; *s; s++) { + if (!isdigit(*s)) + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////// + +char * +integerString(int number) +{ + return stringPrint("%d", number); +} + +// 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; +} + +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 int tmp_string_count_ = 100; +static char **tmp_strings_ = nullptr; +static size_t *tmp_string_lengths_ = nullptr; +static int tmp_string_next_; +static std::mutex string_lock_; + +void +initTmpStrings() +{ + size_t initial_length = 100; + + tmp_strings_ = new char*[tmp_string_count_]; + tmp_string_lengths_ = new size_t[tmp_string_count_]; + for (int i = 0; i < tmp_string_count_; i++) { + tmp_strings_[i] = new char[initial_length]; + tmp_string_lengths_[i] = initial_length; + } + tmp_string_next_ = 0; +} + +void +deleteTmpStrings() +{ + if (tmp_strings_) { + for (int i = 0; i < tmp_string_count_; i++) + delete [] tmp_strings_[i]; + delete [] tmp_strings_; + tmp_strings_ = nullptr; + + delete [] tmp_string_lengths_; + tmp_string_lengths_ = nullptr; + } +} + +static void +getTmpString(// Return values. + char *&str, + size_t &length) +{ + UniqueLock lock(string_lock_); + if (tmp_string_next_ == tmp_string_count_) + tmp_string_next_ = 0; + str = tmp_strings_[tmp_string_next_]; + length = tmp_string_lengths_[tmp_string_next_]; + tmp_string_next_++; +} + +char * +makeTmpString(size_t length) +{ + UniqueLock lock(string_lock_); + 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. + stringDelete(tmp_str); + tmp_str = new char[length]; + tmp_strings_[tmp_string_next_] = tmp_str; + tmp_string_lengths_[tmp_string_next_] = length; + } + tmp_string_next_++; + return tmp_str; +} + +//////////////////////////////////////////////////////////////// + +void +trimRight(string &str) +{ + str.erase(str.find_last_not_of(" ") + 1); +} + +void +split(const string &text, + const string &delims, + // Return values. + StringVector &tokens) +{ + auto start = text.find_first_not_of(delims); + auto end = text.find_first_of(delims, start); + while (end != 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) + tokens.push_back(text.substr(start)); +} + +} // namespace diff --git a/util/StringUtil.hh b/util/StringUtil.hh new file mode 100644 index 0000000..d98b33d --- /dev/null +++ b/util/StringUtil.hh @@ -0,0 +1,189 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_STRING_UTIL_H +#define STA_STRING_UTIL_H + +#include +#include +#include +#include "Vector.hh" + +namespace sta { + +using std::string; + +inline bool +stringEq(const char *str1, + const char *str2) +{ + return strcmp(str1, str2) == 0; +} + +// Compare the first length characters. +inline bool +stringEq(const char *str1, + 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) +{ + 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) +{ + return (str1 == nullptr && str2 == nullptr) + || (str1 && str2 && strcasecmp(str1, str2) == 0); +} + +inline bool +stringLess(const char *str1, + const char *str2) +{ + return strcmp(str1, str2) < 0; +} + +inline bool +stringLessIf(const char *str1, + const char *str2) +{ + return (str1 == nullptr && str2 != nullptr) + || (str1 != nullptr && str2 != nullptr && strcmp(str1, str2) < 0); +} + +class CharPtrLess +{ +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; + } +}; + +// 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); +} + +// Delete for strings allocated with new char[]. +inline void +stringDelete(const char *str) +{ + delete [] 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))); +string +stdstrPrint(const char *fmt, + ...) __attribute__((format (printf, 1, 2))); +char * +stringPrintArgs(const char *fmt, + va_list args); +void +stringPrint(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))); +// Caller owns returned string. +char * +integerString(int number); + +char * +makeTmpString(size_t length); +void +initTmpStrings(); +void +deleteTmpStrings(); + +//////////////////////////////////////////////////////////////// + +// Trim right spaces. +void +trimRight(string &str); + +typedef Vector StringVector; + +void +split(const string &text, + const string &delims, + // Return values. + StringVector &tokens); + +} // namespace +#endif diff --git a/util/Thread.cc b/util/Thread.cc new file mode 100644 index 0000000..38abb0e --- /dev/null +++ b/util/Thread.cc @@ -0,0 +1,51 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "ThreadException.hh" +#include "ThreadWorker.hh" +#include "Thread.hh" + +namespace sta { + +ThreadPool Thread::pool_; + +Thread::Thread() : + worker_(NULL) +{ +} + +void +Thread::beginTask(ThreadFunc func, + void *arg) +{ + worker_ = pool_.pop(); + worker_->beginTask(func, arg); +} + +void +Thread::wait() +{ + worker_->wait(); +#if 0 + delete worker_; +#else + pool_.push(worker_); +#endif + worker_ = NULL; +} + +} // namespace diff --git a/util/Thread.hh b/util/Thread.hh new file mode 100644 index 0000000..d54859a --- /dev/null +++ b/util/Thread.hh @@ -0,0 +1,48 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_THREAD_H +#define STA_THREAD_H + +#include "DisallowCopyAssign.hh" +#include "ThreadWorker.hh" +#include "ThreadPool.hh" + +namespace sta { + +// Basic thread class implemented using a pool of threads, +// i.e., the threads are put to sleep when their task is done +// and they are re-cycled for the next task. +class Thread +{ +public: + Thread(); + // After calling start(), the caller must call wait() before + // destroying this object. + void beginTask(ThreadFunc func, + void *arg); + void wait(); + +private: + DISALLOW_COPY_AND_ASSIGN(Thread); + + ThreadWorker *worker_; + + static ThreadPool pool_; +}; + +} // namespace +#endif diff --git a/util/ThreadException.cc b/util/ThreadException.cc new file mode 100644 index 0000000..295648c --- /dev/null +++ b/util/ThreadException.cc @@ -0,0 +1,38 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "StringUtil.hh" +#include "ThreadException.hh" + +namespace sta { + +ThreadException::ThreadException(const char *filename, + int line, + int error) : + StaExceptionLine(filename, line), + error_(error) +{ +} + +const char * +ThreadException::what() const throw() +{ + const char *msg = strerror(error_); + return stringPrintTmp("Thread error in %s:%d %s.", filename_, line_, msg); +} + +} diff --git a/util/ThreadException.hh b/util/ThreadException.hh new file mode 100644 index 0000000..aad2cb4 --- /dev/null +++ b/util/ThreadException.hh @@ -0,0 +1,41 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_THREAD_EXCEPTION_H +#define STA_THREAD_EXCEPTION_H + +#include "Error.hh" + +namespace sta { + +class ThreadException : public StaExceptionLine +{ +public: + ThreadException(const char *filename, + int line, + int error); + virtual const char *what() const throw(); + +protected: + int error_; +}; + +#define CheckThreadError(error) \ + if (error != 0) \ + throw ThreadException(__FILE__, __LINE__, error) + +} // namespace +#endif diff --git a/util/ThreadForEach.hh b/util/ThreadForEach.hh new file mode 100644 index 0000000..e1fc2fc --- /dev/null +++ b/util/ThreadForEach.hh @@ -0,0 +1,112 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_THREAD_FOR_EACH_H +#define STA_THREAD_FOR_EACH_H + +#include +#include +#include +#include "Iterator.hh" + +namespace sta { + +template +class ForEachArg { +public: + ForEachArg(Iterator *iter, + std::mutex &lock, + Func *func) : + iter_(iter), + lock_(lock), + func_(func) + {} + + ~ForEachArg() + { + delete func_; + } + + // Copy constructor. + ForEachArg(const ForEachArg &arg) : + iter_(arg.iter_), + lock_(arg.lock_), + func_(arg.func_->copy()) + { + } + // Move constructor. + ForEachArg(ForEachArg &&arg) : + iter_(arg.iter_), + lock_(arg.lock_), + func_(arg.func_) + { + arg.func_ = nullptr; + } + + Iterator *iter_; + std::mutex &lock_; + Func *func_; +}; + +template +void +forEachBegin(ForEachArg arg1) +{ + Iterator *iter = arg1.iter_; + std::mutex &lock = arg1.lock_; + Func *func = arg1.func_; + while (true) { + lock.lock(); + if (iter->hasNext()) { + FuncArg arg = iter->next(); + lock.unlock(); + (*func)(arg); + } + else { + lock.unlock(); + break; + } + } +} + +// Parallel version of STL for_each. +// Each thread has its own functor. +// Func::copy() must be defined. +template +void +forEach(Iterator *iter, + Func *func, + int thread_count) +{ + if (thread_count <= 1) { + while (iter->hasNext()) + (*func)(iter->next()); + } + else { + std::vector threads; + std::mutex lock; + for (int i = 0; i < thread_count; i++) { + ForEachArg arg(iter, lock, func->copy()); + threads.push_back(std::thread(forEachBegin, arg)); + } + + for (auto &thread : threads) + thread.join(); + } +} + +} // namespace +#endif diff --git a/util/ThreadPool.cc b/util/ThreadPool.cc new file mode 100644 index 0000000..c821eec --- /dev/null +++ b/util/ThreadPool.cc @@ -0,0 +1,51 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "ThreadWorker.hh" +#include "ThreadPool.hh" + +namespace sta { + +ThreadPool::~ThreadPool() +{ + threads_.deleteContentsClear(); +} + +ThreadWorker * +ThreadPool::pop() +{ + lock_.lock(); + if (!threads_.empty()) { + ThreadWorker *thread = threads_.back(); + threads_.pop_back(); + lock_.unlock(); + return thread; + } + lock_.unlock(); + // No threads in the pool, get a new one. + return new ThreadWorker; +} + +void +ThreadPool::push(ThreadWorker *thread) +{ + lock_.lock(); + threads_.push_back(thread); + lock_.unlock(); +} + +} // namespace diff --git a/util/ThreadPool.hh b/util/ThreadPool.hh new file mode 100644 index 0000000..68eb04d --- /dev/null +++ b/util/ThreadPool.hh @@ -0,0 +1,45 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_THREAD_POOL_H +#define STA_THREAD_POOL_H + +#include "DisallowCopyAssign.hh" +#include "Vector.hh" +#include "Mutex.hh" + +namespace sta { + +class ThreadWorker; + +class ThreadPool +{ +public: + ThreadPool() {} + ~ThreadPool(); + // Create a new working thread if the pool is empty. + ThreadWorker *pop(); + void push(ThreadWorker *thread); + +private: + DISALLOW_COPY_AND_ASSIGN(ThreadPool); + + Vector threads_; + Mutex lock_; +}; + +} // namespace +#endif diff --git a/util/ThreadWorker.cc b/util/ThreadWorker.cc new file mode 100644 index 0000000..107654c --- /dev/null +++ b/util/ThreadWorker.cc @@ -0,0 +1,134 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "Error.hh" +#include "Error.hh" +#include "ThreadException.hh" +#include "ThreadWorker.hh" + +namespace sta { + +bool ThreadWorker::default_attr_inited_ = false; +pthread_attr_t ThreadWorker::default_attr_; + +ThreadWorker::ThreadWorker() : + state_(state_ready), + func_(NULL), + arg_(NULL) +{ + int error = pthread_create(&thread_, defaultAttr(), threadBegin, this); + CheckThreadError(error); +} + +ThreadWorker::~ThreadWorker() +{ + // Stop the pthread function. + lock_.lock(); + state_ = state_stop; + condition_.signal(); + lock_.unlock(); + pthread_join(thread_, NULL); +} + +pthread_attr_t * +ThreadWorker::defaultAttr() +{ + if (!default_attr_inited_) { + int error; + + error = pthread_attr_init(&default_attr_); + CheckThreadError(error); + + // Make sure the thread contends for CPU time with threads + // in other processes. + error = pthread_attr_setscope(&default_attr_, STA_PTHREAD_SCOPE_SYSTEM); + CheckThreadError(error); + + // Set stack size to 1MB. +// error = pthread_attr_setstacksize(&default_attr_, (1 << 20)); + CheckThreadError(error); + + default_attr_inited_ = true; + } + return &default_attr_; +} + +void +ThreadWorker::beginTask(ThreadFunc func, + void *arg) +{ + lock_.lock(); + while (state_ != state_ready) + condition_.wait(lock_); + + func_ = func; + arg_ = arg; + state_ = state_run; + // Use broadcast instead of signal as start(), wait(), threadStart() + // may all be in different threads. + condition_.broadcast(); + lock_.unlock(); +} + +void +ThreadWorker::wait() +{ + lock_.lock(); + while (state_ != state_ready) + condition_.wait(lock_); + lock_.unlock(); +} + +// Evaluate tasks as they are assigned by and then wait for another +// task. +// This function is stopped by the ThreadWorker destructor. +void * +ThreadWorker::threadBegin(void *arg) +{ + ThreadWorker *worker = reinterpret_cast(arg); + while (true) { + worker->lock_.lock(); + while (worker->state_ != state_run && + worker->state_ != state_stop) + worker->condition_.wait(worker->lock_); + + if (worker->state_ == state_stop) { + // Stop the thread. + worker->lock_.unlock(); + break; + } + + try { + (*worker->func_)(worker->arg_); + } + // Keep the thread worker alive even after an exception. + catch (StaException &except) { + printf("Caught %s exception.", except.what()); + } + catch (...) { + printf("Caught ... exception."); + } + + worker->state_ = state_ready; + worker->condition_.broadcast(); + worker->lock_.unlock(); + } + return NULL; +} + +} // namespace diff --git a/util/ThreadWorker.hh b/util/ThreadWorker.hh new file mode 100644 index 0000000..3c7dac3 --- /dev/null +++ b/util/ThreadWorker.hh @@ -0,0 +1,64 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_THREAD_WORKER_H +#define STA_THREAD_WORKER_H + +#include "Pthread.hh" +#include "DisallowCopyAssign.hh" +#include "Mutex.hh" +#include "Condition.hh" + +namespace sta { + +typedef void (*ThreadFunc)(void*); + +class ThreadWorker +{ +public: + ThreadWorker(); + ~ThreadWorker(); + // beginTask() caller must call wait(), or deadlock will happen. + void beginTask(ThreadFunc func, + void *arg); + void wait(); + +private: + DISALLOW_COPY_AND_ASSIGN(ThreadWorker); + + static pthread_attr_t *defaultAttr(); + static void *threadBegin(void *arg); + + typedef enum { + state_ready = 0, + state_run, + state_done, + state_stop + } TaskState; + + pthread_t thread_; + Mutex lock_; + Condition condition_; + TaskState state_; + ThreadFunc func_; + void *arg_; + + static bool default_attr_inited_; + static pthread_attr_t default_attr_; +}; + +} // namespace +#endif diff --git a/util/TokenParser.cc b/util/TokenParser.cc new file mode 100644 index 0000000..68b1b98 --- /dev/null +++ b/util/TokenParser.cc @@ -0,0 +1,80 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include +#include "Machine.hh" +#include "TokenParser.hh" + +namespace sta { + +TokenParser::TokenParser(const char *str, const char *delimiters) : + delimiters_(delimiters), + token_(const_cast(str)), + 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/TokenParser.hh b/util/TokenParser.hh new file mode 100644 index 0000000..d22cd0a --- /dev/null +++ b/util/TokenParser.hh @@ -0,0 +1,50 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_TOKEN_PARSER_H +#define STA_TOKEN_PARSER_H + +#include "DisallowCopyAssign.hh" + +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: + DISALLOW_COPY_AND_ASSIGN(TokenParser); + + const char *delimiters_; + char *token_; + char *token_end_; + char token_delimiter_; + bool first_; +}; + +} // namespace +#endif diff --git a/util/UnorderedMap.hh b/util/UnorderedMap.hh new file mode 100644 index 0000000..cf7289b --- /dev/null +++ b/util/UnorderedMap.hh @@ -0,0 +1,179 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_UNORDERED_MAP_H +#define STA_UNORDERED_MAP_H + +#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(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 + 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 +#endif diff --git a/util/UnorderedSet.hh b/util/UnorderedSet.hh new file mode 100644 index 0000000..2033f3e --- /dev/null +++ b/util/UnorderedSet.hh @@ -0,0 +1,128 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_UNORDERED_SET_H +#define STA_UNORDERED_SET_H + +#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, + 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 +#endif diff --git a/util/Vector.hh b/util/Vector.hh new file mode 100644 index 0000000..8546a4a --- /dev/null +++ b/util/Vector.hh @@ -0,0 +1,140 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_VECTOR_H +#define STA_VECTOR_H + +#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 +#endif diff --git a/util/Zlib.hh b/util/Zlib.hh new file mode 100644 index 0000000..54c9382 --- /dev/null +++ b/util/Zlib.hh @@ -0,0 +1,42 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_ZLIB_H +#define STA_ZLIB_H + +// The zlib package is optional. +// Define stdio based macros if it is missing. + +#include "StaConfig.hh" // ZLIB + +#if ZLIB + +#include + +#else // ZLIB + +#include + +#define gzFile FILE* +#define gzopen fopen +#define gzclose fclose +#define gzgets(stream,s,size) fgets(s,size,stream) +#define gzprintf fprintf +#define Z_NULL nullptr + +#endif // ZLIB + +#endif diff --git a/verilog/Makefile.am b/verilog/Makefile.am new file mode 100644 index 0000000..0c02804 --- /dev/null +++ b/verilog/Makefile.am @@ -0,0 +1,52 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +lib_LTLIBRARIES = libverilog.la + +include_HEADERS = \ + Verilog.hh \ + VerilogReader.hh + +libverilog_la_SOURCES = \ + VerilogLex.ll \ + VerilogParse.yy \ + VerilogReader.cc + +VerilogLex.ll: VerilogParse.hh + +VerilogLex.cc: VerilogLex.ll + $(LEX) $(LFLAGS) -o VerilogLex.cc --prefix=VerilogLex_ --header-file=VerilogLex.hh VerilogLex.ll + +# Rules to support automake pre 1.12 that name header .h instead of .hh +VerilogParse.hh: VerilogParse.cc + if test -f VerilogParse.h; then \ + cp VerilogParse.h VerilogParse.hh; \ + fi + +EXTRA_DIST = \ + VerilogParse.hh \ + Verilog.i \ + Verilog.tcl + +MAINTAINERCLEANFILES = \ + VerilogParse.hh \ + VerilogParse.cc \ + VerilogLex.cc + +libs: $(lib_LTLIBRARIES) + +xtags: $(SOURCES) $(HEADERS) + etags -a -o ../TAGS $(SOURCES) $(HEADERS) diff --git a/verilog/Verilog.i b/verilog/Verilog.i new file mode 100644 index 0000000..d61f7a5 --- /dev/null +++ b/verilog/Verilog.i @@ -0,0 +1,63 @@ +%module verilog + +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "VerilogReader.hh" +#include "VerilogWriter.hh" +#include "Sta.hh" + +using sta::Sta; +using sta::NetworkReader; +using sta::readVerilogFile; + +%} + +%inline %{ + +bool +read_verilog(const char *filename) +{ + Sta *sta = Sta::sta(); + NetworkReader *network = sta->networkReader(); + if (network) { + sta->readNetlistBefore(); + return readVerilogFile(filename, network); + } + else + return false; +} + +void +delete_verilog_reader() +{ + deleteVerilogReader(); +} + +void +write_verilog_cmd(const char *filename, + bool sort) +{ + // This does NOT want the SDC (cmd) network because it wants + // to see the sta internal names. + Sta *sta = Sta::sta(); + Network *network = sta->network(); + writeVerilog(filename, sort, network); +} + +%} // inline diff --git a/verilog/Verilog.tcl b/verilog/Verilog.tcl new file mode 100644 index 0000000..61d717b --- /dev/null +++ b/verilog/Verilog.tcl @@ -0,0 +1,34 @@ +# OpenSTA, Static Timing Analyzer +# Copyright (c) 2019, Parallax Software, Inc. +# +# This 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 . + +namespace eval sta { + +# Defined by SWIG interface Verilog.i. +define_cmd_args "read_verilog" {filename} + +define_cmd_args "write_verilog" {[-sort] filename} + +proc write_verilog { args } { + parse_key_args "write_verilog" args keys {} flags {-sort} + + set sort [info exists flags(-sort)] + check_argc_eq1 "write_verilog" $args + set filename $args + write_verilog_cmd $filename $sort +} + +# sta namespace end +} diff --git a/verilog/VerilogLex.ll b/verilog/VerilogLex.ll new file mode 100644 index 0000000..9b27fbc --- /dev/null +++ b/verilog/VerilogLex.ll @@ -0,0 +1,186 @@ +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include "Machine.hh" +#include "Debug.hh" +#include "VerilogNamespace.hh" +#include "VerilogReaderPvt.hh" +#include "VerilogParse.hh" + +#define YY_NO_INPUT + +int verilog_line = 1; +static std::string string_buf; + +void +verilogFlushBuffer() +{ + YY_FLUSH_BUFFER; +} + +%} + +/* %option debug */ +%option noyywrap +%option nounput +%option never-interactive + +%x COMMENT +%x ATTRIBUTE +%x QSTRING + +SIGN "+"|"-" +UNSIGNED_NUMBER [0-9][0-9_]* +BLANK [ \t\r] +EOL \r?\n +ID_ESCAPED_TOKEN \\[^ \t\r\n]+[\r\n\t ] +ID_ALPHA_TOKEN [A-Za-z_][A-Za-z0-9_$]* +ID_TOKEN {ID_ESCAPED_TOKEN}|{ID_ALPHA_TOKEN} + +%% + +^[ \t]*`.*{EOL} { /* Macro definition. */ + sta::verilog_reader->incrLine(); + } + +"//"[^\n]*{EOL} { /* Single line comment. */ + sta::verilog_reader->incrLine(); + } + +"/*" { BEGIN COMMENT; } +{ +. + +{EOL} { sta::verilog_reader->incrLine(); } + +"*/" { BEGIN INITIAL; } + +<> { + VerilogParse_error("unterminated comment"); + BEGIN(INITIAL); + yyterminate(); + } +} + +"(*" { BEGIN ATTRIBUTE; } +{ +. + +{EOL} { sta::verilog_reader->incrLine(); } + +"*)" { BEGIN INITIAL; } + +<> { + VerilogParse_error("unterminated attribute"); + BEGIN(INITIAL); + yyterminate(); + } +} + +{SIGN}?{UNSIGNED_NUMBER}?"'"[bB][01_xz]+ { + VerilogParse_lval.constant = sta::stringCopy(VerilogLex_text); + return CONSTANT; +} + +{SIGN}?{UNSIGNED_NUMBER}?"'"[oO][0-7_xz]+ { + VerilogParse_lval.constant = sta::stringCopy(VerilogLex_text); + return CONSTANT; +} + +{SIGN}?{UNSIGNED_NUMBER}?"'"[dD][0-9_]+ { + VerilogParse_lval.constant = sta::stringCopy(VerilogLex_text); + return CONSTANT; +} + +{SIGN}?{UNSIGNED_NUMBER}?"'"[hH][0-9a-fA-F_xz]+ { + VerilogParse_lval.constant = sta::stringCopy(VerilogLex_text); + return CONSTANT; +} + +{SIGN}?[0-9]+ { + VerilogParse_lval.ival = atol(VerilogLex_text); + return INT; +} + +":"|"."|"{"|"}"|"["|"]"|","|"*"|";"|"="|"-"|"+"|"|"|"("|")" { + return ((int) VerilogLex_text[0]); +} + +assign { return ASSIGN; } +endmodule { return ENDMODULE; } +inout { return INOUT; } +input { return INPUT; } +module { return MODULE; } +output { return OUTPUT; } +parameter { return PARAMETER; } +defparam { return DEFPARAM; } +reg { return REG; } +supply0 { return SUPPLY0; } +supply1 { return SUPPLY1; } +tri { return TRI; } +wand { return WAND; } +wire { return WIRE; } +wor { return WOR; } + +{ID_TOKEN}("."{ID_TOKEN})* { + VerilogParse_lval.string = sta::stringCopy(sta::verilogToSta(VerilogLex_text)); + return ID; +} + +{EOL} { sta::verilog_reader->incrLine(); } + +{BLANK} { /* ignore blanks */ } + +\" { + string_buf.erase(); + BEGIN(QSTRING); + } + +\" { + BEGIN(INITIAL); + VerilogParse_lval.string = sta::stringCopy(string_buf.c_str()); + return STRING; + } + +{EOL} { + VerilogParse_error("unterminated string constant"); + BEGIN(INITIAL); + VerilogParse_lval.string = sta::stringCopy(string_buf.c_str()); + return STRING; + } + +\\{EOL} { + /* Line continuation. */ + sta::verilog_reader->incrLine(); + } + +[^\r\n\"]+ { + /* Anything return or double quote */ + string_buf += VerilogLex_text; + } + +<> { + VerilogParse_error("unterminated string constant"); + BEGIN(INITIAL); + yyterminate(); + } + + /* Send out of bound characters to parser. */ +. { return (int) VerilogLex_text[0]; } + +%% diff --git a/verilog/VerilogParse.yy b/verilog/VerilogParse.yy new file mode 100644 index 0000000..f42f5a6 --- /dev/null +++ b/verilog/VerilogParse.yy @@ -0,0 +1,465 @@ +%{ + +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "PortDirection.hh" +#include "VerilogReaderPvt.hh" +#include "VerilogReader.hh" + +int VerilogLex_lex(); +#define VerilogParse_lex VerilogLex_lex +// Use yacc generated parser errors. +#define YYERROR_VERBOSE + +%} + +%union{ + int ival; + const char *string; + const char *constant; + 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; +} + +%token INT CONSTANT ID STRING MODULE ENDMODULE ASSIGN PARAMETER DEFPARAM +%token WIRE WAND WOR TRI INPUT OUTPUT INOUT SUPPLY1 SUPPLY0 REG + +%left '-' '+' +%left '*' '/' +%left NEG /* negation--unary minus */ + +%type ID STRING +%type WIRE WAND WOR TRI INPUT OUTPUT INOUT SUPPLY1 SUPPLY0 +%type INT parameter_exprs parameter_expr module_begin +%type CONSTANT +%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 net_assignment +%type dcl_arg +%type dcl_args +%type port net_scalar net_bit_select net_part_select +%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 + +%start file + +%{ +%} + +%% + +file: + modules + ; + +modules: + // empty +| modules module + ; + +module_begin: + MODULE { $$ = sta::verilog_reader->line(); } + { $$ = $2; } + ; + +module: + module_begin ID ';' stmts ENDMODULE + { sta::verilog_reader->makeModule($2, new sta::VerilogNetSeq,$4,$1);} +| module_begin ID '(' ')' ';' stmts ENDMODULE + { sta::verilog_reader->makeModule($2, new sta::VerilogNetSeq,$6,$1);} +| module_begin ID '(' port_list ')' ';' stmts ENDMODULE + { sta::verilog_reader->makeModule($2, $4, $7, $1); } +| module_begin ID '(' port_dcls ')' ';' stmts ENDMODULE + { sta::verilog_reader->makeModule($2, $4, $7, $1); } + ; + +port_list: + port + { $$ = new sta::VerilogNetSeq; + $$->push_back($1); + } +| port_list ',' port + { $1->push_back($3); } + ; + +port: + port_expr +| '.' ID '(' ')' + { $$=sta::verilog_reader->makeNetNamedPortRefScalar($2, NULL);} +| '.' ID '(' port_expr ')' + { $$=sta::verilog_reader->makeNetNamedPortRefScalar($2, $4);} + ; + +port_expr: + port_ref +| '{' port_refs '}' + { $$ = sta::verilog_reader->makeNetConcat($2); } ; + +port_refs: + port_ref + { $$ = new sta::VerilogNetSeq; + $$->push_back($1); + } +| port_refs ',' port_ref + { $1->push_back($3); } + ; + +port_ref: + net_scalar +| net_bit_select +| net_part_select + ; + +port_dcls: + port_dcl + { $$ = new sta::VerilogStmtSeq; + $$->push_back($1); + } +| port_dcls ',' port_dcl + { $$ = $1; + $1->push_back($3); + } +| port_dcls ',' dcl_arg + { + sta::VerilogDcl *dcl = dynamic_cast($1->back()); + dcl->appendArg($3); + $$ = $1; + } + ; + +port_dcl: + port_dcl_type { $$ = sta::verilog_reader->line(); } dcl_arg + { $$ = sta::verilog_reader->makeDcl($1, $3, $2); } +| port_dcl_type { $$ = sta::verilog_reader->line(); } + '[' INT ':' INT ']' dcl_arg + { $$ = sta::verilog_reader->makeDclBus($1, $4, $6, $8, $2); } + ; + +port_dcl_type: + INPUT { $$ = sta::PortDirection::input(); } +| INPUT WIRE { $$ = sta::PortDirection::input(); } +| INOUT { $$ = sta::PortDirection::bidirect(); } +| INOUT REG { $$ = sta::PortDirection::bidirect(); } +| INOUT WIRE { $$ = sta::PortDirection::bidirect(); } +| OUTPUT { $$ = sta::PortDirection::output(); } +| OUTPUT WIRE { $$ = sta::PortDirection::output(); } +| OUTPUT REG { $$ = sta::PortDirection::output(); } + ; + +stmts: + // empty + { $$ = new sta::VerilogStmtSeq; } +| stmts stmt + { if ($2) $1->push_back($2); } +| stmts stmt_seq + // Append stmt_seq to stmts. + { sta::VerilogStmtSeq::Iterator iter($2); + while (iter.hasNext()) + $1->push_back(iter.next()); + delete $2; + } + ; + +stmt: + parameter +| defparam +| declaration +| instance +| error ';' + { yyerrok; $$ = NULL; } + ; + +stmt_seq: + continuous_assign + ; + +/* Parameters are parsed and ignored. */ +parameter: + PARAMETER parameter_dcls ';' + { $$ = NULL; } +| PARAMETER '[' INT ':' INT ']' parameter_dcls ';' + { $$ = NULL; } + ; + +parameter_dcls: + parameter_dcl + { $$ = NULL; } +| parameter_dcls ',' parameter_dcl + { $$ = NULL; } + ; + +parameter_dcl: + ID '=' parameter_expr + { sta::stringDelete($1); + $$ = NULL; + } +| ID '=' STRING + { sta::stringDelete($1); + sta::stringDelete($3); + $$ = NULL; + } +; + +parameter_expr: + ID + { sta::stringDelete($1); + $$ = 0; + } +| '`' ID + { sta::stringDelete($2); + $$ = 0; + } +| CONSTANT + { sta::stringDelete($1); + $$ = 0; + } +| INT +| '-' parameter_expr %prec NEG + { $$ = - $2; } +| parameter_expr '+' parameter_expr + { $$ = $1 + $3; } +| parameter_expr '-' parameter_expr + { $$ = $1 - $3; } +| parameter_expr '*' parameter_expr + { $$ = $1 * $3; } +| parameter_expr '/' parameter_expr + { $$ = $1 / $3; } +| '(' parameter_expr ')' + { $$ = $2; } + ; + +defparam: + DEFPARAM param_values ';' + { $$ = NULL; } + ; + +param_values: + param_value + { $$ = NULL; } +| param_values ',' param_value + { $$ = NULL; } + ; + +param_value: + ID '=' parameter_expr + { sta::stringDelete($1); + $$ = NULL; + } +| ID '=' STRING + { sta::stringDelete($1); + sta::stringDelete($3); + $$ = NULL; + } + ; + +declaration: + dcl_type { $$ = sta::verilog_reader->line(); } dcl_args ';' + { $$ = sta::verilog_reader->makeDcl($1, $3, $2); } +| dcl_type { $$ = sta::verilog_reader->line(); } + '[' INT ':' INT ']' dcl_args ';' + { $$ = sta::verilog_reader->makeDclBus($1, $4, $6, $8, $2); } + ; + +dcl_type: + INPUT { $$ = sta::PortDirection::input(); } +| INOUT { $$ = sta::PortDirection::bidirect(); } +| OUTPUT { $$ = sta::PortDirection::output(); } +| SUPPLY0 { $$ = sta::PortDirection::ground(); } +| SUPPLY1 { $$ = sta::PortDirection::power(); } +| TRI { $$ = sta::PortDirection::tristate(); } +| WAND { $$ = sta::PortDirection::internal(); } +| WIRE { $$ = sta::PortDirection::internal(); } +| WOR { $$ = sta::PortDirection::internal(); } + ; + +dcl_args: + dcl_arg + { $$ = new sta::VerilogDclArgSeq; + $$->push_back($1); + } +| dcl_args ',' dcl_arg + { $1->push_back($3); + $$ = $1; + } + ; + +dcl_arg: + ID + { $$ = sta::verilog_reader->makeDclArg($1); } +| net_assignment + { $$ = sta::verilog_reader->makeDclArg($1); } + ; + +continuous_assign: + ASSIGN net_assignments ';' + { $$ = $2; } + ; + +net_assignments: + net_assignment + { $$ = new sta::VerilogStmtSeq(); + $$->push_back($1); + } +| net_assignments ',' net_assignment + { $1->push_back($3); } + ; + +net_assignment: + net_named { $$ = sta::verilog_reader->line(); } '=' net_expr + { $$ = sta::verilog_reader->makeAssign($1, $4, $2); } + ; + +instance: + ID { $$ = sta::verilog_reader->line(); } ID '(' inst_pins ')' ';' + { $$ = sta::verilog_reader->makeModuleInst($1, $3, $5, $2); } +| ID { $$ = sta::verilog_reader->line(); } parameter_values + ID '(' inst_pins ')' ';' + { $$ = sta::verilog_reader->makeModuleInst($1, $4, $6, $2); } + ; + +parameter_values: + '#' '(' parameter_exprs ')' + ; + +parameter_exprs: + parameter_expr +| '{' parameter_exprs '}' + { $$ = $2; } +| parameter_exprs ',' parameter_expr + ; + +inst_pins: + // empty + { $$ = NULL; } +| inst_ordered_pins +| inst_named_pins + ; + +// Positional pin connections. +inst_ordered_pins: + net_expr + { $$ = new sta::VerilogNetSeq; + $$->push_back($1); + } +| inst_ordered_pins ',' net_expr + { $1->push_back($3); } + ; + +// Named pin connections. +inst_named_pins: + inst_named_pin + { $$ = new sta::VerilogNetSeq; + $$->push_back($1); + } +| inst_named_pins ',' inst_named_pin + { $1->push_back($3); } + ; + +// The port reference is split out into cases to special case +// the most frequent case of .port_scalar(net_scalar). +inst_named_pin: +// Scalar port. + '.' ID '(' ')' + { $$ = sta::verilog_reader->makeNetNamedPortRefScalarNet($2, NULL); } +| '.' ID '(' ID ')' + { $$ = sta::verilog_reader->makeNetNamedPortRefScalarNet($2, $4); } +| '.' ID '(' ID '[' INT ']' ')' + { $$ = sta::verilog_reader->makeNetNamedPortRefBitSelect($2, $4, $6); } +| '.' ID '(' named_pin_net_expr ')' + { $$ = sta::verilog_reader->makeNetNamedPortRefScalar($2, $4); } +// Bus port bit select. +| '.' ID '[' INT ']' '(' ')' + { $$ = sta::verilog_reader->makeNetNamedPortRefBit($2, $4, NULL); } +| '.' ID '[' INT ']' '(' net_expr ')' + { $$ = sta::verilog_reader->makeNetNamedPortRefBit($2, $4, $7); } +// Bus port part select. +| '.' ID '[' INT ':' INT ']' '(' ')' + { $$ = sta::verilog_reader->makeNetNamedPortRefPart($2, $4, $6, NULL); } +| '.' ID '[' INT ':' INT ']' '(' net_expr ')' + { $$ = sta::verilog_reader->makeNetNamedPortRefPart($2, $4, $6, $9); } + ; + +named_pin_net_expr: + net_part_select +| net_constant +| net_expr_concat + ; + +net_named: + net_scalar +| net_bit_select +| net_part_select + ; + +net_scalar: + ID + { $$ = sta::verilog_reader->makeNetScalar($1); } + ; + +net_bit_select: + ID '[' INT ']' + { $$ = sta::verilog_reader->makeNetBitSelect($1, $3); } + ; + +net_part_select: + ID '[' INT ':' INT ']' + { $$ = sta::verilog_reader->makeNetPartSelect($1, $3, $5); } + ; + +net_constant: + CONSTANT + { $$ = sta::verilog_reader->makeNetConstant($1); } + ; + +net_expr_concat: + '{' net_exprs '}' + { $$ = sta::verilog_reader->makeNetConcat($2); } + ; + +net_exprs: + net_expr + { $$ = new sta::VerilogNetSeq; + $$->push_back($1); + } +| net_exprs ',' net_expr + { $$->push_back($3); } + ; + +net_expr: + net_scalar +| net_bit_select +| net_part_select +| net_constant +| net_expr_concat + ; + +%% diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc new file mode 100644 index 0000000..b1dc4c2 --- /dev/null +++ b/verilog/VerilogReader.cc @@ -0,0 +1,2257 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "DisallowCopyAssign.hh" +#include "Debug.hh" +#include "Report.hh" +#include "Error.hh" +#include "Stats.hh" +#include "PortDirection.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "VerilogNamespace.hh" +#include "VerilogReaderPvt.hh" +#include "VerilogReader.hh" + +extern int +VerilogParse_parse(); + +namespace sta { + +VerilogReader *verilog_reader; +static const char *unconnected_net_name = reinterpret_cast(1); + +static const char * +verilogBusBitName(const char *bus_name, + int index); +static const char * +verilogBusBitNameTmp(const char *bus_name, + int index); +static int +hierarchyLevel(Net *net, + Network *network); +// Return top level instance. +Instance * +linkVerilogNetwork(const char *top_cell_name, + bool make_black_boxes, + Report *report, + NetworkReader *network); + +bool +readVerilogFile(const char *filename, + NetworkReader *network) +{ + if (verilog_reader == nullptr) + verilog_reader = new VerilogReader(network); + return verilog_reader->read(filename); +} + +void +deleteVerilogReader() +{ + delete verilog_reader; + verilog_reader = nullptr; +} + +//////////////////////////////////////////////////////////////// + +class VerilogError +{ +public: + VerilogError(const char *filename, + int line, + const char *msg, + bool warn); + ~VerilogError(); + void report(Report *report); + bool warn() const { return warn_; } + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogError); + + const char *filename_; + int line_; + const char *msg_; + bool warn_; + + friend class VerilogErrorCmp; +}; + +VerilogError::VerilogError(const char *filename, + int line, + const char *msg, + bool warn) : + filename_(filename), + line_(line), + msg_(msg), + warn_(warn) +{ +} + +VerilogError::~VerilogError() +{ + // filename is owned by VerilogReader. + stringDelete(msg_); +} + +void +VerilogError::report(Report *report) +{ + if (warn_) + report->fileWarn(filename_, line_, "%s", msg_); + else + report->fileError(filename_, line_, "%s", msg_); +} + +class VerilogErrorCmp +{ +public: + bool operator()(const VerilogError *error1, + const VerilogError *error2) const + { + int file_cmp = strcmp(error1->filename_, error2->filename_); + if (file_cmp == 0) { + if (error1->line_ == error2->line_) + return strcmp(error1->msg_, error2->msg_) < 0; + else + return error1->line_ < error2->line_; + } + else + return file_cmp < 0; + } +}; + +//////////////////////////////////////////////////////////////// + +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(linkVerilogNetwork); + VerilogConstant10 constant10_max = 0; + constant10_max_ = stringPrint("%llu", ~constant10_max); + constant10_max_length_ = strlen(constant10_max_); +} + +VerilogReader::~VerilogReader() +{ + deleteModules(); + stringDelete(constant10_max_); +} + +void +VerilogReader::deleteModules() +{ + StringSet filenames; + VerilogModuleMap::Iterator module_iter(module_map_); + while (module_iter.hasNext()) { + VerilogModule *module = module_iter.next(); + filenames.insert(module->filename()); + delete module; + } + deleteContents(&filenames); + module_map_.clear(); +} + +bool +VerilogReader::read(const char *filename) +{ + // Use zlib to uncompress gzip'd files automagically. + stream_ = gzopen(filename, "rb"); + if (stream_) { + Stats stats(debug_); + init(filename); + bool success = (::VerilogParse_parse() == 0); + gzclose(stream_); + reportStmtCounts(); + stats.report("Read verilog"); + return success; + } + else + throw FileNotReadable(filename); +} + +void +VerilogReader::init(const char *filename) +{ + // Statements point to verilog_filename, so copy it. + filename_ = stringCopy(filename); + line_ = 1; + + library_ = network_->findLibrary("verilog"); + if (library_ == nullptr) + library_ = network_->makeLibrary("verilog", nullptr); + + report_stmt_stats_ = debugCheck(debug_, "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; +} + +void +VerilogReader::getChars(char *buf, + size_t &result, + size_t max_size) +{ + char *status = gzgets(stream_, buf, max_size); + if (status == Z_NULL) + result = 0; // YY_nullptr + else + result = strlen(buf); +} + +void +VerilogReader::getChars(char *buf, + int &result, + size_t max_size) +{ + char *status = gzgets(stream_, buf, max_size); + if (status == Z_NULL) + result = 0; // YY_nullptr + else + result = strlen(buf); +} + +VerilogModule * +VerilogReader::module(Cell *cell) +{ + return module_map_.findKey(cell); +} + +void +VerilogReader::makeModule(const char *name, + VerilogNetSeq *ports, + VerilogStmtSeq *stmts, + int line) +{ + Cell *cell = network_->findCell(library_, name); + if (cell) { + VerilogModule *module = module_map_[cell]; + delete module; + module_map_.erase(cell); + network_->deleteCell(cell); + } + VerilogModule *module = new VerilogModule(name, ports, stmts, + filename_, line, this); + cell = network_->makeCell(library_, name, false, filename_); + module_map_[cell] = module; + makeCellPorts(cell, module, ports); + module_count_++; +} + +void +VerilogReader::makeModule(const char *name, + VerilogStmtSeq *port_dcls, + VerilogStmtSeq *stmts, + int line) +{ + VerilogNetSeq *ports = new VerilogNetSeq; + // Pull the port names out of the port declarations. + VerilogStmtSeq::Iterator dcl_iter1(port_dcls); + while (dcl_iter1.hasNext()) { + VerilogStmt *dcl = dcl_iter1.next(); + if (dcl->isDeclaration()) { + VerilogDcl *dcl1 = dynamic_cast(dcl); + VerilogDclArgSeq::Iterator arg_iter(dcl1->args()); + while (arg_iter.hasNext()) { + VerilogDclArg *arg = arg_iter.next(); + const char *port_name = stringCopy(arg->netName()); + VerilogNetNamed *port = new VerilogNetScalar(port_name); + ports->push_back(port); + } + // Add the port declarations to the statements. + stmts->push_back(dcl); + } + } + delete port_dcls; + makeModule(name, ports, stmts, line); +} + +void +VerilogReader::makeCellPorts(Cell *cell, + VerilogModule *module, + VerilogNetSeq *ports) +{ + StringSet port_names; + VerilogNetSeq::Iterator port_iter(ports); + while (port_iter.hasNext()) { + VerilogNet *mod_port = port_iter.next(); + const char *port_name = mod_port->name(); + if (port_names.findKey(port_name) == nullptr) { + port_names.insert(stringCopy(port_name)); + if (mod_port->isNamed()) { + if (mod_port->isNamedPortRef()) + makeNamedPortRefCellPorts(cell, module, mod_port, port_names); + else + makeCellPort(cell, module, mod_port->name()); + } + } + else + warn(module->filename(), module->line(), + "module %s repeated port name %s.\n", + module->name(), + port_name); + } + checkModuleDcls(module, port_names); + deleteContents(&port_names); +} + +Port * +VerilogReader::makeCellPort(Cell *cell, + VerilogModule *module, + const char *port_name) +{ + VerilogDcl *dcl = module->declaration(port_name); + if (dcl) { + PortDirection *dir = dcl->direction(); + VerilogDclBus *dcl_bus = dynamic_cast(dcl); + Port *port = dcl->isBus() + ? network_->makeBusPort(cell, port_name, dcl_bus->fromIndex(), + dcl_bus->toIndex()) + : network_->makePort(cell, port_name); + network_->setDirection(port, dir); + return port; + } + else { + warn(module->filename(), module->line(), + "module %s missing declaration for port %s.\n", + module->name(), + port_name); + return network_->makePort(cell, port_name); + } +} + +void +VerilogReader::makeNamedPortRefCellPorts(Cell *cell, + VerilogModule *module, + VerilogNet *mod_port, + StringSet &port_names) +{ + PortSeq *member_ports = new PortSeq; + VerilogNetNameIterator *net_name_iter = mod_port->nameIterator(module,this); + while (net_name_iter->hasNext()) { + const char *net_name = net_name_iter->next(); + port_names.insert(stringCopy(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(), member_ports); +} + +// Make sure each declaration appears in the module port list. +void +VerilogReader::checkModuleDcls(VerilogModule *module, + StringSet &port_names) +{ + VerilogDclMap::ConstIterator dcl_iter(module->declarationMap()); + while (dcl_iter.hasNext()) { + VerilogDcl *dcl; + const char *port_name; + dcl_iter.next(port_name, dcl); + PortDirection *dir = dcl->direction(); + if (dir->isInput() + || dir->isOutput() + || dir->isBidirect()) { + if (port_names.findKey(port_name) == nullptr) + linkWarn(module->filename(), module->line(), + "module %s declared signal %s is not in the port list.\n", + module->name(), + port_name); + } + } +} + +VerilogDcl * +VerilogReader::makeDcl(PortDirection *dir, + VerilogDclArgSeq *args, + int line) +{ + if (dir->isInternal()) { + // Prune wire declarations without assigns because they just eat memory. + VerilogDclArgSeq *assign_args = nullptr; + VerilogDclArgSeq::Iterator arg_iter(args); + while (arg_iter.hasNext()) { + VerilogDclArg *arg = arg_iter.next(); + if (arg->assign()) { + if (assign_args == nullptr) + assign_args = new VerilogDclArgSeq; + assign_args->push_back(arg); + } + else { + delete arg; + dcl_arg_count_--; + } + } + delete args; + if (assign_args) { + dcl_count_++; + return new VerilogDcl(dir, assign_args, line); + } + else + return nullptr; + } + else { + dcl_count_++; + return new VerilogDcl(dir, args, line); + } +} + +VerilogDcl * +VerilogReader::makeDcl(PortDirection *dir, + VerilogDclArg *arg, + int line) +{ + dcl_count_++; + return new VerilogDcl(dir, arg, line); +} + +VerilogDclBus * +VerilogReader::makeDclBus(PortDirection *dir, + int from_index, + int to_index, + VerilogDclArg *arg, + int line) +{ + dcl_bus_count_++; + return new VerilogDclBus(dir, from_index, to_index, arg, line); +} + +VerilogDclBus * +VerilogReader::makeDclBus(PortDirection *dir, + int from_index, + int to_index, + VerilogDclArgSeq *args, + int line) +{ + dcl_bus_count_++; + return new VerilogDclBus(dir, from_index, to_index, args, line); +} + +VerilogDclArg * +VerilogReader::makeDclArg(const char *net_name) +{ + dcl_arg_count_++; + return new VerilogDclArg(net_name); +} + +VerilogDclArg * +VerilogReader::makeDclArg(VerilogAssign *assign) +{ + dcl_arg_count_++; + return new VerilogDclArg(assign); +} + +VerilogNetPartSelect * +VerilogReader::makeNetPartSelect(const char *name, + int from_index, + int to_index) +{ + net_part_select_count_++; + if (report_stmt_stats_) + net_bus_names_ += strlen(name) + 1; + return new VerilogNetPartSelect(name, from_index, to_index); +} + +VerilogNetConstant * +VerilogReader::makeNetConstant(const char *constant) +{ + net_constant_count_++; + return new VerilogNetConstant(constant, this); +} + +VerilogNetScalar * +VerilogReader::makeNetScalar(const char *name) +{ + net_scalar_count_++; + if (report_stmt_stats_) + net_scalar_names_ += strlen(name) + 1; + return new VerilogNetScalar(name); +} + +VerilogNetBitSelect * +VerilogReader::makeNetBitSelect(const char *name, + int index) +{ + net_bit_select_count_++; + if (report_stmt_stats_) + net_bus_names_ += strlen(name) + 1; + return new VerilogNetBitSelect(name, index); +} + +VerilogAssign * +VerilogReader::makeAssign(VerilogNet *lhs, + VerilogNet *rhs, + int line) +{ + assign_count_++; + return new VerilogAssign(lhs, rhs, line); +} + +VerilogInst * +VerilogReader::makeModuleInst(const char *module_name, + const char *inst_name, + VerilogNetSeq *pins, + const int line) +{ + 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)) { + int port_count = network_->portBitCount(cell); + const char **net_names = new const char *[port_count]; + for (int i = 0; i < port_count; i++) + net_names[i] = nullptr; + VerilogNetSeq::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + VerilogNetPortRefScalarNet *vpin = + dynamic_cast(pin_iter.next()); + const char *port_name = vpin->name(); + const char *net_name = vpin->netName(); + // Steal the net name string. + vpin->setNetName(nullptr); + Port *port = network_->findPort(cell, port_name); + LibertyPort *lport = network_->libertyPort(port); + if (lport->isBus()) { + LibertyPortMemberIterator member_iter(lport); + lport = member_iter.next(); + } + int pin_index = lport->pinIndex(); + const char *prev_net_name = net_names[pin_index]; + if (prev_net_name + && prev_net_name !=unconnected_net_name) + // Repeated port reference. + stringDelete(prev_net_name); + net_names[pin_index]=(net_name == nullptr) ? unconnected_net_name : net_name; + delete vpin; + net_port_ref_scalar_net_count_--; + } + VerilogInst *inst = new VerilogLibertyInst(liberty_cell, inst_name, + net_names, line); + stringDelete(module_name); + delete pins; + if (report_stmt_stats_) { + inst_names_ += strlen(inst_name) + 1; + inst_lib_count_++; + inst_lib_net_arrays_ += port_count; + } + return inst; + } + else { + VerilogInst *inst = new VerilogModuleInst(module_name, inst_name, pins, line); + if (report_stmt_stats_) { + inst_module_names_ += strlen(module_name) + 1; + inst_names_ += strlen(inst_name) + 1; + inst_mod_count_++; + } + return inst; + } +} + +bool +VerilogReader::hasScalarNamedPortRefs(LibertyCell *liberty_cell, + VerilogNetSeq *pins) +{ + if (pins + && pins->size() > 0 + && (*pins)[0]->isNamedPortRef()) { + VerilogNetSeq::Iterator pin_iter(pins); + while (pin_iter.hasNext()) { + VerilogNet *vpin = pin_iter.next(); + const char *port_name = vpin->name(); + LibertyPort *port = liberty_cell->findLibertyPort(port_name); + if (port) { + if (!(port->size() == 1 + && (vpin->isNamedPortRefScalarNet()))) + return false; + } + else + return false; + } + return true; + } + else + return false; +} + +VerilogNetPortRef * +VerilogReader::makeNetNamedPortRefScalarNet(const char *port_name, + const char *net_name) +{ + net_port_ref_scalar_net_count_++; + if (report_stmt_stats_) { + if (net_name) + net_scalar_names_ += strlen(net_name) + 1; + port_names_ += strlen(port_name) + 1; + } + return new VerilogNetPortRefScalarNet(port_name, net_name); +} + +VerilogNetPortRef * +VerilogReader::makeNetNamedPortRefBitSelect(const char *port_name, + const char *bus_name, + int index) +{ + net_port_ref_scalar_net_count_++; + const char *net_name = verilogBusBitName(bus_name, index); + if (report_stmt_stats_) { + net_scalar_names_ += strlen(net_name) + 1; + port_names_ += strlen(port_name) + 1; + } + stringDelete(bus_name); + return new VerilogNetPortRefScalarNet(port_name, net_name); +} + +VerilogNetPortRef * +VerilogReader::makeNetNamedPortRefScalar(const char *port_name, + VerilogNet *net) +{ + net_port_ref_scalar_count_++; + if (report_stmt_stats_) + port_names_ += strlen(port_name) + 1; + return new VerilogNetPortRefScalar(port_name, net); +} + +VerilogNetPortRef * +VerilogReader::makeNetNamedPortRefBit(const char *port_name, + int index, + VerilogNet *net) +{ + net_port_ref_bit_count_++; + return new VerilogNetPortRefBit(port_name, index, net); +} + +VerilogNetPortRef * +VerilogReader::makeNetNamedPortRefPart(const char *port_name, + int from_index, + int to_index, + VerilogNet *net) +{ + net_port_ref_part_count_++; + return new VerilogNetPortRefPart(port_name, from_index, to_index, net); +} + +VerilogNetConcat * +VerilogReader::makeNetConcat(VerilogNetSeq *nets) +{ + concat_count_++; + return new VerilogNetConcat(nets); +} + +void +VerilogReader::incrLine() +{ + line_++; +} + +#define printClassMemory(name, class_name, count) \ + debug_->print(" %-20s %9d * %3d = %6.1fMb\n", \ + name, \ + count, \ + static_cast(sizeof(class_name)), \ + (count * sizeof(class_name) * 1e-6)) + +#define printStringMemory(name, count) \ + debug_->print(" %-20s %6.1fMb\n", name, count * 1e-6) + +void +VerilogReader::reportStmtCounts() +{ + if (debugCheck(debug_, "verilog", 1)) { + debug_->print("Verilog stats\n"); + 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(const char *filename, + int line, + const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + report()->vfileError(filename, line, fmt, args); + va_end(args); +} + +void +VerilogReader::warn(const char *filename, + int line, + const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + report()->vfileWarn(filename, line, fmt, args); + va_end(args); +} + +const char * +VerilogReader::verilogName(VerilogModuleInst *mod_inst) +{ + return sta::instanceVerilogName(mod_inst->instanceName(), + network_->pathEscape()); +} + +const char * +VerilogReader::instanceVerilogName(const char *inst_name) +{ + return sta::netVerilogName(inst_name, network_->pathEscape()); +} + +const char * +VerilogReader::netVerilogName(const char *net_name) +{ + return sta::netVerilogName(net_name, network_->pathEscape()); +} + +//////////////////////////////////////////////////////////////// + +VerilogModule::VerilogModule(const char *name, + VerilogNetSeq *ports, + VerilogStmtSeq *stmts, + const char *filename, + int line, + VerilogReader *reader) : + VerilogStmt(line), + name_(name), + filename_(filename), + ports_(ports), + stmts_(stmts) +{ + parseStmts(reader); +} + +VerilogModule::~VerilogModule() +{ + ports_->deleteContents(); + delete ports_; + stmts_->deleteContents(); + delete stmts_; + stringDelete(name_); +} + +void +VerilogModule::parseStmts(VerilogReader *reader) +{ + StringSet inst_names; + VerilogStmtSeq::Iterator stmt_iter(stmts_); + while (stmt_iter.hasNext()) { + VerilogStmt *stmt = stmt_iter.next(); + if (stmt->isDeclaration()) + parseDcl(dynamic_cast(stmt), reader); + else if (stmt->isInstance()) + checkInstanceName(dynamic_cast(stmt), inst_names, + reader); + } +} + +void +VerilogModule::parseDcl(VerilogDcl *dcl, + VerilogReader *reader) +{ + VerilogDclArgSeq::Iterator arg_iter(dcl->args()); + while (arg_iter.hasNext()) { + VerilogDclArg *arg = arg_iter.next(); + const char *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] = dcl; + else if (dcl->direction()->isTristate() + && (existing_dir->isOutput() + || existing_dir->isInput() + || existing_dir->isBidirect())) + // tri dcl can be used as modifier for input/output/inout dcls. + // Keep the tristate dcl because it is more specific. + dcl_map_[net_name] = dcl; + else if (dcl->direction()->isPowerGround() + && (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] = dcl; + else if (!dcl->direction()->isInternal()) + reader->warn(filename_, dcl->line(), + "signal %s previously declared on line %d.\n", + reader->netVerilogName(net_name), + existing_dcl->line()); + } + else + dcl_map_[net_name] = dcl; + } +} + +// Check for duplicate instance names during parse rather than during +// expansion so errors are only reported once. +void +VerilogModule::checkInstanceName(VerilogInst *inst, + StringSet &inst_names, + VerilogReader *reader) +{ + const char *inst_name = inst->instanceName(); + if (inst_names.findKey(inst_name)) { + int i = 1; + const char *replacement_name = nullptr; + do { + if (replacement_name) + stringDelete(replacement_name); + replacement_name = stringPrint("%s_%d", inst_name, i); + } while (inst_names.findKey(replacement_name)); + reader->warn(filename_, inst->line(), + "instance name %s duplicated - renamed to %s.\n", + reader->instanceVerilogName(inst_name), + replacement_name); + inst_name = replacement_name; + inst->setInstanceName(inst_name); + } + inst_names.insert(inst_name); +} + +VerilogDcl * +VerilogModule::declaration(const char *net_name) +{ + return dcl_map_.findKey(net_name); +} + +//////////////////////////////////////////////////////////////// + +VerilogStmt::VerilogStmt(int line) : + line_(line) +{ +} + +VerilogInst::VerilogInst(const char *inst_name, + const int line) : + VerilogStmt(line), + inst_name_(inst_name) +{ +} + +VerilogInst::~VerilogInst() +{ + stringDelete(inst_name_); +} + +void +VerilogInst::setInstanceName(const char *inst_name) +{ + stringDelete(inst_name_); + inst_name_ = inst_name; +} + +VerilogModuleInst::VerilogModuleInst(const char *module_name, + const char *inst_name, + VerilogNetSeq *pins, + int line) : + VerilogInst(inst_name, line), + module_name_(module_name), + pins_(pins) +{ +} + +VerilogModuleInst::~VerilogModuleInst() +{ + if (pins_) { + pins_->deleteContents(); + delete pins_; + } + stringDelete(module_name_); +} + +bool +VerilogModuleInst::hasPins() +{ + return pins_ + && pins_->size() > 0; + +} + +bool +VerilogModuleInst::namedPins() +{ + return pins_ + && pins_->size() > 0 + && (*pins_)[0]->isNamedPortRef(); +} + +VerilogLibertyInst::VerilogLibertyInst(LibertyCell *cell, + const char *inst_name, + const char **net_names, + const int line) : + VerilogInst(inst_name, line), + cell_(cell), + net_names_(net_names) +{ +} + +VerilogLibertyInst::~VerilogLibertyInst() +{ + int port_count = cell_->portBitCount(); + for (int i = 0; i < port_count; i++) { + const char *net_name = net_names_[i]; + if (net_name + && net_name != unconnected_net_name) + stringDelete(net_name); + } + delete [] net_names_; +} + +VerilogDcl::VerilogDcl(PortDirection *dir, + VerilogDclArgSeq *args, + int line) : + VerilogStmt(line), + dir_(dir), + args_(args) +{ +} + +VerilogDcl::VerilogDcl(PortDirection *dir, + VerilogDclArg *arg, + int line) : + VerilogStmt(line), + dir_(dir) +{ + args_ = new VerilogDclArgSeq; + args_->push_back(arg); +} + +VerilogDcl::~VerilogDcl() +{ + args_->deleteContents(); + delete args_; +} + +void +VerilogDcl::appendArg(VerilogDclArg *arg) +{ + args_->push_back(arg); +} + +const char * +VerilogDcl::portName() +{ + return (*args_)[0]->netName(); +} + +VerilogDclBus::VerilogDclBus(PortDirection *dir, + int from_index, + int to_index, + VerilogDclArgSeq *args, + int line) : + VerilogDcl(dir, args, line), + from_index_(from_index), + to_index_(to_index) +{ +} + +VerilogDclBus::VerilogDclBus(PortDirection *dir, + int from_index, + int to_index, + VerilogDclArg *arg, + int line) : + VerilogDcl(dir, arg, line), + from_index_(from_index), + to_index_(to_index) +{ +} + +int +VerilogDclBus::size() const +{ + return abs(to_index_ - from_index_) + 1; +} + +VerilogDclArg::VerilogDclArg(const char *net_name) : + net_name_(net_name), + assign_(nullptr) +{ +} + +VerilogDclArg::VerilogDclArg(VerilogAssign *assign) : + net_name_(nullptr), + assign_(assign) +{ +} + +VerilogDclArg::~VerilogDclArg() +{ + stringDelete(net_name_); + delete assign_; +} + +const char * +VerilogDclArg::netName() +{ + if (net_name_) + return net_name_; + else if (assign_) + return assign_->lhs()->name(); + else + return nullptr; +} + +VerilogAssign::VerilogAssign(VerilogNet *lhs, + VerilogNet *rhs, + int line) : + VerilogStmt(line), + lhs_(lhs), + rhs_(rhs) +{ +} + +VerilogAssign::~VerilogAssign() +{ + delete lhs_; + delete rhs_; +} + +//////////////////////////////////////////////////////////////// + +class VerilogNullNetNameIterator : public VerilogNetNameIterator +{ +public: + VerilogNullNetNameIterator() {} + virtual bool hasNext() { return false; } + virtual const char *next() { return nullptr; } + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogNullNetNameIterator); +}; + +class VerilogOneNetNameIterator : public VerilogNetNameIterator +{ +public: + explicit VerilogOneNetNameIterator(const char *name); + virtual bool hasNext(); + virtual const char *next(); + +protected: + const char *name_; + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogOneNetNameIterator); +}; + +VerilogOneNetNameIterator::VerilogOneNetNameIterator(const char *name) : + name_(name) +{ +} + +bool +VerilogOneNetNameIterator::hasNext() +{ + return name_ != nullptr; +} + +const char * +VerilogOneNetNameIterator::next() +{ + const char *name = name_; + name_ = nullptr; + return name; +} + +class VerilogBusNetNameIterator : public VerilogNetNameIterator +{ +public: + VerilogBusNetNameIterator(const char *bus_name, + int from_index, + int to_index); + virtual bool hasNext(); + virtual const char *next(); + +protected: + const char *bus_name_; + int from_index_; + int to_index_; + int index_; + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogBusNetNameIterator); +}; + +VerilogBusNetNameIterator::VerilogBusNetNameIterator(const char *bus_name, + int from_index, + int to_index) : + bus_name_(bus_name), + from_index_(from_index), + to_index_(to_index), + index_(from_index) +{ +} + +bool +VerilogBusNetNameIterator::hasNext() +{ + return (to_index_ > from_index_ + && index_ <= to_index_) + || (to_index_ <= from_index_ + && index_ >= to_index_); +} + +const char * +VerilogBusNetNameIterator::next() +{ + const char *bit_name = verilogBusBitNameTmp(bus_name_, index_); + if (to_index_ > from_index_) + index_++; + else + index_--; + return bit_name; +} + +static const char * +verilogBusBitNameTmp(const char *bus_name, + int index) +{ + return stringPrintTmp("%s[%d]", bus_name, index); +} + +static const char * +verilogBusBitName(const char *bus_name, + int index) +{ + return stringPrint("%s[%d]", bus_name, index); +} + +class VerilogConstantNetNameIterator : public VerilogNetNameIterator +{ +public: + VerilogConstantNetNameIterator(VerilogConstantValue *value, + const char *zero, + const char *one); + virtual bool hasNext(); + virtual const char *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogConstantNetNameIterator); + + VerilogConstantValue *value_; + const char *zero_; + const char *one_; + int bit_index_; +}; + +VerilogConstantNetNameIterator:: +VerilogConstantNetNameIterator(VerilogConstantValue *value, + const char *zero, + const char *one) : + value_(value), + zero_(zero), + one_(one), + bit_index_(value->size() - 1) +{ +} + +bool +VerilogConstantNetNameIterator::hasNext() +{ + return bit_index_ >= 0; +} + +const char * +VerilogConstantNetNameIterator::next() +{ + return (*value_)[bit_index_--] ? one_ : zero_; +} + +class VerilogNetConcatNameIterator : public VerilogNetNameIterator +{ +public: + VerilogNetConcatNameIterator(VerilogNetSeq *nets, + VerilogModule *module, + VerilogReader *reader); + virtual ~VerilogNetConcatNameIterator(); + virtual bool hasNext(); + virtual const char *next(); + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogNetConcatNameIterator); + + VerilogModule *module_; + VerilogReader *reader_; + VerilogNetSeq::Iterator net_iter_; + VerilogNetNameIterator *net_name_iter_; +}; + +VerilogNetConcatNameIterator:: +VerilogNetConcatNameIterator(VerilogNetSeq *nets, + VerilogModule *module, + VerilogReader *reader) : + module_(module), + reader_(reader), + net_iter_(nets), + net_name_iter_(nullptr) +{ + if (net_iter_.hasNext()) + net_name_iter_ = net_iter_.next()->nameIterator(module, reader); +} + +VerilogNetConcatNameIterator::~VerilogNetConcatNameIterator() +{ + delete net_name_iter_; +} + +bool +VerilogNetConcatNameIterator::hasNext() +{ + return (net_name_iter_ && net_name_iter_->hasNext()) + || net_iter_.hasNext(); +} + +const char * +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(); + 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 nullptr; +} + +//////////////////////////////////////////////////////////////// + +VerilogNetNamed::VerilogNetNamed(const char *name) : + VerilogNet(), + name_(name) +{ +} + +VerilogNetNamed::~VerilogNetNamed() +{ + stringDelete(name_); +} + +void +VerilogNetNamed::setName(const char *name) +{ + name_ = name; +} + +VerilogNetScalar::VerilogNetScalar(const char *name) : + VerilogNetNamed(name) +{ +} + +static int +verilogNetScalarSize(const char *name, + VerilogModule *module) +{ + VerilogDcl *dcl = module->declaration(name); + if (dcl) + return dcl->size(); + else + // undeclared signals are size 1 + return 1; +} + +int +VerilogNetScalar::size(VerilogModule *module) +{ + return verilogNetScalarSize(name_, module); +} + +static VerilogNetNameIterator * +verilogNetScalarNameIterator(const char *name, + VerilogModule *module) +{ + if (name) { + 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()); + } + else + return new VerilogOneNetNameIterator(name); + } + else + return new VerilogOneNetNameIterator(nullptr); +} + +VerilogNetNameIterator * +VerilogNetScalar::nameIterator(VerilogModule *module, + VerilogReader *) +{ + return verilogNetScalarNameIterator(name_, module); +} + +VerilogNetBitSelect::VerilogNetBitSelect(const char *name, + int index) : + VerilogNetNamed(name), + index_(index) +{ +} + +const char * +VerilogNetBitSelect::name() +{ + return verilogBusBitNameTmp(name_, index_); +} + +int +VerilogNetBitSelect::size(VerilogModule *) +{ + return 1; +} + +VerilogNetNameIterator * +VerilogNetBitSelect::nameIterator(VerilogModule *, + VerilogReader *) +{ + return new VerilogOneNetNameIterator(verilogBusBitNameTmp(name_, index_)); +} + +VerilogNetPartSelect::VerilogNetPartSelect(const char *name, + int from_index, + int to_index): + VerilogNetNamed(name), + from_index_(from_index), + to_index_(to_index) +{ +} + +int +VerilogNetPartSelect::size(VerilogModule *) +{ + if (to_index_ > from_index_) + return to_index_ - from_index_ + 1; + else + return from_index_ - to_index_ + 1; +} + +VerilogNetNameIterator * +VerilogNetPartSelect::nameIterator(VerilogModule *, + VerilogReader *) +{ + return new VerilogBusNetNameIterator(name_, from_index_, to_index_); +} + +VerilogNetConstant::VerilogNetConstant(const char *constant, + VerilogReader *reader) +{ + parseConstant(constant, reader); +} + +void +VerilogNetConstant::parseConstant(const char *constant, + VerilogReader *reader) +{ + size_t constant_length = strlen(constant); + char *tmp = new char[constant_length + 1]; + char *t = tmp; + const char *c = constant; + + // Copy the constant size to tmp. + while (*c && *c != '\'') + *t++ = *c++; + *t = '\0'; + + // Read the constant size. + size_t size = atol(tmp); + value_ = new VerilogConstantValue(size); + + // Read the constant base. + const char *base_ptr = c + 1; + char base_ch = *base_ptr; + switch (base_ch) { + case 'b': + case 'B': + parseConstant(constant, constant_length, base_ptr, 2, 1); + break; + case 'o': + case 'O': + parseConstant(constant, constant_length, base_ptr, 8, 3); + break; + case 'h': + case 'H': + parseConstant(constant, constant_length, base_ptr, 16, 4); + break; + case 'd': + case 'D': + parseConstant10(base_ptr + 1, tmp, reader); + break; + default: + case '\0': + VerilogParse_error("unknown constant base.\n"); + break; + } + + stringDelete(tmp); + stringDelete(constant); +} + +void +VerilogNetConstant::parseConstant(const char *constant, + size_t constant_length, + const char *base_ptr, + int base, + int digit_bit_count) +{ + // Scan the constant from LSD to MSD. + size_t size = value_->size(); + char value_digit_str[2]; + char *end; + value_digit_str[1] = '\0'; + const char *c = &constant[constant_length - 1]; + size_t bit = 0; + while (bit < size) { + char ch = (c > base_ptr) ? *c-- : '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; + } + } + } +} + +void +VerilogNetConstant::parseConstant10(const char *constant_str, + char *tmp, + VerilogReader *reader) +{ + // Copy the constant skipping underscores. + char *t = tmp; + for (const char *c = constant_str; *c; c++) { + char ch = *c; + if (ch != '_') + *t++ = ch; + } + *t = '\0'; + + size_t size = value_->size(); + size_t length = strlen(tmp); + size_t max_length = reader->constant10MaxLength(); + if (length > max_length + || (length == max_length + && strcmp(tmp, reader->constant10Max()) > 0)) + reader->warn(reader->filename(), reader->line(), + "base 10 constant greater than %s not supported.\n", + reader->constant10Max()); + else { + char *end; + VerilogConstant10 value = strtoull(tmp, &end, 10); + VerilogConstant10 mask = 1; + for (size_t bit = 0; bit < size; bit++) { + (*value_)[bit] = (value & mask) != 0; + mask = mask << 1; + } + } +} + +VerilogNetConstant::~VerilogNetConstant() +{ + delete value_; +} + +VerilogNetNameIterator * +VerilogNetConstant::nameIterator(VerilogModule *, + VerilogReader *reader) +{ + return new VerilogConstantNetNameIterator(value_, + reader->zeroNetName(), + reader->oneNetName()); +} + + +int +VerilogNetConstant::size(VerilogModule *) +{ + return value_->size(); +} + + +VerilogNetConcat::VerilogNetConcat(VerilogNetSeq *nets) : + nets_(nets) +{ +} + +VerilogNetConcat::~VerilogNetConcat() +{ + nets_->deleteContents(); + delete nets_; +} + +int +VerilogNetConcat::size(VerilogModule *module) +{ + VerilogNetSeq::Iterator net_iter(nets_); + int sz = 0; + while (net_iter.hasNext()) { + VerilogNet *net = net_iter.next(); + sz += net->size(module); + } + return sz; +} + +VerilogNetNameIterator * +VerilogNetConcat::nameIterator(VerilogModule *module, + VerilogReader *reader) +{ + return new VerilogNetConcatNameIterator(nets_, module, reader); +} + +VerilogNetPortRef::VerilogNetPortRef(const char *name) : + VerilogNetScalar(name) +{ +} + +VerilogNetPortRefScalarNet:: +VerilogNetPortRefScalarNet(const char *name, + const char *net_name) : + VerilogNetPortRef(name), + net_name_(net_name) +{ +} + +VerilogNetPortRefScalarNet::~VerilogNetPortRefScalarNet() +{ + stringDelete(net_name_); +} + +int +VerilogNetPortRefScalarNet::size(VerilogModule *module) +{ + // VerilogNetScalar::size + VerilogDcl *dcl = nullptr; + if (net_name_) + dcl = module->declaration(net_name_); + if (dcl) + return dcl->size(); + else + // undeclared signals are size 1 + return 1; +} + +VerilogNetNameIterator * +VerilogNetPortRefScalarNet::nameIterator(VerilogModule *module, + VerilogReader *) +{ + return verilogNetScalarNameIterator(net_name_, module); +} + +VerilogNetPortRefScalar::VerilogNetPortRefScalar(const char *name, + VerilogNet *net) : + VerilogNetPortRef(name), + net_(net) +{ +} + +VerilogNetPortRefScalar::~VerilogNetPortRefScalar() +{ + delete net_; +} + +int +VerilogNetPortRefScalar::size(VerilogModule *module) +{ + if (net_) + return net_->size(module); + else + return 0; +} + +VerilogNetNameIterator * +VerilogNetPortRefScalar::nameIterator(VerilogModule *module, + VerilogReader *reader) +{ + if (net_) + return net_->nameIterator(module, reader); + else + return new VerilogNullNetNameIterator(); +} + +VerilogNetPortRefBit::VerilogNetPortRefBit(const char *name, + int index, + VerilogNet *net) : + VerilogNetPortRefScalar(name, net), + index_(index) +{ +} + +const char * +VerilogNetPortRefBit::name() +{ + return verilogBusBitNameTmp(name_, index_); +} + +VerilogNetPortRefPart::VerilogNetPortRefPart(const char *name, + int from_index, + int to_index, + VerilogNet *net) : + VerilogNetPortRefBit(name, from_index, net), + to_index_(to_index) +{ +} + +const char * +VerilogNetPortRefPart::name() +{ + return name_; +} + +//////////////////////////////////////////////////////////////// +// +// Link verilog network +// +//////////////////////////////////////////////////////////////// + +Instance * +linkVerilogNetwork(const char *top_cell_name, + bool make_black_boxes, + Report *report, + NetworkReader *) +{ + return verilog_reader->linkNetwork(top_cell_name, make_black_boxes, report); +} + +// Verilog net name to network net map. +typedef Map BindingMap; + +class VerilogBindingTbl +{ +public: + VerilogBindingTbl(const char *zero_net_name_, + const char *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); + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogBindingTbl); + + const char *zero_net_name_; + const char *one_net_name_; + BindingMap map_; +}; + +Instance * +VerilogReader::linkNetwork(const char *top_cell_name, + bool make_black_boxes, + Report *report) +{ + if (library_) { + Cell *top_cell = network_->findCell(library_, top_cell_name); + 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, "", 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 char *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(report); + deleteModules(); + if (errors) { + network_->deleteInstance(top_instance); + return nullptr; + } + else + return top_instance; + } + else { + report->error("%s is not a verilog module.\n", top_cell_name); + return nullptr; + } + } + else { + report->error("%s is not a verilog module.\n", top_cell_name); + return nullptr; + } +} + +void +VerilogReader::makeModuleInstBody(VerilogModule *module, + Instance *inst, + VerilogBindingTbl *bindings, + bool make_black_boxes) +{ + VerilogStmtSeq::Iterator stmt_iter(module->stmts()); + while (stmt_iter.hasNext()) { + VerilogStmt *stmt = stmt_iter.next(); + if (stmt->isModuleInst()) + makeModuleInstNetwork(dynamic_cast(stmt), + inst, module, bindings, make_black_boxes); + else if (stmt->isLibertyInst()) + makeLibertyInst(dynamic_cast(stmt), + inst, module, bindings); + else if (stmt->isDeclaration()) { + 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(),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); + } +} + +void +VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings, + bool make_black_boxes) +{ + const char *module_name = mod_inst->moduleName(); + Cell *cell = network_->findAnyCell(module_name); + if (cell == nullptr) { + if (make_black_boxes) { + cell = makeBlackBox(mod_inst, parent_module); + linkWarn(filename_, mod_inst->line(), + "module %s not found. Creating black box for %s.\n", + mod_inst->moduleName(), + verilogName(mod_inst)); + } + else + linkError(filename_, mod_inst->line(), + "module %s not found for instance %s.\n", + mod_inst->moduleName(), + verilogName(mod_inst)); + } + if (cell) { + Instance *inst = network_->makeInstance(cell, mod_inst->instanceName(), + parent); + bool is_leaf = network_->isLeaf(cell); + if (is_leaf) { + // Make all pins. + LibertyCell *lib_cell = network_->libertyCell(cell); + if (lib_cell) { + LibertyCellPortBitIterator port_iter(lib_cell); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + network_->makePin(inst, reinterpret_cast(port), nullptr); + } + } + } + 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); + else + makeOrderedInstPins(cell, inst, mod_inst, &bindings, parent, + parent_module, parent_bindings, is_leaf); + } + if (!is_leaf) { + VerilogModule *module = this->module(cell); + makeModuleInstBody(module, inst, &bindings, make_black_boxes); + } + } +} + +void +VerilogReader::makeNamedInstPins(Cell *cell, + Instance *inst, + VerilogModuleInst *mod_inst, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogModule *parent_module, + VerilogBindingTbl *parent_bindings, + bool is_leaf) +{ + VerilogNetSeq::Iterator pin_iter(mod_inst->pins()); + while (pin_iter.hasNext()) { + VerilogNetPortRef *vpin = dynamic_cast(pin_iter.next()); + const char *port_name = vpin->name(); + Port *port = network_->findPort(cell, port_name); + if (port) { + if (vpin->hasNet() + && network_->size(port) != vpin->size(parent_module)) + linkWarn(parent_module->filename(), mod_inst->line(), + "instance %s port %s size %d does not match net size %d.\n", + verilogName(mod_inst), + 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; + } + } + else { + linkWarn(parent_module->filename(), mod_inst->line(), + "instance %s port %s not found.\n", + verilogName(mod_inst), + 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) +{ + 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(); + Port *port = port_iter->next(); + if (network_->size(port) != net->size(parent_module)) + linkWarn(parent_module->filename(), mod_inst->line(), + "instance %s port %s size %d does not match net size %d.\n", + verilogName(mod_inst), + network_->name(port), + network_->size(port), + net->size(parent_module)); + else { + 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; + } + else + makeInstPin(inst, port, net_name_iter, bindings, + parent, parent_bindings, is_leaf); + delete net_name_iter; + } + } + delete port_iter; +} + +void +VerilogReader::makeInstPin(Instance *inst, + Port *port, + VerilogNetNameIterator *net_name_iter, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogBindingTbl *parent_bindings, + bool is_leaf) +{ + const char *net_name = nullptr; + if (net_name_iter->hasNext()) + net_name = net_name_iter->next(); + makeInstPin(inst, port, net_name, bindings, parent, parent_bindings, + is_leaf); +} + +void +VerilogReader::makeInstPin(Instance *inst, + Port *port, + const char *net_name, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogBindingTbl *parent_bindings, + bool is_leaf) +{ + Net *net = nullptr; + if (net_name) + 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); + Net *child_net = bindings->ensureNetBinding(port_name, inst, network_); + network_->makeTerm(pin, child_net); + } + } +} + +void +VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, + 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(), + parent); + const char **net_names = lib_inst->netNames(); + LibertyCellPortBitIterator port_iter(lib_cell); + while (port_iter.hasNext()) { + LibertyPort *port = port_iter.next(); + const char *net_name = net_names[port->pinIndex()]; + // net_name may be the name of a single bit bus. + if (net_name) { + Net *net = nullptr; + // If the pin is unconnected (ie, .A()) make the pin but not the net. + if (net_name != unconnected_net_name) { + VerilogDcl *dcl = parent_module->declaration(net_name); + // Check for single bit bus reference .A(BUS) -> .A(BUS[LSB]). + if (dcl && dcl->isBus()) { + VerilogDclBus *dcl_bus = dynamic_cast(dcl); + // Bus is only 1 bit wide. + const char *bus_name = verilogBusBitNameTmp(net_name, + dcl_bus->fromIndex()); + net = parent_bindings->ensureNetBinding(bus_name, parent, network_); + } + else + 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); + } +} + +//////////////////////////////////////////////////////////////// + +Cell * +VerilogReader::makeBlackBox(VerilogModuleInst *mod_inst, + VerilogModule *parent_module) +{ + const char *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 + makeBlackBoxOrderedPorts(cell, mod_inst, parent_module); + return cell; +} + +void +VerilogReader::makeBlackBoxNamedPorts(Cell *cell, + 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(); + size_t size = vpin->size(parent_module); + Port *port = (size == 1) + ? network_->makePort(cell, port_name) + : network_->makeBusPort(cell, port_name, 0, size - 1); + network_->setDirection(port, PortDirection::bidirect()); + } +} + +void +VerilogReader::makeBlackBoxOrderedPorts(Cell *cell, + 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::bidirect()); + port_index++; + } +} + +bool +VerilogReader::isBlackBox(Cell *cell) +{ + return network_->library(cell) == library_; +} + +//////////////////////////////////////////////////////////////// + +void +VerilogReader::mergeAssignNet(VerilogAssign *assign, + VerilogModule *module, + Instance *inst, + VerilogBindingTbl *bindings) +{ + VerilogNet *lhs = assign->lhs(); + VerilogNet *rhs = assign->rhs(); + if (lhs->size(module) == rhs->size(module)) { + VerilogNetNameIterator *lhs_iter = lhs->nameIterator(module, this); + VerilogNetNameIterator *rhs_iter = rhs->nameIterator(module, this); + while (lhs_iter->hasNext() && rhs_iter->hasNext()) { + const char *lhs_name = lhs_iter->next(); + const char *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); + else + network_->mergeInto(rhs_net, lhs_net); + // No need to update binding tables because the VerilogBindingTbl::find + // finds the net that survives the merge. + } + delete lhs_iter; + delete rhs_iter; + } + else + linkWarn(module->filename(), assign->line(), + "assign left hand side size %d not equal right hand size %d.\n", + lhs->size(module), + rhs->size(module)); +} + +static int +hierarchyLevel(Net *net, + Network *network) +{ + Instance *parent = network->instance(net); + int level = 0; + while (parent) { + parent = network->parent(parent); + level++; + } + return level; +} + +//////////////////////////////////////////////////////////////// + +VerilogBindingTbl::VerilogBindingTbl(const char *zero_net_name, + const char *one_net_name) : + zero_net_name_(zero_net_name), + one_net_name_(one_net_name) +{ +} + +// Follow the merged_into pointers rather than update the +// 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) +{ + Net *net = map_.findKey(name); + while (net && network->mergedInto(net)) + net = network->mergedInto(net); + return net; +} + +void +VerilogBindingTbl::bind(const char *name, + Net *net) +{ + map_[name] = net; +} + +Net * +VerilogBindingTbl::ensureNetBinding(const char *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 (stringEq(net_name, zero_net_name_)) + network->addConstantNet(net, LogicValue::zero); + if (stringEq(net_name, one_net_name_)) + network->addConstantNet(net, LogicValue::one); + } + return net; +} + +//////////////////////////////////////////////////////////////// + +void +VerilogReader::linkWarn(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(filename, line, msg_str, true); + link_errors_.push_back(error); + va_end(args); +} + +void +VerilogReader::linkError(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(filename, line, msg_str, false); + link_errors_.push_back(error); + va_end(args); +} + +bool +VerilogReader::reportLinkErrors(Report *report) +{ + // Sort errors so they are in line number order rather than the order + // 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(); + error->report(report); + errors |= !error->warn(); + delete error; + } + link_errors_.clear(); + return errors; +} + +} // namespace + +//////////////////////////////////////////////////////////////// +// Global namespace + +void verilogFlushBuffer(); + +int +VerilogParse_error(const char *msg) +{ + sta::verilog_reader->report()->fileError(sta::verilog_reader->filename(), + sta::verilog_reader->line(), + "%s.\n", msg); + verilogFlushBuffer(); + return 0; +} diff --git a/verilog/VerilogReader.hh b/verilog/VerilogReader.hh new file mode 100644 index 0000000..1265fcc --- /dev/null +++ b/verilog/VerilogReader.hh @@ -0,0 +1,34 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_READ_VERILOG_H +#define STA_READ_VERILOG_H + +namespace sta { + +class NetworkReader; + +// Return true if successful. +bool +readVerilogFile(const char *filename, + NetworkReader *network); + +void +deleteVerilogReader(); + +} // namespace + +#endif diff --git a/verilog/VerilogReaderPvt.hh b/verilog/VerilogReaderPvt.hh new file mode 100644 index 0000000..81b5823 --- /dev/null +++ b/verilog/VerilogReaderPvt.hh @@ -0,0 +1,728 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_VERILOG_H +#define STA_VERILOG_H + +#include "DisallowCopyAssign.hh" +#include "Zlib.hh" +#include "Vector.hh" +#include "Map.hh" +#include "StringSeq.hh" +#include "StringSet.hh" +#include "NetworkClass.hh" + +// Global namespace. +#define YY_INPUT(buf,result,max_size) \ + sta::verilog_reader->getChars(buf, result, max_size) + +int +VerilogParse_error(const char *msg); + +namespace sta { + +class Debug; +class Report; +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 Vector VerilogNetSeq; +typedef Vector VerilogStmtSeq; +typedef Map VerilogDclMap; +typedef Vector VerilogDclArgSeq; +typedef Map VerilogModuleMap; +typedef Vector VerilogErrorSeq; +typedef Vector VerilogConstantValue; +// Max base 10 constant net value (for strtoll). +typedef unsigned long long VerilogConstant10; + +extern VerilogReader *verilog_reader; + +class VerilogReader +{ +public: + explicit VerilogReader(NetworkReader *network); + ~VerilogReader(); + bool read(const char *filename); + // flex YY_INPUT yy_n_chars arg changed definition from int to size_t, + // so provide both forms. + void getChars(char *buf, + size_t &result, + size_t max_size); + void getChars(char *buf, + int &result, + size_t max_size); + void makeModule(const char *module_name, + VerilogNetSeq *ports, + VerilogStmtSeq *stmts, + int line); + void makeModule(const char *module_name, + VerilogStmtSeq *port_dcls, + VerilogStmtSeq *stmts, + int line); + VerilogDcl *makeDcl(PortDirection *dir, + VerilogDclArgSeq *args, + int line); + VerilogDcl *makeDcl(PortDirection *dir, + VerilogDclArg *arg, + int line); + VerilogDclArg *makeDclArg(const char *net_name); + VerilogDclArg*makeDclArg(VerilogAssign *assign); + VerilogDclBus *makeDclBus(PortDirection *dir, + int from_index, + int to_index, + VerilogDclArg *arg, + int line); + VerilogDclBus *makeDclBus(PortDirection *dir, + int from_index, + int to_index, + VerilogDclArgSeq *args, + int line); + VerilogInst *makeModuleInst(const char *module_name, + const char *inst_name, + VerilogNetSeq *pins, + const int line); + VerilogAssign *makeAssign(VerilogNet *lhs, + VerilogNet *rhs, + int line); + VerilogNetScalar *makeNetScalar(const char *name); + VerilogNetPortRef *makeNetNamedPortRefScalarNet(const char *port_name, + const char *net_name); + VerilogNetPortRef *makeNetNamedPortRefBitSelect(const char *port_name, + const char *bus_name, + int index); + VerilogNetPortRef *makeNetNamedPortRefScalar(const char *port_name, + VerilogNet *net); + VerilogNetPortRef *makeNetNamedPortRefBit(const char *port_name, + int index, + VerilogNet *net); + VerilogNetPortRef *makeNetNamedPortRefPart(const char *port_name, + int from_index, + int to_index, + VerilogNet *net); + VerilogNetConcat *makeNetConcat(VerilogNetSeq *nets); + VerilogNetConstant *makeNetConstant(const char *constant); + VerilogNetBitSelect *makeNetBitSelect(const char *name, + int index); + VerilogNetPartSelect *makeNetPartSelect(const char *name, + int from_index, + int to_index); + VerilogModule *module(Cell *cell); + Instance *linkNetwork(const char *top_cell_name, + bool make_black_boxes, + Report *report); + int line() const { return line_; } + const char *filename() const { return filename_; } + void incrLine(); + Report *report() const { return report_; } + void error(const char *filename, + int line, + const char *fmt, ...); + void warn(const char *filename, + int line, + const char *fmt, ...); + const char *zeroNetName() const { return zero_net_name_; } + const char *oneNetName() const { return one_net_name_; } + void deleteModules(); + void reportStmtCounts(); + const char *constant10Max() const { return constant10_max_; } + size_t constant10MaxLength() const { return constant10_max_length_; } + const char * + verilogName(VerilogModuleInst *inst); + const char * + instanceVerilogName(const char *inst_name); + const char * + netVerilogName(const char *net_name); + +protected: + DISALLOW_COPY_AND_ASSIGN(VerilogReader); + void init(const char *filename); + void makeCellPorts(Cell *cell, + VerilogModule *module, + VerilogNetSeq *ports); + Port *makeCellPort(Cell *cell, + VerilogModule *module, + const char *port_name); + void makeNamedPortRefCellPorts(Cell *cell, + VerilogModule *module, + VerilogNet *mod_port, + StringSet &port_names); + void checkModuleDcls(VerilogModule *module, + StringSet &port_names); + void makeModuleInstBody(VerilogModule *module, + Instance *inst, + VerilogBindingTbl *bindings, + bool make_black_boxes); + 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 char *net_name, + VerilogBindingTbl *bindings, + Instance *parent, + VerilogBindingTbl *parent_bindings, + bool is_leaf); + void linkWarn(const char *filename, + int line, + const char *msg, ...); + void linkError(const char *filename, + int line, + const char *msg, ...); + bool reportLinkErrors(Report *report); + 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); + + Report *report_; + Debug *debug_; + NetworkReader *network_; + + const char *filename_; + int line_; + gzFile stream_; + + Library *library_; + int black_box_index_; + VerilogModuleMap module_map_; + VerilogErrorSeq link_errors_; + const char *zero_net_name_; + const char *one_net_name_; + const char *constant10_max_; + size_t constant10_max_length_; + 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_; +}; + +class VerilogStmt +{ +public: + explicit VerilogStmt(int line); + virtual ~VerilogStmt() {} + virtual bool isInstance() const { return false; } + virtual bool isModuleInst() const { return false; } + virtual bool isLibertyInst() const { return false; } + virtual bool isAssign() const { return false; } + virtual bool isDeclaration() const { return false; } + int line() const { return line_; } + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogStmt); + + int line_; +}; + +class VerilogModule : public VerilogStmt +{ +public: + VerilogModule(const char *name, + VerilogNetSeq *ports, + VerilogStmtSeq *stmts, + const char *filename, + int line, + VerilogReader *reader); + virtual ~VerilogModule(); + const char *name() { return name_; } + const char *filename() { return filename_; } + VerilogNetSeq *ports() { return ports_; } + VerilogDcl *declaration(const char *net_name); + VerilogStmtSeq *stmts() { return stmts_; } + VerilogDclMap *declarationMap() { return &dcl_map_; } + void parseDcl(VerilogDcl *dcl, + VerilogReader *reader); + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogModule); + void parseStmts(VerilogReader *reader); + void checkInstanceName(VerilogInst *inst, + StringSet &inst_names, + VerilogReader *reader); + + const char *name_; + const char *filename_; + VerilogNetSeq *ports_; + VerilogStmtSeq *stmts_; + VerilogDclMap dcl_map_; +}; + +class VerilogDcl : public VerilogStmt +{ +public: + VerilogDcl(PortDirection *dir, + VerilogDclArgSeq *args, + int line); + VerilogDcl(PortDirection *dir, + VerilogDclArg *arg, + int line); + virtual ~VerilogDcl(); + const char *portName(); + virtual bool isBus() const { return false; } + virtual bool isDeclaration() const { return true; } + VerilogDclArgSeq *args() const { return args_; } + void appendArg(VerilogDclArg *arg); + PortDirection *direction() const { return dir_; } + virtual int size() const { return 1; } + void check(); + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogDcl); + + PortDirection *dir_; + VerilogDclArgSeq *args_; +}; + +class VerilogDclBus : public VerilogDcl +{ +public: + VerilogDclBus(PortDirection *dir, + int from_index, + int to_index, + VerilogDclArgSeq *args, + int line); + VerilogDclBus(PortDirection *dir, + int from_index, + int to_index, + VerilogDclArg *arg, + int line); + virtual bool isBus() const { return true; } + int fromIndex() const { return from_index_; } + int toIndex() const { return to_index_; } + virtual int size() const; + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogDclBus); + + int from_index_; + int to_index_; +}; + +// Declaratation arguments can be a net name or an assignment. +class VerilogDclArg +{ +public: + explicit VerilogDclArg(const char *net_name); + explicit VerilogDclArg(VerilogAssign *assign); + ~VerilogDclArg(); + const char *netName(); + VerilogAssign *assign() { return assign_; } + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogDclArg); + + const char *net_name_; + VerilogAssign *assign_; +}; + +// Continuous assignment. +class VerilogAssign : public VerilogStmt +{ +public: + VerilogAssign(VerilogNet *lhs, + VerilogNet *rhs, + int line); + virtual ~VerilogAssign(); + virtual bool isAssign() const { return true; } + VerilogNet *lhs() const { return lhs_; } + VerilogNet *rhs() const { return rhs_; } + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogAssign); + + VerilogNet *lhs_; + VerilogNet *rhs_; +}; + +class VerilogInst : public VerilogStmt +{ +public: + VerilogInst(const char *inst_name, + const int line); + virtual ~VerilogInst(); + virtual bool isInstance() const { return true; } + const char *instanceName() const { return inst_name_; } + void setInstanceName(const char *inst_name); + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogInst); + + const char *inst_name_; +}; + +class VerilogModuleInst : public VerilogInst +{ +public: + VerilogModuleInst(const char *module_name, + const char *inst_name, + VerilogNetSeq *pins, + const int line); + virtual ~VerilogModuleInst(); + virtual bool isModuleInst() const { return true; } + const char *moduleName() const { return module_name_; } + VerilogNetSeq *pins() const { return pins_; } + bool namedPins(); + bool hasPins(); + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogModuleInst); + + const char *module_name_; + VerilogNetSeq *pins_; +}; + +// Instance of liberty cell when all connections are single bit. +// Connections are an array of net name strings indexed by port pin +// index. +class VerilogLibertyInst : public VerilogInst +{ +public: + VerilogLibertyInst(LibertyCell *cell, + const char *inst_name, + const char **net_names, + const int line); + virtual ~VerilogLibertyInst(); + virtual bool isLibertyInst() const { return true; } + LibertyCell *cell() const { return cell_; } + const char **netNames() const { return net_names_; } + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogLibertyInst); + + LibertyCell *cell_; + const char **net_names_; +}; + +// Abstract base class for nets. +class VerilogNet +{ +public: + VerilogNet() {} + virtual ~VerilogNet() {} + virtual bool isNamed() = 0; + virtual const char *name() = 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; + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogNet); +}; + +class VerilogNetUnnamed : public VerilogNet +{ +public: + VerilogNetUnnamed() {} + virtual bool isNamed() { return false; } + virtual const char *name() { return nullptr; } + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogNetUnnamed); +}; + +class VerilogNetNamed : public VerilogNet +{ +public: + explicit VerilogNetNamed(const char *name); + virtual ~VerilogNetNamed(); + virtual bool isNamed() { return true; } + virtual bool isScalar() const = 0; + virtual const char *name() { return name_; } + const char *baseName() { return name_; } + void setName(const char *name); + +protected: + const char *name_; + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogNetNamed); +}; + +// Named net reference, which could be the name of a scalar or bus signal. +class VerilogNetScalar : public VerilogNetNamed +{ +public: + explicit VerilogNetScalar(const char *name); + virtual bool isScalar() const { return true; } + virtual int size(VerilogModule *module); + virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader); + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogNetScalar); +}; + +class VerilogNetBitSelect : public VerilogNetNamed +{ +public: + VerilogNetBitSelect(const char *name, + int index); + virtual const char *name(); + int index() { return index_; } + virtual bool isScalar() const { return false; } + virtual int size(VerilogModule *module); + virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader); + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogNetBitSelect); + + int index_; +}; + +class VerilogNetPartSelect : public VerilogNetNamed +{ +public: + VerilogNetPartSelect(const char *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); + int fromIndex() const { return from_index_; } + int toIndex() const { return to_index_; } + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogNetPartSelect); + + int from_index_; + int to_index_; +}; + +class VerilogNetConstant : public VerilogNetUnnamed +{ +public: + VerilogNetConstant(const char *constant, + VerilogReader *reader); + virtual ~VerilogNetConstant(); + virtual int size(VerilogModule *module); + virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader); + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogNetConstant); + void parseConstant(const char *constant, + VerilogReader *reader); + void parseConstant(const char *constant, + size_t constant_length, + const char *base_ptr, + int base, + int digit_bit_count); + void parseConstant10(const char *constant_str, + char *tmp, + VerilogReader *reader); + + VerilogConstantValue *value_; +}; + +class VerilogNetConcat : public VerilogNetUnnamed +{ +public: + explicit VerilogNetConcat(VerilogNetSeq *nets); + virtual ~VerilogNetConcat(); + virtual int size(VerilogModule *module); + virtual VerilogNetNameIterator *nameIterator(VerilogModule *module, + VerilogReader *reader); + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogNetConcat); + + VerilogNetSeq *nets_; +}; + +// Module instance named port reference base class. +class VerilogNetPortRef : public VerilogNetScalar +{ +public: + explicit VerilogNetPortRef(const char *name); + virtual bool isNamedPortRef() { return true; } + virtual bool hasNet() = 0; + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogNetPortRef); +}; + +// Named scalar port reference to scalar net .port(net). +// This is special cased because it is so common. The overhead +// of pointers to VerilogNet objects for the port name and net name +// is quite high. +class VerilogNetPortRefScalarNet : public VerilogNetPortRef +{ +public: + VerilogNetPortRefScalarNet(const char *name, + const char *net_name); + virtual ~VerilogNetPortRefScalarNet(); + 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_ != nullptr; } + const char *netName() const { return net_name_; } + void setNetName(const char *net_name) { net_name_ = net_name; } + +private: + const char *net_name_; +}; + +class VerilogNetPortRefScalar : public VerilogNetPortRef +{ +public: + VerilogNetPortRefScalar(const char *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; } + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogNetPortRefScalar); + + VerilogNet *net_; +}; + +class VerilogNetPortRefBit : public VerilogNetPortRefScalar +{ +public: + VerilogNetPortRefBit(const char *name, + int index, + VerilogNet *net); + virtual const char *name(); + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogNetPortRefBit); + + int index_; +}; + +class VerilogNetPortRefPart : public VerilogNetPortRefBit +{ +public: + VerilogNetPortRefPart(const char *name, + int from_index, + int to_index, + VerilogNet *net); + virtual const char *name(); + int toIndex() const { return to_index_; } + +private: + DISALLOW_COPY_AND_ASSIGN(VerilogNetPortRefPart); + + int to_index_; +}; + +// Abstract class for iterating over the component nets of a net. +class VerilogNetNameIterator : public Iterator +{ +}; + +} // namespace +#endif diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc new file mode 100644 index 0000000..32ea7f2 --- /dev/null +++ b/verilog/VerilogWriter.cc @@ -0,0 +1,297 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#include +#include "Machine.hh" +#include "Error.hh" +#include "PortDirection.hh" +#include "Liberty.hh" +#include "Network.hh" +#include "NetworkCmp.hh" +#include "VerilogNamespace.hh" +#include "VerilogWriter.hh" + +namespace sta { + +class VerilogWriter +{ +public: + VerilogWriter(const char *filename, + bool sort, + FILE *stream, + Network *network); + void writeModule(Instance *inst); + +protected: + void writePorts(Cell *cell); + void writePortDcls(Cell *cell); + const char *verilogPortDir(PortDirection *dir); + void writeChildren(Instance *inst); + void writeChild(Instance *child); + void writeInstPin(Instance *inst, + Port *port, + bool &first_port); + void writeInstBusPin(Instance *inst, + Port *port, + bool &first_port); + void writeInstBusPinBit(Instance *inst, + Port *port, + bool &first_member); + + const char *filename_; + bool sort_; + FILE *stream_; + Network *network_; + + Set written_cells_; + Set pending_children_; + int unconnected_net_index_; +}; + +void +writeVerilog(const char *filename, + bool sort, + Network *network) +{ + if (network->topInstance()) { + FILE *stream = fopen(filename, "w"); + if (stream) { + VerilogWriter writer(filename, sort, stream, network); + writer.writeModule(network->topInstance()); + fclose(stream); + } + else + throw FileNotWritable(filename); + } +} + +VerilogWriter::VerilogWriter(const char *filename, + bool sort, + FILE *stream, + Network *network) : + filename_(filename), + sort_(sort), + stream_(stream), + network_(network), + unconnected_net_index_(1) +{ +} + +void +VerilogWriter::writeModule(Instance *inst) +{ + Cell *cell = network_->cell(inst); + fprintf(stream_, "module %s (", + network_->name(cell)); + writePorts(cell); + writePortDcls(cell); + fprintf(stream_, "\n"); + writeChildren(inst); + fprintf(stream_, "endmodule\n"); + written_cells_.insert(cell); + + for (auto child : pending_children_) { + Cell *child_cell = network_->cell(child); + if (!written_cells_.hasKey(child_cell)) + writeModule(child); + } +} + +void +VerilogWriter::writePorts(Cell *cell) +{ + bool first = true; + CellPortIterator *port_iter = network_->portIterator(cell); + while (port_iter->hasNext()) { + Port *port = port_iter->next(); + if (!first) + fprintf(stream_, ",\n "); + fprintf(stream_, "%s", portVerilogName(network_->name(port), + network_->pathEscape())); + first = false; + } + delete port_iter; + fprintf(stream_, ");\n"); +} + +void +VerilogWriter::writePortDcls(Cell *cell) +{ + CellPortIterator *port_iter = network_->portIterator(cell); + while (port_iter->hasNext()) { + Port *port = port_iter->next(); + PortDirection *dir = network_->direction(port); + const char *port_name = portVerilogName(network_->name(port), + network_->pathEscape()); + const char *vtype = verilogPortDir(dir); + if (vtype) { + fprintf(stream_, " %s", vtype); + if (network_->isBus(port)) + fprintf(stream_, " [%d:%d]", + network_->fromIndex(port), + network_->toIndex(port)); + fprintf(stream_, " %s;\n", port_name); + if (dir->isTristate()) { + fprintf(stream_, " tri"); + if (network_->isBus(port)) + fprintf(stream_, " [%d:%d]", + network_->fromIndex(port), + network_->toIndex(port)); + fprintf(stream_, " %s;\n", port_name); + } + } + } + delete port_iter; +} + +const char * +VerilogWriter::verilogPortDir(PortDirection *dir) +{ + if (dir == PortDirection::input()) + return "input"; + else if (dir == PortDirection::output()) + return "output"; + else if (dir == PortDirection::bidirect()) + return "inout"; + else if (dir == PortDirection::tristate()) + return "output"; + else + return nullptr; +} + +void +VerilogWriter::writeChildren(Instance *inst) +{ + Vector children; + InstanceChildIterator *child_iter = network_->childIterator(inst); + while (child_iter->hasNext()) { + Instance *child = child_iter->next(); + children.push_back(child); + if (network_->isHierarchical(child)) + pending_children_.insert(child); + } + delete child_iter; + + if (sort_) + sort(children, InstancePathNameLess(network_)); + + for (auto child : children) + writeChild(child); +} + +void +VerilogWriter::writeChild(Instance *child) +{ + Cell *child_cell = network_->cell(child); + const char *child_name = network_->name(child); + const char *child_vname = instanceVerilogName(child_name, + network_->pathEscape()); + fprintf(stream_, " %s %s (", + network_->name(child_cell), + child_vname); + bool first_port = true; + CellPortIterator *port_iter = network_->portIterator(child_cell); + while (port_iter->hasNext()) { + Port *port = port_iter->next(); + if (network_->hasMembers(port)) + writeInstBusPin(child, port, first_port); + else + writeInstPin(child, port, first_port); + } + delete port_iter; + fprintf(stream_, ");\n"); +} + +void +VerilogWriter::writeInstPin(Instance *inst, + 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); + const char *net_vname = netVerilogName(net_name, network_->pathEscape()); + if (!first_port) + fprintf(stream_, ",\n "); + const char *port_name = portVerilogName(network_->name(port), + network_->pathEscape()); + fprintf(stream_, ".%s(%s)", + port_name, + net_vname); + first_port = false; + } + } +} + +void +VerilogWriter::writeInstBusPin(Instance *inst, + Port *port, + bool &first_port) +{ + if (!first_port) + fprintf(stream_, ",\n "); + + fprintf(stream_, ".%s({", network_->name(port)); + first_port = false; + bool first_member = true; + + // Match the member order of the liberty cell if it exists. + LibertyPort *lib_port = network_->libertyPort(port); + if (lib_port) { + Cell *cell = network_->cell(inst); + LibertyPortMemberIterator member_iter(lib_port); + while (member_iter.hasNext()) { + LibertyPort *lib_member = member_iter.next(); + Port *member = network_->findPort(cell, lib_member->name()); + writeInstBusPinBit(inst, member, first_member); + } + } + else { + PortMemberIterator *member_iter = network_->memberIterator(port); + while (member_iter->hasNext()) { + Port *member = member_iter->next(); + writeInstBusPinBit(inst, member, first_member); + } + delete member_iter; + } + fprintf(stream_, "})"); +} + +void +VerilogWriter::writeInstBusPinBit(Instance *inst, + Port *port, + bool &first_member) +{ + Pin *pin = network_->findPin(inst, port); + const char *net_name = nullptr; + if (pin) { + Net *net = network_->net(pin); + if (net) + net_name = network_->name(net); + } + if (net_name == nullptr) + // There is no verilog syntax to "skip" a bit in the concatentation. + net_name = stringPrintTmp("_NC%d", unconnected_net_index_++); + const char *net_vname = netVerilogName(net_name, network_->pathEscape()); + if (!first_member) + fprintf(stream_, ",\n "); + fprintf(stream_, "%s", net_vname); + first_member = false; +} + +} // namespace diff --git a/verilog/VerilogWriter.hh b/verilog/VerilogWriter.hh new file mode 100644 index 0000000..83f3ffe --- /dev/null +++ b/verilog/VerilogWriter.hh @@ -0,0 +1,31 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2019, Parallax Software, Inc. +// +// This 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 . + +#ifndef STA_WRITE_VERILOG_H +#define STA_WRITE_VERILOG_H + +namespace sta { + +class Network; + +void +writeVerilog(const char *filename, + bool sort, + Network *network); + +} // namespace + +#endif -- cgit v1.2.3 From 25877f154ef236bfcac763b589f67747ae4e01a4 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Sun, 31 Mar 2019 21:34:17 +0200 Subject: Use readline for interactive input. This adds 'command line completion', search functionality etc Gbp-Pq: Name 0003-Use-readline-for-interactive-input.-This-adds-comman.patch --- CMakeLists.txt | 5 ++ app/StaMain.cc | 142 ++++++++++++++++++++++++++++++++++++++- cmake/modules/FindReadline.cmake | 47 +++++++++++++ 3 files changed, 191 insertions(+), 3 deletions(-) create mode 100644 cmake/modules/FindReadline.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 716d486..8b5f237 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,8 @@ cmake_minimum_required (VERSION 3.9) project(STA VERSION 2.0.16) +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules CACHE STRING "CMake module path") + set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -562,6 +564,7 @@ get_filename_component(TCL_HEADER_DIR "${TCL_HEADER}" PATH) find_package(FLEX) find_package(BISON) +find_package(Readline REQUIRED) # LibertyExpr scan/parse. bison_target(LibertyExprParser liberty/LibertyExprParse.yy ${STA_HOME}/liberty/LibertyExprParse.cc @@ -706,6 +709,8 @@ if (ZLIB_FOUND) target_link_libraries(sta ${ZLIB_LIBRARIES}) endif() +target_link_libraries(sta ${Readline_LIBRARY}) + message(STATUS "STA executable: ${STA_HOME}/app/sta") # g++ std::thread apparently still needs -pthreads. diff --git a/app/StaMain.cc b/app/StaMain.cc index 0ee8fde..81eb933 100644 --- a/app/StaMain.cc +++ b/app/StaMain.cc @@ -16,6 +16,8 @@ #include #include +#include +#include #include "Machine.hh" #include "StringUtil.hh" #include "Vector.hh" @@ -26,6 +28,12 @@ namespace sta { typedef sta::Vector SwigInitFuncSeq; +char **extra_command_completion (const char *, int, int); +char *extra_command_generator (const char *, int); +int command_exit (ClientData, Tcl_Interp *, int, Tcl_Obj *const []); +void save_history(); +void load_history(); + // "Arguments" passed to staTclAppInit. static int sta_argc; static char **sta_argv; @@ -34,6 +42,8 @@ static SwigInitFunc sta_swig_init; static const char *init_filename = "[file join $env(HOME) .sta]"; +static bool ended = 0; + void staMain(Sta *sta, int argc, @@ -41,6 +51,10 @@ staMain(Sta *sta, SwigInitFunc swig_init, const char *tcl_inits[]) { + char *buffer; + Tcl_Interp *myInterp; + int status; + initSta(); Sta::setSta(sta); @@ -50,9 +64,31 @@ staMain(Sta *sta, sta->setThreadCount(thread_count); staSetupAppInit(argc, argv, swig_init, tcl_inits); - // Set argc to 1 so Tcl_Main doesn't source any files. - // Tcl_Main never returns. - Tcl_Main(1, argv, staTclAppInit); + + myInterp = Tcl_CreateInterp(); + Tcl_CreateObjCommand(myInterp, "exit", (Tcl_ObjCmdProc*)command_exit, 0, 0); + + rl_attempted_completion_function = extra_command_completion; + + staTclAppInit(myInterp); + + load_history(); + + while((!ended) && (buffer = readline("OpenSTA> ")) != NULL) { + status = Tcl_Eval(myInterp, buffer); + if(status != TCL_OK) { + fprintf(stderr, "%s\n", Tcl_GetStringResult(myInterp)); + } + if (buffer[0] != 0) + add_history(buffer); + free(buffer); + if(ended) break; + } + + save_history(); + + Tcl_DeleteInterp(myInterp); + Tcl_Finalize(); } int @@ -215,6 +251,106 @@ evalTclInit(Tcl_Interp *interp, delete [] unencoded; } +int command_exit(ClientData, Tcl_Interp *, int, Tcl_Obj *const []) +{ + ended = 1; + return 0; +} + +char const *extra_commands[] = { + "all_clocks", + "all_inputs", + "all_outputs", + "all_registers", + "check_setup", + "create_clock", + "create_generated_clock", + "create_voltage_area", + "current_design", + "current_instance", + "define_corners", + + "get_clocks", + "get_fanin", + "get_fanout", + + "get_nets", + "get_pins", + "get_ports", + "read_liberty", + "read_parasitics", + "read_sdc", + "read_sdf", + "read_spef", + "read_verilog", + "report_annotated_delay", + "report_cell", + "report_checks", + "report_path", + "report_slack", + "set_input_delay", + "write_sdc", + "write_sdf", + NULL +}; + +char **extra_command_completion(const char *text, int, int) +{ + rl_attempted_completion_over = 0; + return rl_completion_matches(text, extra_command_generator); +} + +char *extra_command_generator(const char *text, int state) +{ + static int list_index, len; + const char *name; + + if (!state) { + list_index = 0; + len = strlen(text); + } + + while ((name = extra_commands[list_index++])) { + if(strncmp(name, text, len) == 0) { + return strdup(name); + } + } + + return NULL; +} + +void load_history() +{ + FILE *histin = fopen(".history_sta", "r"); + if(histin != NULL) { + char *line = NULL; + size_t len = 0; + ssize_t read; + while((read = getline(&line, &len, histin)) != -1) { + line[strlen(line)-1] = 0; + if (line[0] != 0) { + add_history(line); + } + } + fclose(histin); + } +} + +void save_history() +{ + printf("Saving command history\n"); + HIST_ENTRY **the_list; + the_list = history_list(); + if(the_list){ + FILE *histout = fopen(".history_sta", "w"); + for(int i=0; the_list[i] ; i++) { + fprintf(histout, "%s\n", the_list[i]->line); + } + fclose(histout); + } +} + + void showUsage(const char * prog) { diff --git a/cmake/modules/FindReadline.cmake b/cmake/modules/FindReadline.cmake new file mode 100644 index 0000000..4dcb649 --- /dev/null +++ b/cmake/modules/FindReadline.cmake @@ -0,0 +1,47 @@ +# - Try to find readline include dirs and libraries +# +# Usage of this module as follows: +# +# find_package(Readline) +# +# Variables used by this module, they can change the default behaviour and need +# to be set before calling find_package: +# +# Readline_ROOT_DIR Set this variable to the root installation of +# readline if the module has problems finding the +# proper installation path. +# +# Variables defined by this module: +# +# READLINE_FOUND System has readline, include and lib dirs found +# Readline_INCLUDE_PATH The readline include directories. +# Readline_LIBRARY The readline library. + +find_path(Readline_ROOT_DIR + NAMES include/readline/readline.h +) + +find_path(Readline_INCLUDE_PATH + NAMES readline/readline.h + HINTS ${Readline_ROOT_DIR}/include +) + +find_library(Readline_LIBRARY + NAMES readline + HINTS ${Readline_ROOT_DIR}/lib +) + +if(Readline_INCLUDE_PATH AND Readline_LIBRARY AND Ncurses_LIBRARY) + set(READLINE_FOUND TRUE) +else(Readline_INCLUDE_PATH AND Readline_LIBRARY AND Ncurses_LIBRARY) + FIND_LIBRARY(Readline_LIBRARY NAMES readline) + include(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_PATH Readline_LIBRARY ) + MARK_AS_ADVANCED(Readline_INCLUDE_PATH Readline_LIBRARY) +endif(Readline_INCLUDE_PATH AND Readline_LIBRARY AND Ncurses_LIBRARY) + +mark_as_advanced( + Readline_ROOT_DIR + Readline_INCLUDE_PATH + Readline_LIBRARY +) -- cgit v1.2.3