diff --git a/CMakeLists.txt b/CMakeLists.txt index 202733b7..518b5447 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,18 @@ -# Parallax Static Timing Analyzer +# OpenSTA, Static Timing Analyzer # Copyright (c) 2019, Parallax Software, Inc. -# All rights reserved. # -# No part of this document may be copied, transmitted or -# disclosed in any form or fashion without the express -# written consent of Parallax Software, Inc. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General 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) @@ -205,7 +213,6 @@ set(STA_SOURCE verilog/VerilogReader.cc verilog/VerilogLex.cc verilog/VerilogParse.cc - ) set(STA_HEADERS @@ -707,3 +714,10 @@ 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/INSTALL b/INSTALL index c293f50e..5c286ced 100644 --- a/INSTALL +++ b/INSTALL @@ -1,204 +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 . - -Builds are supported with cmake. Builds with Autotools are supported -for compatibility but are deprecated. - -Building with Cmake -------------------- - -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 -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 - -Building with cmake requires bison, flex and swig. - -These packages are optional: - -libz 1.1.4 1.2.5 1.2.8 -cudd 2.4.1 3.0.0 - -CUDD is a BDD package that is used to improve conditional timing arc -handling. It is available here: - - https://www.davidkebo.com/source/cudd_versions/cudd-3.0.0.tar.gz - https://sourceforge.net/projects/cudd-mirror/ - -Note that the file hierarchy of the CUDD installation changed with -version 3.0. This build only supports the 3.0 layout but only small -changes to configure.ac are required to support older versions. - - cd $HOME/cudd-3.0.0 - mkdir $HOME/cudd - ./configure --prefix $HOME/cudd - make - make install - -And then pass the install directory to cmake before building OpenSTA. - - cmake .. -DCUDD=$HOME/cudd - -The Zlib library is an optional. If the configure script finds libz, -OpenSTA can read Verilog, SDF, SPF, and SPEF files compressed with -gzip. - -Use the following commands to checkout the git repository build the -OpenSTA library and excutable. - - git clone https://xp-dev.com/git/opensta - cd opensta - mkdir build - cd build - cmake .. - make - -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 - ZLIB_ROOT - path to zlib - CMAKE_INSTALL_PREFIX - -If TCL_LIB is specified the cmake script will attempt to locate the -header from its path. - -The default install directory is /usr/local. -To install in a different directory with cmake use: - - cmake .. -DCMAKE_INSTALL_PREFIX= - -or 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. - -Building from a tarfile ------------------------ - -Use the following commands to unpack the dist file and compile it. - - tar zvfz opensta-.tgz - cd opensta- - ./configure [options...] - make - -With no options, configure builds an optimized executable. -The resulting executable is app/sta. - -configure options: - -h, --help display configure help and exit - --enable-debug enable debug - --enable-asan enable AddressSanitizer - --enable-gprof enable gprof profiling - --enable-gcov enable gcov profiling - --enable-32bit force 32 bit compile - --with-include=dirs directories to search for include files - --with-lib=dirs directories to search for libraries - --with-tcl=dirs directories to search for Tcl init files - --with-cudd=path use Cudd BDD package, defaults to $CUDD - --with-visualstudio use Microcruft Visual Studio C++ compiler - -If the configure script fails to find any of the TCL, Zlib or CUDD -files, use the --with-include, --with-lib, --with-tcl, --with-cudd -options to add directories to search for the files. - -The configure -help option lists the generic configure options that -are not described above. The default arguments to configure disable -shared libraries. To build with shared libraries use the ---enable-shared option. - -Building with GNU Autotools ---------------------------- - -Building with GNU Autotools the additional build dependencies -shown below. - - from Ubuntu Xcode - 18.04.1 10.1 -autoconf 2.53 2.69 2.69 -automake 1.6.3 1.15.1 1.16.1 -libtool 1.4.2 2.4.6 2.4.6 - -Use the following commands to checkout the git repository and compile -it. - - git clone https://xp-dev.com/git/opensta - git checkout master|branch - ./bootstrap - ./configure [options...] - make - -Building on Windoz ------------------- - -The Win32 API does not natively support the pthreads API. The -pthreads-win32 package is one way to get support for pthreads for 32 -bit builds. It is available from www.sourceware.org/pthreads-win32. -If the configure script does not find pthreads.h the build proceeds -without thread support. - -Use a .bat file to start a cygwin shell that has its path set to -support the Microcruft cl compiler by calling the vsvars32.bat script -from the Visual C++ installation. - -tcsh-startup.bat - @echo off - call "c:\Microsoft Visual Studio 9.0\Common7\Tools\vsvars.bat" - set path=c:\cygwin\bin;%PATH% - c:\cygwin\bin\tcsh - -cmake is supposedly more compatible with the windoz environment -so you may have better luck wih it. - -Configure and build from the shell. Note that tcl and zlib must be -built with the Visual C++ compiler to link to the sta libraries. - -./bootstrap -./configure --with-visualstudio -# Rebuild flex/bison files because include files are different. -make maintainer-clean - -Good luck and don't bother me with windoz specific issues. -I am happy to say I haven't owned a windoz machine in 20 years. - - -Submitting Bug Reports ----------------------- -All bug reports should attach a testcase, preferably in the following -form: - - A gzip'd tar file that unpacks into a directory with the same name as the tar file - A file named README that describes the problem - All commands in one .tcl file (usually run.tcl) for small cases - No calls to "exit" - No shell scripts to envoke the sta. +See README.MD for installation instructions. diff --git a/README b/README index 971347e7..9b7ba4c2 100644 --- a/README +++ b/README @@ -1,69 +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 . - -Parallax Gate Level Static Timing Analyzer - -## Install - -See INSTALL for installation and build instructions. Alternatively, run using Docker as described in the next section - -## Run using Docker -1. 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/). -2. Navigate to the directory where you have the input files. -3. 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 `-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. - -## Standard file formats - -* Verilog -* Liberty -* SDC -* SDF -* SPEF - -## Exception path support -* False path -* Multicycle path -* Min/Max 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 - -## Clocks -* Generated -* Latency -* Source latency (insertion delay) -* Uncertainty -* Propagated/Ideal -* Gated clock checks -* Multiple frequency clocks - -## 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 - -## Search Engine -* Query based incremental update of delays, arrival and required times -* Simulator to propagate constants from constraints and netlist tie high/low - -## Timing engine library -* Network adapter uses external netlist database without duplicating any data +See README.MD diff --git a/README.md b/README.md index d4dc8361..ff10b618 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,19 @@ -# OpenSTA, Static Timing Analyzer -Copyright (c) 2019, Parallax Software, Inc. +# Parallax Static Timing Analyzer -> This program is free software: you can redistribute it and/or modify -> it under the terms of the GNU General Public License as published by -> the Free Software Foundation, either version 3 of the License, or -> (at your option) any later version. +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. -> This program is distributed in the hope that it will be useful, -> but WITHOUT ANY WARRANTY; without even the implied warranty of -> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -> GNU General Public License for more details. +* Verilog netlist +* Liberty library +* SDC timing constraints +* SDF delay annotation +* SPEF parasitics -> You should have received a copy of the GNU General Public License -> along with this program. If not, see . +OpenSTA uses a TCL command interpreter to read the design, specify +timing constraints and print timing reports. -Parallax Gate Level Static Timing Analyzer - -## Install - -See INSTALL for installation and build instructions. Alternatively, run using Docker as described in the next section - -## Run using Docker -1. 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/). -2. Navigate to the directory where you have the input files. -3. 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 `-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. - -## Standard file formats - -* Verilog -* Liberty -* SDC -* SDF -* RSPF/DSPF/SPEF - -## Exception path support -* False path -* Multicycle path -* Min/Max 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 - -## Clocks +##### Clocks * Generated * Latency * Source latency (insertion delay) @@ -52,18 +22,247 @@ See INSTALL for installation and build instructions. Alternatively, run using Do * Gated clock checks * Multiple frequency clocks -## Delay calculation +##### 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 +##### Analysis * Report timing checks -from, -through, -to, multiple paths to endpoint * Report delay calculation * Check timing setup -## Search Engine +##### 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 -## Timing engine library -* Network adapter uses external netlist database without duplicating any data +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 or Autotools. + +### 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. 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 the CMake or configure scripts are required to support older +versions. + +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 +``` + +The Zlib library is an optional. If CMake or the configure script +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://xp-dev.com/git/opensta +cd opensta +mkdir build +cd build +cmake .. -DCUDD=$HOME/cudd +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 ($HOME/cudd if following install shown above) +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. + +### Installing from a tarfile + +Installing from a tarfile has the advantage that Autotools, bison, +flex and swig do not need to be installed. + +Use the following commands to unpack the dist file and compile it. +``` +tar zvfz opensta-.tgz +cd opensta- +./configure [options...] +make +``` +With no options, configure builds an optimized executable. +The resulting executable is app/sta. +``` +configure options: + -h, --help display configure help and exit + --enable-debug enable debug + --enable-asan enable AddressSanitizer + --enable-gprof enable gprof profiling + --enable-gcov enable gcov profiling + --enable-32bit force 32 bit compile + --with-include=dirs directories to search for include files + --with-lib=dirs directories to search for libraries + --with-tcl=dirs directories to search for Tcl init files + --with-cudd=path use Cudd BDD package, defaults to $CUDD + --with-visualstudio use Microcruft Visual Studio C++ compiler +``` +If the configure script fails to find any of the `TCL`, `Zlib` or `CUDD` +files, use the `--with-include`, `--with-lib`, `--with-tcl`, `--with-cudd` +options to add directories to search for the files. + +The configure `--help` option lists the generic configure options that +are not described above. The default arguments to configure disable +shared libraries. To build with shared libraries use the +`--enable-shared` option. + +### Installing with Autotools + +Building with GNU Autotools the additional build dependencies shown +below. +``` + from Ubuntu Xcode + 18.04.1 10.1 +autoconf 2.53 2.69 2.69 +automake 1.6.3 1.15.1 1.16.1 +libtool 1.4.2 2.4.6 2.4.6 +``` +Use the following commands to checkout the git repository and compile +it. +``` +git clone https://xp-dev.com/git/opensta +git checkout master|branch +./bootstrap +./configure [options...] +make +``` +Configure options are show above in the "Installing from tarkit" section. + +### Installing on Windoz + +The Win32 API does not natively support the pthreads API. The +pthreads-win32 package is one way to get support for pthreads for 32 +bit builds. It is available from [pthreads](www.sourceware.org/pthreads-win32). +If the configure script does not find `pthreads.h` the build proceeds +without thread support. + +Use a .bat file to start a cygwin shell that has its path set to +support the Microcruft cl compiler by calling the vsvars32.bat script +from the Visual C++ installation. +``` +tcsh-startup.bat + @echo off + call "c:\Microsoft Visual Studio 9.0\Common7\Tools\vsvars.bat" + set path=c:\cygwin\bin;%PATH% + c:\cygwin\bin\tcsh +``` +CMake is supposedly more compatible with the windoz environment +so you may have better luck wih it. + +Configure and build from the shell. Note that tcl and zlib must be +built with the Visual C++ compiler to link to the sta libraries. +... +./bootstrap +./configure --with-visualstudio +make maintainer-clean +... +Good luck and don't bother me with windoz specific issues. +I am happy to say I haven't owned a windoz machine in 20 years. + +## 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. + +## License + +Copyright (c) 2019, Parallax Software, Inc. +All rights reserved. + +No part of this document may be copied, transmitted or +disclosed in any form or fashion without the express +written consent of Parallax Software, Inc. diff --git a/liberty/FuncExpr.hh b/liberty/FuncExpr.hh index 5514b4a1..15659056 100644 --- a/liberty/FuncExpr.hh +++ b/liberty/FuncExpr.hh @@ -53,10 +53,12 @@ public: // 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_; } + // NULL when op == op_not FuncExpr *right() const { return right_; } TimingSense portTimingSense(const LibertyPort *port) const; // Return true if expression has port as an input. diff --git a/search/WritePathSpice.cc b/search/WritePathSpice.cc index 20d3f2c8..7548e68c 100644 --- a/search/WritePathSpice.cc +++ b/search/WritePathSpice.cc @@ -25,6 +25,7 @@ #include "StringUtil.hh" #include "FuncExpr.hh" #include "Units.hh" +#include "Sequential.hh" #include "Liberty.hh" #include "TimingArc.hh" #include "Network.hh" @@ -83,17 +84,18 @@ 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 writeStageVoltageSources(LibertyCell *cell, - StringVector *spice_port_names, - const Instance *inst, - const char *inst_name, - LibertyPort *from_port, - LibertyPort *drvr_port); + void writeStageVoltageSources(Stage stage, + StringVector *spice_port_names); void writeStageParasitics(Stage stage); void writeSubckts(); void findPathCellnames(// Return values. @@ -114,9 +116,36 @@ private: Path *path); void sensitizationValues(const Instance *inst, FuncExpr *expr, - LibertyPort *from_port, + LibertyPort *input_port, // Return values. LibertyPortLogicValues &port_values); + void seqSensitizationValues(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); + float findSlew(Vertex *vertex, + const TransRiseFall *tr, + DcalcAPIndex dcalc_ap_index); + LibertyPort *onePort(FuncExpr *expr); + void writeVoltageSource(LibertyCell *cell, + const char *inst_name, + const char *subckt_port_name, + const char *pg_port_name, + int &volt_index); // Stage "accessors". // @@ -144,7 +173,7 @@ private: TimingArc *stageWireArc(Stage stage); Edge *stageGateEdge(Stage stage); Edge *stageWireEdge(Stage stage); - Pin *stageInputPin(Stage stage); + Pin *stageGateInputPin(Stage stage); Pin *stageDrvrPin(Stage stage); Pin *stageLoadPin(Stage stage); const char *stageGateInputPinName(Stage stage); @@ -167,8 +196,11 @@ private: 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_; }; //////////////////////////////////////////////////////////////// @@ -235,11 +267,12 @@ WritePathSpice::WritePathSpice(Path *path, gnd_name_(gnd_name), path_expanded_(sta), net_name_(NULL), - short_ckt_resistance_(.0001) + default_library_(network_->defaultLibertyLibrary()), + short_ckt_resistance_(.0001), + clk_cycle_count_(3) { - auto lib = network_->defaultLibertyLibrary(); - power_voltage_ = lib->supplyVoltage(power_name_); - gnd_voltage_ = lib->supplyVoltage(gnd_name_); + power_voltage_ = default_library_->supplyVoltage(power_name_); + gnd_voltage_ = default_library_->supplyVoltage(gnd_name_); } WritePathSpice::~WritePathSpice() @@ -274,7 +307,7 @@ WritePathSpice::writeHeader() auto min_max = path_->minMax(this); auto pvt = sdc_->operatingConditions(min_max); if (pvt == NULL) - pvt = network_->defaultLibertyLibrary()->defaultOperatingConditions(); + pvt = default_library_->defaultOperatingConditions(); auto temp = pvt->temperature(); streamPrint(spice_stream_, ".temp %.1f\n", temp); streamPrint(spice_stream_, ".include \"%s\"\n", model_filename_); @@ -291,12 +324,21 @@ WritePathSpice::maxTime() { auto input_stage = stageFirst(); auto input_path = stageDrvrPath(input_stage); - auto input_slew = input_path->slew(this); - auto end_slew = path_->slew(this); - auto max_time = delayAsFloat(input_slew - + path_->arrival(this) - + end_slew * 2) * 1.5; - return max_time; + auto input_slew = findSlew(input_path); + 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 @@ -351,11 +393,36 @@ WritePathSpice::writeInputSource() streamPrint(spice_stream_, "**************\n\n"); auto input_stage = stageFirst(); - streamPrint(spice_stream_, "v1 %s 0 pwl(\n", - stageDrvrPinName(input_stage)); - auto wire_arc = stageWireArc(input_stage); + 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 slew0 = findSlew(input_path, tr); + // Arbitrary offset. + auto time0 = slew0; + int volt_index = 1; + writeStepVoltSource(stageDrvrPin(input_stage), 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 (wire_arc->fromTrans()->asRiseFall() == TransRiseFall::rise()) { + if (tr == TransRiseFall::rise()) { volt0 = gnd_voltage_; volt1 = power_voltage_; } @@ -363,20 +430,114 @@ WritePathSpice::writeInputSource() volt0 = power_voltage_; volt1 = gnd_voltage_; } - auto input_path = stageDrvrPath(input_stage); - auto input_slew = delayAsFloat(input_path->slew(this)); - if (input_slew == 0.0) - input_slew = maxTime() / 1e+3; - // Arbitrary offset. - auto time0 = input_slew; - auto time1 = time0 + input_slew; + 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 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); + auto slew1 = findSlew(input_path, tr1); + 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, dcalc_ap_index); +} + +float +WritePathSpice::findSlew(Path *path, + const TransRiseFall *tr) +{ + auto vertex = path->vertex(this); + auto dcalc_ap_index = path->dcalcAnalysisPt(this)->index(); + return findSlew(vertex, tr, dcalc_ap_index); +} + +float +WritePathSpice::findSlew(Vertex *vertex, + const TransRiseFall *tr, + DcalcAPIndex dcalc_ap_index) +{ + auto slew = delayAsFloat(graph_->slew(vertex, tr, dcalc_ap_index)); + if (slew == 0.0) + slew = units_->timeUnit()->scale(); + return slew; +} + +// 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); - streamPrint(spice_stream_, "+%.3e %.3e\n", maxTime(), volt1); - streamPrint(spice_stream_, "+)\n\n"); } +//////////////////////////////////////////////////////////////// + void WritePathSpice::writeMeasureStmts() { @@ -407,14 +568,13 @@ WritePathSpice::writeMeasureDelayStmt(Stage stage, Path *from_path, Path *to_path) { - auto lib = network_->defaultLibertyLibrary(); auto from_pin_name = network_->pathName(from_path->pin(this)); auto from_tr = from_path->transition(this); - auto from_threshold = power_voltage_ * lib->inputThreshold(from_tr); + 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_ * lib->inputThreshold(to_tr); + auto to_threshold = power_voltage_ * default_library_->inputThreshold(to_tr); streamPrint(spice_stream_, ".measure tran %s_%s_delay_%s\n", stageName(stage).c_str(), @@ -436,12 +596,11 @@ void WritePathSpice::writeMeasureSlewStmt(Stage stage, Path *path) { - auto lib = network_->defaultLibertyLibrary(); auto pin_name = network_->pathName(path->pin(this)); auto tr = path->transition(this); auto spice_tr = spiceTrans(tr); - auto lower = power_voltage_ * lib->slewLowerThreshold(tr); - auto upper = power_voltage_ * lib->slewUpperThreshold(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; @@ -511,9 +670,8 @@ WritePathSpice::writeInputStage(Stage stage) void WritePathSpice::writeGateStage(Stage stage) { - auto input_pin = stageInputPin(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_name = stageLoadPinName(stage); streamPrint(spice_stream_, ".subckt stage%d %s %s %s\n", @@ -544,29 +702,54 @@ WritePathSpice::writeGateStage(Stage stage) } streamPrint(spice_stream_, " %s\n", cell_name); - writeStageVoltageSources(cell, spice_port_names, - inst, inst_name, - network_->libertyPort(input_pin), - network_->libertyPort(drvr_pin)); + writeStageVoltageSources(stage, spice_port_names); writeStageParasitics(stage); streamPrint(spice_stream_, ".ends\n\n"); } // Power/ground and input voltage sources. void -WritePathSpice::writeStageVoltageSources(LibertyCell *cell, - StringVector *spice_port_names, - const Instance *inst, - const char *inst_name, - LibertyPort *from_port, - LibertyPort *drvr_port) +WritePathSpice::writeStageVoltageSources(Stage stage, + StringVector *spice_port_names) { - auto from_port_name = from_port->name(); + auto input_pin = stageGateInputPin(stage); + 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 lib = cell->libertyLibrary(); + auto inst = network_->instance(input_pin); + auto inst_name = network_->pathName(inst); + auto cell = network_->libertyCell(inst); + auto gate_edge = stageGateEdge(stage); + LibertyPortLogicValues port_values; - sensitizationValues(inst, drvr_port->function(), from_port, port_values); - int volt_source = 1; + const Clock *clk = NULL; + DcalcAPIndex dcalc_ap_index = 0; + if (gate_edge->role()->genericRole() == TimingRole::regClkToQ()) { + 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 seq = cell->outputPortSequential(q_port); + if (seq) { + auto drvr_path = stageDrvrPath(stage); + auto drvr_tr = drvr_path->transition(this); + seqSensitizationValues(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", + input_port_name, drvr_port_name); + } + } + } + else + sensitizationValues(inst, drvr_port->function(), input_port, port_values); + int volt_index = 1; debugPrint1(debug_, "write_spice", 2, "subckt %s\n", cell->name()); StringVector::Iterator port_iter(spice_port_names); while (port_iter.hasNext()) { @@ -578,16 +761,15 @@ WritePathSpice::writeStageVoltageSources(LibertyCell *cell, if (pg_port) { auto voltage = pgPortVoltage(pg_port); streamPrint(spice_stream_, "v%d %s/%s 0 %.3f\n", - volt_source, + volt_index, inst_name, subckt_port_name, voltage); - volt_source++; - } else if (!(stringEq(subckt_port_name, from_port_name) + 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) { - const char *pg_port_name = NULL; const Pin *pin = network_->findPin(inst, port); // Look for tie high/low or propagated constant values. LogicValue port_value = sim_->logicValue(pin); @@ -598,43 +780,81 @@ WritePathSpice::writeStageVoltageSources(LibertyCell *cell, if (has_value) port_value = value; } - if (port_value == logic_zero) - pg_port_name = port->relatedGroundPin(); - else if (port_value == logic_one) - pg_port_name = port->relatedPowerPin(); - 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); + switch (port_value) { + case logic_zero: + writeVoltageSource(cell, inst_name, subckt_port_name, + port->relatedGroundPin(), + volt_index); + break; + case logic_one: + writeVoltageSource(cell, inst_name, subckt_port_name, + port->relatedPowerPin(), + volt_index); + break; + case logic_rise: + writeClkedStepSource(pin, TransRiseFall::rise(), clk, + dcalc_ap_index, volt_index); + break; + case logic_fall: + writeClkedStepSource(pin, TransRiseFall::fall(), clk, + dcalc_ap_index, volt_index); + break; + case logic_unknown: + break; } } } } } -// Find the logic values for expression inputs to enable paths from_port. +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, dcalc_ap_index); + auto time = clkWaveformTImeOffset(clk) + clk->period() / 2.0; + writeStepVoltSource(pin, tr, slew, time, volt_index); +} + +void +WritePathSpice::writeVoltageSource(LibertyCell *cell, + const char *inst_name, + const char *subckt_port_name, + const char *pg_port_name, + int &volt_index) +{ + auto pg_port = cell->findPgPort(pg_port_name); + if (pg_port) { + auto voltage_name = pg_port->voltageName(); + if (voltage_name) { + float voltage = cell->libertyLibrary()->supplyVoltage(voltage_name); + streamPrint(spice_stream_, "v%d %s/%s 0 %.3f\n", + volt_index, + inst_name, subckt_port_name, + voltage); + volt_index++; + } + 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); +} + +// Find the logic values for expression inputs to enable paths input_port. void WritePathSpice::sensitizationValues(const Instance *inst, FuncExpr *expr, - LibertyPort *from_port, + LibertyPort *input_port, // Return values. LibertyPortLogicValues &port_values) { @@ -644,63 +864,63 @@ WritePathSpice::sensitizationValues(const Instance *inst, case FuncExpr::op_port: break; case FuncExpr::op_not: - sensitizationValues(inst, expr->left(), from_port, port_values); + sensitizationValues(inst, left, input_port, port_values); break; case FuncExpr::op_or: - if (left->hasPort(from_port) + if (left->hasPort(input_port) && right->op() == FuncExpr::op_port) port_values[right->port()] = logic_zero; - else if (left->hasPort(from_port) + else if (left->hasPort(input_port) && right->op() == FuncExpr::op_not && right->left()->op() == FuncExpr::op_port) - // from_port + !right_port + // input_port + !right_port port_values[right->left()->port()] = logic_one; - else if (right->hasPort(from_port) + else if (right->hasPort(input_port) && left->op() == FuncExpr::op_port) port_values[left->port()] = logic_zero; - else if (right->hasPort(from_port) + else if (right->hasPort(input_port) && left->op() == FuncExpr::op_not && left->left()->op() == FuncExpr::op_port) - // from_port + !left_port + // input_port + !left_port port_values[left->left()->port()] = logic_one; else { - sensitizationValues(inst, expr->left(), from_port, port_values); - sensitizationValues(inst, expr->right(), from_port, port_values); + sensitizationValues(inst, left, input_port, port_values); + sensitizationValues(inst, right, input_port, port_values); } break; case FuncExpr::op_and: - if (left->hasPort(from_port) + if (left->hasPort(input_port) && right->op() == FuncExpr::op_port) port_values[right->port()] = logic_one; - else if (left->hasPort(from_port) + else if (left->hasPort(input_port) && right->op() == FuncExpr::op_not && right->left()->op() == FuncExpr::op_port) - // from_port * !right_port + // input_port * !right_port port_values[right->left()->port()] = logic_zero; - else if (right->hasPort(from_port) + else if (right->hasPort(input_port) && left->op() == FuncExpr::op_port) port_values[left->port()] = logic_one; - else if (right->hasPort(from_port) + else if (right->hasPort(input_port) && left->op() == FuncExpr::op_not && left->left()->op() == FuncExpr::op_port) - // from_port * !left_port + // input_port * !left_port port_values[left->left()->port()] = logic_zero; else { - sensitizationValues(inst, expr->left(), from_port, port_values); - sensitizationValues(inst, expr->right(), from_port, port_values); + sensitizationValues(inst, left, input_port, port_values); + sensitizationValues(inst, right, input_port, port_values); } break; case FuncExpr::op_xor: // Need to know timing arc sense to get this right. - if (left->port() == from_port + if (left->port() == input_port && right->op() == FuncExpr::op_port) port_values[right->port()] = logic_zero; - else if (right->port() == from_port + else if (right->port() == input_port && left->op() == FuncExpr::op_port) port_values[left->port()] = logic_zero; else { - sensitizationValues(inst, expr->left(), from_port, port_values); - sensitizationValues(inst, expr->right(), from_port, port_values); + sensitizationValues(inst, left, input_port, port_values); + sensitizationValues(inst, right, input_port, port_values); } break; case FuncExpr::op_one: @@ -709,6 +929,63 @@ WritePathSpice::sensitizationValues(const Instance *inst, } } +void +WritePathSpice::seqSensitizationValues(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 timing_sense_positive_unate: + if (tr == TransRiseFall::rise()) + port_values[port] = logic_rise; + else + port_values[port] = logic_fall; + break; + case timing_sense_negative_unate: + if (tr == TransRiseFall::rise()) + port_values[port] = logic_fall; + else + port_values[port] = logic_rise; + break; + case timing_sense_non_unate: + case timing_sense_none: + case timing_sense_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 == NULL) + port = onePort(right); + return port; + case FuncExpr::op_one: + case FuncExpr::op_zero: + return NULL; + } +} + class ParasiticNodeNameLess { public: @@ -1033,7 +1310,7 @@ WritePathSpice::stageWireArc(Stage stage) Edge * WritePathSpice::stageGateEdge(Stage stage) { - auto path = stageGateInputPath(stage); + auto path = stageDrvrPath(stage); auto arc = stageGateArc(stage); return path->prevEdge(arc, this); } @@ -1047,7 +1324,7 @@ WritePathSpice::stageWireEdge(Stage stage) } Pin * -WritePathSpice::stageInputPin(Stage stage) +WritePathSpice::stageGateInputPin(Stage stage) { auto path = stageGateInputPath(stage); return path->pin(this); @@ -1070,7 +1347,7 @@ WritePathSpice::stageLoadPin(Stage stage) const char * WritePathSpice::stageGateInputPinName(Stage stage) { - auto pin = stageInputPin(stage); + auto pin = stageGateInputPin(stage); return network_->pathName(pin); }