diff --git a/.gitignore b/.gitignore index 37210dd6..8730b7a1 100644 --- a/.gitignore +++ b/.gitignore @@ -18,12 +18,16 @@ Makefile.in .libs .deps -# / /build /configure /m4 /compile -/config.* +/config.h +/config.guess +/config.h.in +/config.log +/config.sub +/config.status /libtool /ltmain.sh /install-sh @@ -50,9 +54,11 @@ Makefile.in # /liberty/ /liberty/LibertyExprLex.cc +/liberty/LibertyExprLex.hh /liberty/LibertyExprParse.cc /liberty/LibertyExprParse.h /liberty/LibertyLex.cc +/liberty/LibertyLex.hh /liberty/LibertyParse.cc /liberty/LibertyParse.h /liberty/LibertyExprParse.hh @@ -60,18 +66,18 @@ Makefile.in # /parasitics/ /parasitics/SpefLex.cc +/parasitics/SpefLex.hh /parasitics/SpefParse.cc -/parasitics/SpefParse.h -/parasitics/SpfLex.cc -/parasitics/SpfParse.cc -/parasitics/SpfParse.h /parasitics/SpefParse.hh +/parasitics/SpfLex.cc +/parasitics/SpfLex.hh +/parasitics/SpfParse.cc /parasitics/SpfParse.hh # /sdf/ /sdf/SdfLex.cc +/sdf/SdfLex.hh /sdf/SdfParse.cc -/sdf/SdfParse.h /sdf/SdfParse.hh # /tcl/ @@ -85,6 +91,7 @@ Makefile.in /test_native # /verilog/ +/verilog/VerilogLex.hh /verilog/VerilogLex.cc /verilog/VerilogParse.cc /verilog/VerilogParse.h diff --git a/INSTALL b/INSTALL index 239eea33..d41da109 100644 --- a/INSTALL +++ b/INSTALL @@ -148,3 +148,36 @@ form: All commands in one .tcl file (usually run.tcl) for small cases No calls to "exit" No shell scripts to envoke the sta. + +---------------------------------------------------------------- +Building with Cmake + + git clone https://xp-dev.com/git/opensta + cd opensta + mkdir build + cd build + cmake ../ + make + +The resulting executable is app/sta for compatibility with configure. +The library without main is app/libSTA.a. + +Optional cmake variables passed as -D= arguments to cmake. + + MAKE_BUILD_TYPE DEBUG|RELEASE + TCL_LIB - path to tcl library + TCL_HEADER - path to tcl.h + TCL_INIT - path to init.tcl + CMAKE_INSTALL_PREFIX + +If TCL_LIB is specified the cmake script will attempt to locate +the header and init files from its path. + +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 diff --git a/app/Makefile.am b/app/Makefile.am index d63620f4..fd7b30a1 100644 --- a/app/Makefile.am +++ b/app/Makefile.am @@ -42,7 +42,7 @@ StaApp_wrap.cc: $(SWIG_DEPEND) StaApp.i ../verilog/Verilog.i # so that they do not have to be installed on the client host. TclInitVar.cc: ../etc/TclEncode.tcl $(TCL_INIT_FILES) - ../etc/TclEncode.tcl TclInitVar.cc "tcl_inits" $(TCL_INIT_DIR) \ + ../etc/TclEncode.tcl TclInitVar.cc "tcl_inits" \ $(TCL_INIT_FILES) ../verilog/Verilog.tcl EXTRA_DIST = \ diff --git a/configure.ac b/configure.ac index d42cb79f..3b405290 100644 --- a/configure.ac +++ b/configure.ac @@ -50,7 +50,6 @@ case "${HOST_OS}" in INCLUDE_DIRS="/usr/include" LIB_DIRS="/usr/lib /usr/local/lib /usr/lib/i386-linux-gnu" LIB_EXTS="a so .so.1" - TCL_INIT_DIRS="/usr/lib/tcl /usr/lib /usr/share/tcltk" ;; x86_64) @@ -58,7 +57,6 @@ case "${HOST_OS}" in INCLUDE_DIRS="/usr/include" LIB_DIRS="/usr/lib64 /usr/lib /usr/lib/x86_64-linux-gnu" LIB_EXTS="a so" - TCL_INIT_DIRS="/usr/lib64/tcl /usr/lib /usr/share/tcltk /usr/share" ;; *) @@ -66,7 +64,6 @@ case "${HOST_OS}" in INCLUDE_DIRS="/usr/include" LIB_DIRS="/usr/lib" LIB_EXTS="a so" - TCL_INIT_DIRS="/usr/lib/tcl /usr/lib" ;; esac ;; @@ -76,7 +73,6 @@ case "${HOST_OS}" in INCLUDE_DIRS="/usr/include" LIB_DIRS="/usr/lib" LIB_EXTS="a so" - TCL_INIT_DIRS="/usr/share" ;; Darwin) @@ -88,14 +84,12 @@ case "${HOST_OS}" in CC=clang++ INCLUDE_DIRS="/usr/local/include /usr/include" LIB_DIRS="/usr/local/lib /usr/lib" - TCL_INIT_DIRS="/usr/local/lib/tcl8.6" else # gcc CXX=g++ CC=g++ INCLUDE_DIRS="/usr/local/opt/tcl-tk/include /usr/local/opt/zlib/include /usr/include" LIB_DIRS="/usr/local/opt/tcl-tk/lib /usr/local/opt/zlib/lib /usr/local/lib" - TCL_INIT_DIRS="/usr/local/opt/tcl-tk/lib/tcl8.6" fi LIB_EXTS="dylib" ;; @@ -105,7 +99,6 @@ case "${HOST_OS}" in INCLUDE_DIRS="/usr/include /usr/local/include" LIB_DIRS="/usr/lib /usr/local/lib" LIB_EXTS="a so" - TCL_INIT_DIRS="/usr/lib/tcl /usr/share /usr/share/tcl" ;; esac @@ -167,11 +160,6 @@ AC_ARG_WITH(lib, [LIB_DIRS="${withval}"], []) -AC_ARG_WITH(tcl, - [AS_HELP_STRING([--with-tcl=dirs],[directories to search for Tcl init files])], - [TCL_INIT_DIRS="${withval}"], - []) - AC_ARG_WITH(cudd, [AS_HELP_STRING([--with-cudd=path],[use CUDD BDD package, defaults to $CUDD])], [CUDD_ARG="${withval}"], @@ -274,39 +262,13 @@ for tcl_minor in $tcl_minors; do AC_MSG_RESULT(not found) fi - AC_MSG_CHECKING(for Tcl $tcl_major.$tcl_minor init files) - TCL_INIT_DIR="" - for dir in $TCL_INIT_DIRS ; do - init_dir=$dir - init_path="$dir/init.tcl" - if test -r $init_path; then - AC_MSG_RESULT($init_path) - TCL_INIT_DIR="$init_dir" - break - fi - - init_dir="$dir/tcl$tcl_major.$tcl_minor" - init_path="$init_dir/init.tcl" - if test -r $init_path; then - AC_MSG_RESULT($init_path) - TCL_INIT_DIR="$init_dir" - break - fi - done - if test -z "$TCL_INIT_DIR"; then - AC_MSG_RESULT(not found) - fi - if test $found_tcl_header = true && - test -n "$TCL_LIB" && - test -n "$TCL_INIT_DIR"; then + test -n "$TCL_LIB"; then break fi done -AC_SUBST(TCL_INIT_DIR, $TCL_INIT_DIR) - #-------------------------------------------------------------------- # Locate the Zlib package #-------------------------------------------------------------------- diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index bc0eec26..411c8025 100644 Binary files a/doc/OpenSTA.odt and b/doc/OpenSTA.odt differ diff --git a/etc/TclEncode.tcl b/etc/TclEncode.tcl index 67a9612a..eaca7b8d 100755 --- a/etc/TclEncode.tcl +++ b/etc/TclEncode.tcl @@ -27,8 +27,7 @@ exec tclsh $0 ${1+"$@"} set encoded_filename [lindex $argv 0] set init_var [lindex $argv 1] -set tcl_init_dir [lindex $argv 2] -set init_filenames [lrange $argv 3 end] +set init_filenames [lrange $argv 2 end] set mail_log 0 if [info exists env(STA_MAIL_LOG)] { @@ -78,12 +77,6 @@ proc encode_file { filename } { close $in_stream } -# init.tcl requires tcl_library to be bound for autoloading other files. -# Allow the environment variable TCL_INIT_DIR to override the TCL init -# file directory found by configure. -encode_line "if \[info exists env(TCL_INIT_DIR)] { set tcl_library \$env(TCL_INIT_DIR) } else { set tcl_library \"$tcl_init_dir\" }" -encode_line {source [file join $tcl_library init.tcl]} - foreach filename $init_filenames { encode_file $filename } diff --git a/graph/DelayNormal2.hh b/graph/DelayNormal2.hh index acb8b0a2..0752aa39 100644 --- a/graph/DelayNormal2.hh +++ b/graph/DelayNormal2.hh @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#ifndef STA_DELAY_FLOAT_CLASS_H -#define STA_DELAY_FLOAT_CLASS_H +#ifndef STA_DELAY_NORMAL2_H +#define STA_DELAY_NORMAL2_H #include "MinMax.hh" diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 3afe7c5d..55915c70 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -2714,10 +2714,12 @@ OcvDerate::setDerateTable(const TransRiseFall *tr, //////////////////////////////////////////////////////////////// -LibertyPgPort::LibertyPgPort(const char *name) : +LibertyPgPort::LibertyPgPort(const char *name, + LibertyCell *cell) : name_(stringCopy(name)), pg_type_(unknown), - voltage_name_(NULL) + voltage_name_(NULL), + cell_(cell) { } diff --git a/liberty/Liberty.hh b/liberty/Liberty.hh index d705aa62..eae35871 100644 --- a/liberty/Liberty.hh +++ b/liberty/Liberty.hh @@ -1021,9 +1021,11 @@ public: internal_power, internal_ground, nwell, pwell, deepnwell, deeppwell}; - LibertyPgPort(const char *name); + LibertyPgPort(const char *name, + LibertyCell *cell); ~LibertyPgPort(); const char *name() { return name_; } + LibertyCell *cell() const { return cell_; } PgType pgType() const { return pg_type_; } void setPgType(PgType type); const char *voltageName() const { return voltage_name_; } @@ -1033,6 +1035,7 @@ private: const char *name_; PgType pg_type_; const char *voltage_name_; + LibertyCell *cell_; }; } // namespace diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index ab92fb6c..9a9408e2 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -289,7 +289,6 @@ LibertyReader::defineVisitors() &LibertyReader::endScaledCell); defineAttrVisitor("clock_gating_integrated_cell", &LibertyReader::visitClockGatingIntegratedCell); - defineAttrVisitor("area", &LibertyReader::visitArea); defineAttrVisitor("dont_use", &LibertyReader::visitDontUse); defineAttrVisitor("is_macro", &LibertyReader::visitIsMacro); @@ -4730,7 +4729,7 @@ LibertyReader::beginPgPin(LibertyGroup *group) { if (cell_) { const char *name = group->firstName(); - pg_port_ = new LibertyPgPort(name); + pg_port_ = new LibertyPgPort(name, cell_); cell_->addPgPort(pg_port_); } } diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index cbaa87e0..2e428844 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -1885,6 +1885,18 @@ ConcreteParasitics::value(const ParasiticDevice *device, return cdevice->value(); } +void +ConcreteParasitics::resistorNodes(const ParasiticDevice *device, + // Return values. + ParasiticNode *&node1, + ParasiticNode *&node2) const +{ + const ConcreteParasiticResistor *cdevice = + static_cast(device); + node1 = cdevice->node1(); + node2 = cdevice->node2(); +} + ParasiticNode * ConcreteParasitics::otherNode(const ParasiticDevice *device, ParasiticNode *node) const diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh index 8a1b9949..868999ed 100644 --- a/parasitics/ConcreteParasitics.hh +++ b/parasitics/ConcreteParasitics.hh @@ -175,6 +175,10 @@ public: virtual const char *name(const ParasiticDevice *device) const; virtual float value(const ParasiticDevice *device, const ParasiticAnalysisPt *ap) const; + virtual void resistorNodes(const ParasiticDevice *device, + // Return values. + ParasiticNode *&node1, + ParasiticNode *&node2) const; virtual ParasiticNode *otherNode(const ParasiticDevice *device, ParasiticNode *node) const; diff --git a/parasitics/ConcreteParasiticsPvt.hh b/parasitics/ConcreteParasiticsPvt.hh index f92a6c47..16fa5967 100644 --- a/parasitics/ConcreteParasiticsPvt.hh +++ b/parasitics/ConcreteParasiticsPvt.hh @@ -322,8 +322,8 @@ public: virtual ParasiticNode *otherNode(ParasiticNode *node) const; virtual void replaceNode(ConcreteParasiticNode *from_node, ConcreteParasiticNode *to_node); - ConcreteParasiticNode *node1() { return node_; } - ConcreteParasiticNode *node2() { return other_node_; } + ConcreteParasiticNode *node1() const { return node_; } + ConcreteParasiticNode *node2() const { return other_node_; } private: ConcreteParasiticNode *other_node_; diff --git a/parasitics/Parasitics.hh b/parasitics/Parasitics.hh index 95561550..b6362919 100644 --- a/parasitics/Parasitics.hh +++ b/parasitics/Parasitics.hh @@ -248,6 +248,10 @@ public: // Device "value" (resistance, capacitance). virtual float value(const ParasiticDevice *device, const ParasiticAnalysisPt *ap) const = 0; + virtual void resistorNodes(const ParasiticDevice *device, + // Return values. + ParasiticNode *&node1, + ParasiticNode *&node2) const = 0; virtual ParasiticNode *otherNode(const ParasiticDevice *device, ParasiticNode *node) const = 0; diff --git a/search/Makefile.am b/search/Makefile.am index 2ccddb94..6742ab81 100644 --- a/search/Makefile.am +++ b/search/Makefile.am @@ -56,7 +56,8 @@ include_HEADERS = \ VertexVisitor.hh \ VisitPathEnds.hh \ VisitPathGroupVertices.hh \ - WorstSlack.hh + WorstSlack.hh \ + WriteSpice.hh libsearch_la_SOURCES = \ Bfs.cc \ @@ -97,7 +98,8 @@ libsearch_la_SOURCES = \ VertexVisitor.cc \ VisitPathEnds.cc \ VisitPathGroupVertices.cc \ - WorstSlack.cc + WorstSlack.cc \ + WriteSpice.cc libs: $(lib_LTLIBRARIES) diff --git a/search/PathExpanded.cc b/search/PathExpanded.cc index 3be4cc17..6345634a 100644 --- a/search/PathExpanded.cc +++ b/search/PathExpanded.cc @@ -27,6 +27,11 @@ namespace sta { +PathExpanded::PathExpanded(const StaState *sta) : + sta_(sta) +{ +} + PathExpanded::PathExpanded(const Path *path, // Expand generated clk source paths. bool expand_genclks, diff --git a/search/PathExpanded.hh b/search/PathExpanded.hh index 6467ec69..b0982684 100644 --- a/search/PathExpanded.hh +++ b/search/PathExpanded.hh @@ -28,6 +28,7 @@ namespace sta { class PathExpanded { public: + PathExpanded(const StaState *sta); // Expand path for lookup by index. PathExpanded(const Path *path, const StaState *sta); @@ -35,6 +36,8 @@ public: // Expand generated clk source paths. bool expand_genclks, const StaState *sta); + void expand(const Path *path, + bool expand_genclks); size_t size() const { return paths_.size(); } // path(0) is the startpoint. // path(size()-1) is the endpoint. @@ -55,8 +58,6 @@ public: Edge *&d_q_edge); protected: - void expand(const Path *path, - bool expand_genclks); void expandGenclk(PathRef *clk_path); // Convert external index that starts at the path root // and increases to an index for paths_ (reversed). diff --git a/search/WriteSpice.cc b/search/WriteSpice.cc new file mode 100644 index 00000000..a3a9ea08 --- /dev/null +++ b/search/WriteSpice.cc @@ -0,0 +1,946 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2018, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include +#include +#include +#include "Machine.hh" +#include "Debug.hh" +#include "Error.hh" +#include "Report.hh" +#include "StringUtil.hh" +#include "FuncExpr.hh" +#include "Liberty.hh" +#include "TimingArc.hh" +#include "Network.hh" +#include "Graph.hh" +#include "Sdc.hh" +#include "DcalcAnalysisPt.hh" +#include "Parasitics.hh" +#include "PathAnalysisPt.hh" +#include "Path.hh" +#include "PathRef.hh" +#include "PathExpanded.hh" +#include "StaState.hh" +#include "WriteSpice.hh" + +namespace sta { + +using std::string; +using std::ofstream; +using std::ifstream; + +typedef Vector StringVector; +typedef Map CellSpicePortNames; +typedef int Stage; +typedef Map ParasiticNodeMap; + +void +split(const string &text, + const string &delims, + // Return values. + StringVector &tokens); +void +streamPrint(ofstream &stream, + const char *fmt, + ...) __attribute__((format (printf, 2, 3))); +void +stringPrint(string &str, + const char *fmt, + ...) __attribute__((format (printf, 2, 3))); + +//////////////////////////////////////////////////////////////// + +class WriteSpice : public StaState +{ +public: + WriteSpice(Path *path, + const char *spice_filename, + const char *subckts_filename, + const char *lib_subckts_filename, + const char *models_filename, + const StaState *sta); + ~WriteSpice(); + void writeSpice();; + +private: + void writeHeader(); + void writeStageInstances(); + void writeInputSource(); + void writeStageSubckts(); + void writeInputStage(Stage stage); + void writeMeasureStmts(); + void writeGateStage(Stage stage); + void writeStageVoltageSources(LibertyCell *cell, + StringVector *spice_port_names, + const char *inst_name, + LibertyPort *from_port, + LibertyPort *drvr_port); + void writeStageParasitics(Stage stage); + void writeSubckts(); + void findPathCellnames(// Return values. + StringSet &path_cell_names); + void recordSpicePortNames(const char *cell_name, + StringVector &tokens); + float pgPortVoltage(const char *pg_port_name, + LibertyCell *cell); + float pgPortVoltage(LibertyPgPort *pg_port); + float maxTime(); + const char *nodeName(ParasiticNode *node); + void initNodeMap(const char *net_name); + + // Stage "accessors". + // Internally a stage index from stageFirst() to stageLast() + // is turned into an index into path_expanded_. + Stage stageFirst(); + Stage stageLast(); + string stageName(Stage stage); + int stageGateInputPathIndex(Stage stage); + int stageDrvrPathIndex(Stage stage); + int stageLoadPathIndex(Stage stage); + PathRef *stageGateInputPath(Stage stage); + PathRef *stageDrvrPath(Stage stage); + PathRef *stageLoadPath(Stage stage); + TimingArc *stageGateArc(Stage stage); + TimingArc *stageWireArc(Stage stage); + Edge *stageGateEdge(Stage stage); + Edge *stageWireEdge(Stage stage); + Pin *stageInputPin(Stage stage); + Pin *stageDrvrPin(Stage stage); + Pin *stageLoadPin(Stage stage); + const char *stageInputPinName(Stage stage); + const char *stageDrvrPinName(Stage stage); + const char *stageLoadPinName(Stage stage); + + Path *path_; + const char *spice_filename_; + const char *subckts_filename_; + const char *lib_subckts_filename_; + const char *models_filename_; + + ofstream spice_stream_; + PathExpanded path_expanded_; + CellSpicePortNames cell_spice_port_names_; + ParasiticNodeMap node_map_; + int next_node_index_; + const char *net_name_; + + // Resistance to use to simulate a short circuit between spice nodes. + static const float short_ckt_resistance_; +}; + +//////////////////////////////////////////////////////////////// + +class SubcktEndsMissing : public StaException +{ +public: + SubcktEndsMissing(const char *cell_name, + const char *subckt_filename);; + const char *what() const throw(); + +protected: + string what_; +}; + +SubcktEndsMissing::SubcktEndsMissing(const char *cell_name, + const char *subckt_filename) +{ + what_ = "Error: spice subckt for cell "; + what_ += cell_name; + what_ += " missing .ends in "; + what_ += subckt_filename; +} + +const char * +SubcktEndsMissing::what() const throw() +{ + return what_.c_str(); +} + +//////////////////////////////////////////////////////////////// + +void +writeSpice (Path *path, + const char *spice_filename, + const char *subckts_filename, + const char *lib_subckts_filename, + const char *models_filename, + StaState *sta) +{ + WriteSpice writer(path, spice_filename, subckts_filename, + lib_subckts_filename, models_filename, sta); + writer.writeSpice(); +} + +const float WriteSpice::short_ckt_resistance_ = .0001; + +WriteSpice::WriteSpice(Path *path, + const char *spice_filename, + const char *subckts_filename, + const char *lib_subckts_filename, + const char *models_filename, + const StaState *sta) : + StaState(sta), + path_(path), + spice_filename_(spice_filename), + subckts_filename_(subckts_filename), + lib_subckts_filename_(lib_subckts_filename), + models_filename_(models_filename), + path_expanded_(sta), + net_name_(NULL) +{ +} + +WriteSpice::~WriteSpice() +{ + cell_spice_port_names_.deleteContents(); +} + +void +WriteSpice::writeSpice() +{ + spice_stream_.open(spice_filename_); + if (spice_stream_.is_open()) { + path_expanded_.expand(path_, true); + // Find subckt port names as a side-effect of writeSubckts. + writeSubckts(); + writeHeader(); + writeStageInstances(); + writeInputSource(); + writeStageSubckts(); + streamPrint(spice_stream_, ".end\n"); + spice_stream_.close(); + } + else + throw FileNotWritable(spice_filename_); +} + +void +WriteSpice::writeHeader() +{ + const MinMax *min_max = path_->minMax(this); + const Pvt *pvt = sdc_->operatingConditions(min_max); + if (pvt == NULL) + pvt = network_->defaultLibertyLibrary()->defaultOperatingConditions(); + float temp = pvt->temperature(); + streamPrint(spice_stream_, ".temp %.1f\n", temp); + streamPrint(spice_stream_, ".include \"%s\"\n", models_filename_); + streamPrint(spice_stream_, ".include \"%s\"\n", subckts_filename_); + + float max_time = maxTime(); + float time_step = max_time / 1e+3; + streamPrint(spice_stream_, ".tran %.3g %.3g\n\n", + time_step, max_time); +} + +float +WriteSpice::maxTime() +{ + float end_slew = path_->slew(this); + float max_time = (path_->arrival(this) + end_slew * 2) * 1.5; + return max_time; +} + +void +WriteSpice::writeStageInstances() +{ + streamPrint(spice_stream_, "*****************\n"); + streamPrint(spice_stream_, "* Stage instances\n"); + streamPrint(spice_stream_, "*****************\n\n"); + + for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { + const char *stage_name = stageName(stage).c_str(); + if (stage == stageFirst()) + streamPrint(spice_stream_, "x%s %s %s %s\n", + stage_name, + stageDrvrPinName(stage), + stageLoadPinName(stage), + stage_name); + else + streamPrint(spice_stream_, "x%s %s %s %s %s\n", + stage_name, + stageInputPinName(stage), + stageDrvrPinName(stage), + stageLoadPinName(stage), + stage_name); + } + streamPrint(spice_stream_, "\n"); +} + +float +WriteSpice::pgPortVoltage(const char *pg_port_name, + LibertyCell *cell) +{ + auto pg_port = cell->findPgPort(pg_port_name); + return pgPortVoltage(pg_port); +} + +float +WriteSpice::pgPortVoltage(LibertyPgPort *pg_port) +{ + auto cell = pg_port->cell(); + auto voltage_name = pg_port->voltageName(); + auto lib = cell->libertyLibrary(); + float voltage = lib->supplyVoltage(voltage_name); + return voltage; +} + +void +WriteSpice::writeInputSource() +{ + streamPrint(spice_stream_, "**************\n"); + streamPrint(spice_stream_, "* Input source\n"); + streamPrint(spice_stream_, "**************\n\n"); + + Stage input_stage = stageFirst(); + streamPrint(spice_stream_, "v1 %s 0 pwl(\n", + stageDrvrPinName(input_stage)); + auto wire_arc = stageWireArc(input_stage); + auto load_pin = stageLoadPin(input_stage); + auto cell = network_->libertyCell(network_->instance(load_pin)); + auto load_port = network_->libertyPort(load_pin); + const char *pg_gnd_port_name = load_port->relatedGroundPin(); + const char *pg_pwr_port_name = load_port->relatedPowerPin(); + auto gnd_volt = pgPortVoltage(pg_gnd_port_name, cell); + auto pwr_volt = pgPortVoltage(pg_pwr_port_name, cell); + float volt0, volt1; + if (wire_arc->fromTrans()->asRiseFall() == TransRiseFall::rise()) { + volt0 = gnd_volt; + volt1 = pwr_volt; + } + else { + volt0 = pwr_volt; + volt1 = gnd_volt; + } + float time0 = .1e-9; + float time1 = .2e-9; + streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0); + streamPrint(spice_stream_, "+%.3e %.3e\n", time0, volt0); + streamPrint(spice_stream_, "+%.3e %.3e\n", time1, volt1); + streamPrint(spice_stream_, "+%.3e %.3e\n", maxTime(), volt1); + streamPrint(spice_stream_, "+)\n\n"); +} + +void +WriteSpice::writeMeasureStmts() +{ + streamPrint(spice_stream_, "********************\n"); + streamPrint(spice_stream_, "* Measure statements\n"); + streamPrint(spice_stream_, "********************\n\n"); +} + +void +WriteSpice::writeStageSubckts() +{ + streamPrint(spice_stream_, "***************\n"); + streamPrint(spice_stream_, "* Stage subckts\n"); + streamPrint(spice_stream_, "***************\n\n"); + + for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { + if (stage == stageFirst()) + writeInputStage(stage); + else + writeGateStage(stage); + } +} + +// Input port to first gate input. +void +WriteSpice::writeInputStage(Stage stage) +{ + // Input arc. + // External driver not handled. + auto drvr_pin_name = stageDrvrPinName(stage); + auto load_pin_name = stageLoadPinName(stage); + streamPrint(spice_stream_, ".subckt %s %s %s\n", + stageName(stage).c_str(), + drvr_pin_name, + load_pin_name); + writeStageParasitics(stage); + streamPrint(spice_stream_, ".ends\n\n"); +} + +// Gate and load parasitics. +void +WriteSpice::writeGateStage(Stage stage) +{ + auto input_pin = stageInputPin(stage); + auto input_pin_name = stageInputPinName(stage); + auto drvr_pin = stageDrvrPin(stage); + auto drvr_pin_name = stageDrvrPinName(stage); + auto load_pin_name = stageLoadPinName(stage); + streamPrint(spice_stream_, ".subckt stage%d %s %s %s\n", + stage, + input_pin_name, + drvr_pin_name, + load_pin_name); + Instance *inst = network_->instance(input_pin); + const char *inst_name = network_->pathName(inst); + LibertyCell *cell = network_->libertyCell(inst); + const char *cell_name = cell->name(); + auto spice_port_names = cell_spice_port_names_[cell_name]; + + // Instance subckt call. + streamPrint(spice_stream_, "x%s", inst_name); + StringVector::Iterator port_iter(spice_port_names); + while (port_iter.hasNext()) { + const char *subckt_port_name = port_iter.next().c_str(); + auto pin = network_->findPin(inst, subckt_port_name); + auto pg_port = cell->findPgPort(subckt_port_name); + const char *pin_name; + if (pin) { + pin_name = network_->pathName(pin); + streamPrint(spice_stream_, " %s", pin_name); + } + else if (pg_port) + streamPrint(spice_stream_, " %s/%s", inst_name, subckt_port_name); + } + streamPrint(spice_stream_, " %s\n", cell_name); + + writeStageVoltageSources(cell, spice_port_names, + inst_name, + network_->libertyPort(input_pin), + network_->libertyPort(drvr_pin)); + writeStageParasitics(stage); + streamPrint(spice_stream_, ".ends\n\n"); +} + +typedef Map LibertyPortLogicValues; + +// Find the logic values for expression inputs to enable paths from_port. +void +sensitizationValues(FuncExpr *expr, + LibertyPort *from_port, + // Return values. + LibertyPortLogicValues &port_values) +{ + switch (expr->op()) { + case FuncExpr::op_port: { + break; + } + case FuncExpr::op_not: { + sensitizationValues(expr->left(), from_port, port_values); + break; + } + case FuncExpr::op_or: { + FuncExpr *left = expr->left(); + FuncExpr *right = expr->right(); + if (left->port() == from_port + && right->op() == FuncExpr::op_port) + port_values[right->port()] = logic_zero; + else if (right->port() == from_port + && left->op() == FuncExpr::op_port) + port_values[left->port()] = logic_zero; + break; + } + case FuncExpr::op_and: { + FuncExpr *left = expr->left(); + FuncExpr *right = expr->right(); + if (left->port() == from_port + && right->op() == FuncExpr::op_port) + port_values[right->port()] = logic_one; + else if (right->port() == from_port + && left->op() == FuncExpr::op_port) + port_values[left->port()] = logic_one; + break; + } + case FuncExpr::op_xor: { + // Need to know timing arc sense to get this right. + FuncExpr *left = expr->left(); + FuncExpr *right = expr->right(); + if (left->port() == from_port + && right->op() == FuncExpr::op_port) + port_values[right->port()] = logic_zero; + else if (right->port() == from_port + && left->op() == FuncExpr::op_port) + port_values[left->port()] = logic_zero; + break; + } + case FuncExpr::op_one: + case FuncExpr::op_zero: + break; + } +} + +// Power/ground and input voltage sources. +void +WriteSpice::writeStageVoltageSources(LibertyCell *cell, + StringVector *spice_port_names, + const char *inst_name, + LibertyPort *from_port, + LibertyPort *drvr_port) +{ + auto from_port_name = from_port->name(); + auto drvr_port_name = drvr_port->name(); + LibertyLibrary *lib = cell->libertyLibrary(); + LibertyPortLogicValues port_values; + sensitizationValues(drvr_port->function(), from_port, port_values); + int volt_source = 1; + debugPrint1(debug_, "write_spice", 2, "subckt %s\n", cell->name()); + StringVector::Iterator port_iter(spice_port_names); + while (port_iter.hasNext()) { + auto subckt_port_name = port_iter.next().c_str(); + auto pg_port = cell->findPgPort(subckt_port_name); + debugPrint2(debug_, "write_spice", 2, " port %s%s\n", + subckt_port_name, + pg_port ? " pwr/gnd" : ""); + if (pg_port) { + auto voltage = pgPortVoltage(pg_port); + streamPrint(spice_stream_, "v%d %s/%s 0 %.3f\n", + volt_source, + inst_name, subckt_port_name, + voltage); + volt_source++; + } else if (!(stringEq(subckt_port_name, from_port_name) + || stringEq(subckt_port_name, drvr_port_name))) { + // Input voltage to sensitize path from gate input to output. + LibertyPort *port = cell->findLibertyPort(subckt_port_name); + if (port) { + const char *pg_port_name = NULL; + bool port_has_value; + LogicValue port_value; + port_values.findKey(port, port_value, port_has_value); + if (port_has_value) { + switch (port_value) { + case logic_zero: + pg_port_name = port->relatedGroundPin(); + break; + case logic_one: + pg_port_name = port->relatedPowerPin(); + break; + default: + break; + } + if (pg_port_name) { + auto pg_port = cell->findPgPort(pg_port_name); + if (pg_port) { + auto voltage_name = pg_port->voltageName(); + if (voltage_name) { + float voltage = lib->supplyVoltage(voltage_name); + streamPrint(spice_stream_, "v%d %s/%s 0 %.3f\n", + volt_source, + inst_name, subckt_port_name, + voltage); + volt_source++; + } + else + report_->error("port %s %s voltage %s not found,\n", + subckt_port_name, + pg_port_name, + voltage_name); + } + else + report_->error("port %s %s not found,\n", + subckt_port_name, + pg_port_name); + } + } + } + } + } +} + +typedef Set ParasiticDeviceSet; +typedef Set ParasiticNodeSet; + +void +findParasiticDevicesNodes(ParasiticNode *node, + Parasitics *parasitics, + // Return values. + ParasiticNodeSet &nodes, + ParasiticDeviceSet &devices) +{ + nodes.insert(node); + auto device_iter = parasitics->deviceIterator(node); + while (device_iter->hasNext()) { + auto device = device_iter->next(); + if (!devices.hasKey(device)) { + devices.insert(device); + auto other_node = parasitics->otherNode(device, node); + findParasiticDevicesNodes(other_node, parasitics, nodes, devices); + } + } + delete device_iter; +} + +void +WriteSpice::writeStageParasitics(Stage stage) +{ + auto drvr_path = stageDrvrPath(stage); + auto drvr_pin = stageDrvrPin(stage); + auto load_pin = stageLoadPin(stage); + auto dcalc_ap = drvr_path->dcalcAnalysisPt(this); + auto parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + auto parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + int resistor_index = 1; + int cap_index = 1; + if (parasitic) { + Net *net = network_->net(drvr_pin); + auto net_name = + net ? network_->pathName(net) : network_->pathName(drvr_pin); + initNodeMap(net_name); + streamPrint(spice_stream_, "* Net %s\n", net_name); + auto node = parasitics_->findNode(parasitic, drvr_pin); + ParasiticNodeSet nodes; + ParasiticDeviceSet devices; + findParasiticDevicesNodes(node, parasitics_, nodes, devices); + ParasiticDeviceSet::Iterator device_iter(devices); + while (device_iter.hasNext()) { + auto device = device_iter.next(); + auto resistance = parasitics_->value(device, parasitic_ap); + if (parasitics_->isResistor(device)) { + ParasiticNode *node1, *node2; + parasitics_->resistorNodes(device, node1, node2); + streamPrint(spice_stream_, "R%d %s %s %.3e\n", + resistor_index, + nodeName(node1), + nodeName(node2), + resistance); + resistor_index++; + } + else if (parasitics_->isCouplingCap(device)) { + } + } + ParasiticNodeSet::Iterator node_iter(nodes); + while (node_iter.hasNext()) { + auto node = node_iter.next(); + auto cap = parasitics_->nodeGndCap(node, parasitic_ap); + streamPrint(spice_stream_, "C%d %s 0 %.3e\n", + cap_index, + nodeName(node), + cap); + cap_index++; + } + } + else + streamPrint(spice_stream_, "R1 %s %s %.3e\n", + network_->pathName(drvr_pin), + network_->pathName(load_pin), + short_ckt_resistance_); +} + +void +WriteSpice::initNodeMap(const char *net_name) +{ + stringDelete(net_name_); + node_map_.clear(); + next_node_index_ = 1; + net_name_ = stringCopy(net_name); +} + +const char * +WriteSpice::nodeName(ParasiticNode *node) +{ + auto pin = parasitics_->connectionPin(node); + if (pin) + return parasitics_->name(node); + else { + int node_index; + bool node_index_exists; + node_map_.findKey(node, node_index, node_index_exists); + if (!node_index_exists) { + node_index = next_node_index_++; + node_map_[node] = node_index; + } + return stringPrintTmp(strlen(net_name_) + 10, "%s/%d", + net_name_, node_index); + } +} + +//////////////////////////////////////////////////////////////// + +// Copy the subckt definition from lib_subckts_filename for +// each cell in path to path_subckts_filename. +void +WriteSpice::writeSubckts() +{ + StringSet path_cell_names; + findPathCellnames(path_cell_names); + + ifstream lib_subckts_stream(lib_subckts_filename_); + if (lib_subckts_stream.is_open()) { + ofstream subckts_stream(subckts_filename_); + if (subckts_stream.is_open()) { + string line; + while (getline(lib_subckts_stream, line)) { + // .subckt [args..] + StringVector tokens; + split(line, " \t", tokens); + if (tokens.size() >= 2 + && stringEqual(tokens[0].c_str(), ".subckt")) { + const char *cell_name = tokens[1].c_str(); + if (path_cell_names.hasKey(cell_name)) { + subckts_stream << line << "\n"; + bool found_ends = false; + while (getline(lib_subckts_stream, line)) { + subckts_stream << line << "\n"; + if (stringEqual(line.c_str(), ".ends")) { + subckts_stream << "\n"; + found_ends = true; + break; + } + } + if (!found_ends) + throw SubcktEndsMissing(cell_name, lib_subckts_filename_); + path_cell_names.eraseKey(cell_name); + } + recordSpicePortNames(cell_name, tokens); + } + } + subckts_stream.close(); + lib_subckts_stream.close(); + + if (!path_cell_names.empty()) { + StringSet::Iterator cell_iter(path_cell_names); + report_->error("The following subkcts are missing from %s\n", + lib_subckts_filename_); + while (cell_iter.hasNext()) { + const char *cell_name = cell_iter.next(); + report_->printError(" %s\n", cell_name); + } + } + } + else { + lib_subckts_stream.close(); + throw FileNotWritable(subckts_filename_); + } + } + else + throw FileNotReadable(lib_subckts_filename_); +} + +void +WriteSpice::findPathCellnames(// Return values. + StringSet &path_cell_names) +{ + for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { + auto arc = stageGateArc(stage); + if (arc) { + LibertyCell *cell = arc->set()->libertyCell(); + if (cell) { + debugPrint1(debug_, "write_spice", 2, "cell %s\n", cell->name()); + path_cell_names.insert(cell->name()); + } + } + } +} + +void +WriteSpice::recordSpicePortNames(const char *cell_name, + StringVector &tokens) +{ + auto cell = network_->findLibertyCell(cell_name); + auto spice_port_names = new StringVector; + for (int i = 2; i < tokens.size(); i++) { + const char *port_name = tokens[i].c_str(); + auto port = cell->findLibertyPort(port_name); + auto pg_port = cell->findPgPort(port_name); + if (port == NULL && pg_port == NULL) + report_->error("subckt %s port %s has no corresponding liberty port or pg_port.\n", + cell_name, port_name); + spice_port_names->push_back(port_name); + } + cell_spice_port_names_[cell_name] = spice_port_names; +} + +//////////////////////////////////////////////////////////////// + +Stage +WriteSpice::stageFirst() +{ + return 1; +} + +Stage +WriteSpice::stageLast() +{ + return (path_expanded_.size() + 1) / 2; +} + +string +WriteSpice::stageName(Stage stage) +{ + string name; + stringPrint(name, "stage%d", stage); + return name; +} + +int +WriteSpice::stageGateInputPathIndex(Stage stage) +{ + return stage * 2 - 3; +} + +int +WriteSpice::stageDrvrPathIndex(Stage stage) +{ + return stage * 2 - 2; +} + +int +WriteSpice::stageLoadPathIndex(Stage stage) +{ + return stage * 2 - 1; +} + +PathRef * +WriteSpice::stageGateInputPath(Stage stage) +{ + int path_index = stageGateInputPathIndex(stage); + return path_expanded_.path(path_index); +} + +PathRef * +WriteSpice::stageDrvrPath(Stage stage) +{ + int path_index = stageDrvrPathIndex(stage); + return path_expanded_.path(path_index); +} + +PathRef * +WriteSpice::stageLoadPath(Stage stage) +{ + int path_index = stageLoadPathIndex(stage); + return path_expanded_.path(path_index); +} + +TimingArc * +WriteSpice::stageGateArc(Stage stage) +{ + int path_index = stageDrvrPathIndex(stage); + if (path_index >= 0) + return path_expanded_.prevArc(path_index); + else + return NULL; +} + +TimingArc * +WriteSpice::stageWireArc(Stage stage) +{ + int path_index = stageLoadPathIndex(stage); + return path_expanded_.prevArc(path_index); +} + +Edge * +WriteSpice::stageGateEdge(Stage stage) +{ + PathRef *path = stageGateInputPath(stage); + TimingArc *arc = stageGateArc(stage); + return path->prevEdge(arc, this); +} + +Edge * +WriteSpice::stageWireEdge(Stage stage) +{ + PathRef *path = stageLoadPath(stage); + TimingArc *arc = stageWireArc(stage); + return path->prevEdge(arc, this); +} + +Pin * +WriteSpice::stageInputPin(Stage stage) +{ + PathRef *path = stageGateInputPath(stage); + return path->pin(this); +} + +Pin * +WriteSpice::stageDrvrPin(Stage stage) +{ + PathRef *path = stageDrvrPath(stage); + return path->pin(this); +} + +Pin * +WriteSpice::stageLoadPin(Stage stage) +{ + PathRef *path = stageLoadPath(stage); + return path->pin(this); +} + +const char * +WriteSpice::stageInputPinName(Stage stage) +{ + const Pin *pin = stageInputPin(stage); + return network_->pathName(pin); +} + +const char * +WriteSpice::stageDrvrPinName(Stage stage) +{ + Pin *pin = stageDrvrPin(stage); + return network_->pathName(pin); +} + +const char * +WriteSpice::stageLoadPinName(Stage stage) +{ + const Pin *pin = stageLoadPin(stage); + return network_->pathName(pin); +} + +//////////////////////////////////////////////////////////////// + +void +split(const string &text, + const string &delims, + // Return values. + StringVector &tokens) +{ + auto start = text.find_first_not_of(delims); + auto end = text.find_first_of(delims, start); + while (end != string::npos) { + tokens.push_back(text.substr(start, end - start)); + start = text.find_first_not_of(delims, end); + end = text.find_first_of(delims, start); + } + if (start != string::npos) + tokens.push_back(text.substr(start)); +} + +// fprintf for c++ streams. +// Yes, I hate formatted output to ostream THAT much. +void +streamPrint(ofstream &stream, + const char *fmt, + ...) +{ + va_list args; + va_start(args, fmt); + char *result; + vasprintf(&result, fmt, args); + stream << result; + free(result); + va_end(args); +} + +// print for c++ strings. +void +stringPrint(string &str, + const char *fmt, + ...) +{ + va_list args; + va_start(args, fmt); + char *result; + vasprintf(&result, fmt, args); + str = result; + free(result); + va_end(args); +} + +} // namespace diff --git a/search/WriteSpice.hh b/search/WriteSpice.hh new file mode 100644 index 00000000..74c22b6b --- /dev/null +++ b/search/WriteSpice.hh @@ -0,0 +1,37 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2018, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef STA_WRITE_SPICE_H +#define STA_WRITE_SPICE_H + +namespace sta { + +// Write a spice deck for path. +// Throws FileNotReadable, FileNotWritable, SubcktEndsMissing +void +writeSpice(Path *path, + // Spice file written for path. + const char *spice_filename, + // Subckts used by path included in spice file. + const char *subckts_filename, + // File of all cell spice subckt definitions. + const char *lib_subckts_filename, + // Device model file included in spice file. + const char *models_filename, + StaState *sta); + +} // namespace +#endif diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index caf13fa2..6e19a447 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -78,6 +78,7 @@ #include "ReportPath.hh" #include "Power.hh" #include "Property.hh" +#include "WriteSpice.hh" #include "Sta.hh" namespace sta { @@ -4585,6 +4586,19 @@ write_sdc_cmd(const char *filename, Sta::sta()->writeSdc(filename, native, no_timestamp, digits); } +void +write_spice(PathRef *path, + const char *spice_filename, + const char *subckts_filename, + const char *lib_subckts_filename, + const char *models_filename) +{ + cmdLinkedNetwork(); + Sta *sta = Sta::sta(); + writeSpice(path, spice_filename, subckts_filename, + lib_subckts_filename, models_filename, sta); +} + void set_cmd_unit_scale(const char *unit_name, float scale) diff --git a/tcl/Util.tcl b/tcl/Util.tcl index 926c4903..43d68db9 100644 --- a/tcl/Util.tcl +++ b/tcl/Util.tcl @@ -391,15 +391,13 @@ proc check_percent { cmd_arg arg } { ################################################################ # The builtin Tcl "source" and "unknown" commands are redefined by sta. -# The original definition is not clobbered until Final.tcl. # This rename provices a mechanism to refer to the original TCL # command. rename source builtin_source -rename unknown builtin_unknown # Numeric expressions eval to themselves so braces aren't required # around bus names like foo[2] or foo[*]. -proc unknown { args } { +proc sta_unknown { args } { global errorCode errorInfo set name [lindex $args 0] @@ -434,3 +432,179 @@ proc unknown { args } { } } } + +# Copied from init.tcl +proc builtin_unknown args { + variable ::tcl::UnknownPending + global auto_noexec auto_noload env tcl_interactive + + + if {[info exists ::errorInfo]} { + set savedErrorInfo $::errorInfo + } + if {[info exists ::errorCode]} { + set savedErrorCode $::errorCode + } + + set name [lindex $args 0] + if {![info exists auto_noload]} { + # + # Make sure we're not trying to load the same proc twice. + # + if {[info exists UnknownPending($name)]} { + return -code error "self-referential recursion\ + in \"unknown\" for command \"$name\"" + } + set UnknownPending($name) pending + set ret [catch { + auto_load $name [uplevel 1 {::namespace current}] + } msg opts] + unset UnknownPending($name) + if {$ret != 0} { + dict append opts -errorinfo "\n (autoloading \"$name\")" + return -options $opts $msg + } + if {![array size UnknownPending]} { + unset UnknownPending + } + if {$msg} { + if {[info exists savedErrorCode]} { + set ::errorCode $savedErrorCode + } else { + unset -nocomplain ::errorCode + } + if {[info exists savedErrorInfo]} { + set ::errorInfo $savedErrorInfo + } else { + unset -nocomplain ::errorInfo + } + set code [catch {uplevel 1 $args} msg opts] + if {$code == 1} { + # + # Compute stack trace contribution from the [uplevel]. + # Note the dependence on how Tcl_AddErrorInfo, etc. + # construct the stack trace. + # + set errorInfo [dict get $opts -errorinfo] + set errorCode [dict get $opts -errorcode] + set cinfo $args + if {[string bytelength $cinfo] > 150} { + set cinfo [string range $cinfo 0 150] + while {[string bytelength $cinfo] > 150} { + set cinfo [string range $cinfo 0 end-1] + } + append cinfo ... + } + append cinfo "\"\n (\"uplevel\" body line 1)" + append cinfo "\n invoked from within" + append cinfo "\n\"uplevel 1 \$args\"" + # + # Try each possible form of the stack trace + # and trim the extra contribution from the matching case + # + set expect "$msg\n while executing\n\"$cinfo" + if {$errorInfo eq $expect} { + # + # The stack has only the eval from the expanded command + # Do not generate any stack trace here. + # + dict unset opts -errorinfo + dict incr opts -level + return -options $opts $msg + } + # + # Stack trace is nested, trim off just the contribution + # from the extra "eval" of $args due to the "catch" above. + # + set expect "\n invoked from within\n\"$cinfo" + set exlen [string length $expect] + set eilen [string length $errorInfo] + set i [expr {$eilen - $exlen - 1}] + set einfo [string range $errorInfo 0 $i] + # + # For now verify that $errorInfo consists of what we are about + # to return plus what we expected to trim off. + # + if {$errorInfo ne "$einfo$expect"} { + error "Tcl bug: unexpected stack trace in \"unknown\"" {} \ + [list CORE UNKNOWN BADTRACE $einfo $expect $errorInfo] + } + return -code error -errorcode $errorCode \ + -errorinfo $einfo $msg + } else { + dict incr opts -level + return -options $opts $msg + } + } + } + + if {([info level] == 1) && ([info script] eq "") \ + && [info exists tcl_interactive] && $tcl_interactive} { + if {![info exists auto_noexec]} { + set new [auto_execok $name] + if {$new ne ""} { + set redir "" + if {[namespace which -command console] eq ""} { + set redir ">&@stdout <@stdin" + } + uplevel 1 [list ::catch \ + [concat exec $redir $new [lrange $args 1 end]] \ + ::tcl::UnknownResult ::tcl::UnknownOptions] + dict incr ::tcl::UnknownOptions -level + return -options $::tcl::UnknownOptions $::tcl::UnknownResult + } + } + if {$name eq "!!"} { + set newcmd [history event] + } elseif {[regexp {^!(.+)$} $name -> event]} { + set newcmd [history event $event] + } elseif {[regexp {^\^([^^]*)\^([^^]*)\^?$} $name -> old new]} { + set newcmd [history event -1] + catch {regsub -all -- $old $newcmd $new newcmd} + } + if {[info exists newcmd]} { + tclLog $newcmd + history change $newcmd 0 + uplevel 1 [list ::catch $newcmd \ + ::tcl::UnknownResult ::tcl::UnknownOptions] + dict incr ::tcl::UnknownOptions -level + return -options $::tcl::UnknownOptions $::tcl::UnknownResult + } + + set ret [catch {set candidates [info commands $name*]} msg] + if {$name eq "::"} { + set name "" + } + if {$ret != 0} { + dict append opts -errorinfo \ + "\n (expanding command prefix \"$name\" in unknown)" + return -options $opts $msg + } + # Filter out bogus matches when $name contained + # a glob-special char [Bug 946952] + if {$name eq ""} { + # Handle empty $name separately due to strangeness + # in [string first] (See RFE 1243354) + set cmds $candidates + } else { + set cmds [list] + foreach x $candidates { + if {[string first $name $x] == 0} { + lappend cmds $x + } + } + } + if {[llength $cmds] == 1} { + uplevel 1 [list ::catch [lreplace $args 0 0 [lindex $cmds 0]] \ + ::tcl::UnknownResult ::tcl::UnknownOptions] + dict incr ::tcl::UnknownOptions -level + return -options $::tcl::UnknownOptions $::tcl::UnknownResult + } + if {[llength $cmds]} { + return -code error "ambiguous command name \"$name\": [lsort $cmds]" + } + } + return -code error "invalid command name \"$name\"" +} + +namespace unknown sta_unknown diff --git a/util/Vector.hh b/util/Vector.hh index 3b1ac463..d83775ae 100644 --- a/util/Vector.hh +++ b/util/Vector.hh @@ -30,6 +30,7 @@ public: Vector() : std::vector() {} Vector(size_t n) : std::vector(n) {} Vector(size_t n, const OBJ &obj) : std::vector(n, obj) {} + // Erase an object from the vector (slow). void eraseObject(OBJ obj)