write_path_spice register path support

This commit is contained in:
James Cherry 2019-01-22 20:41:32 -08:00
parent b700eca86b
commit f2a28bdcaf
6 changed files with 657 additions and 436 deletions

View File

@ -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 <https://www.gnu.org/licenses/>.
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}
)

205
INSTALL
View File

@ -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 <https://www.gnu.org/licenses/>.
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<var>=<value> 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=<prefix_path>
or use the DESTDIR variable with make.
make DESTDIR=<prefix_path> 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-<version>.tgz
cd opensta-<version>
./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.

70
README
View File

@ -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 <https://www.gnu.org/licenses/>.
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

293
README.md
View File

@ -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 <https://www.gnu.org/licenses/>.
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<var>=<value> 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=<prefix_path>
```
Alternatively, you can use the `DESTDIR` variable with make.
```
make DESTDIR=<prefix_path> 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-<version>.tgz
cd opensta-<version>
./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.

View File

@ -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.

View File

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