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);
}