From 79fda841d5d7baf13cb1df73da44df604a4bd426 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 15 Apr 2023 14:31:47 -0700 Subject: [PATCH 01/24] write path spice off path pins Signed-off-by: James Cherry --- include/sta/WritePathSpice.hh | 12 ++++++- search/WritePathSpice.cc | 66 +++++++++++++++++++++++++++-------- tcl/StaTcl.i | 27 +++++++++++++- tcl/WritePathSpice.tcl | 2 +- 4 files changed, 89 insertions(+), 18 deletions(-) diff --git a/include/sta/WritePathSpice.hh b/include/sta/WritePathSpice.hh index 6ab489e5..a54c9c95 100644 --- a/include/sta/WritePathSpice.hh +++ b/include/sta/WritePathSpice.hh @@ -16,8 +16,16 @@ #pragma once +#include +#include + namespace sta { +using std::string; +using std::set; + +typedef set StdStringSet; + class Path; class StaState; @@ -33,7 +41,9 @@ writePathSpice(Path *path, const char *lib_subckt_filename, // Device model file included in spice file. const char *model_filename, - const char *power_name, + // Nets off of path to include in the spice run. + StdStringSet *off_path_pin_names, + const char *power_name, const char *gnd_name, StaState *sta); diff --git a/search/WritePathSpice.cc b/search/WritePathSpice.cc index 99ec69df..ee68a312 100644 --- a/search/WritePathSpice.cc +++ b/search/WritePathSpice.cc @@ -45,11 +45,9 @@ namespace sta { -using std::string; using std::ofstream; using std::ifstream; using std::max; -using std::set; typedef Map CellSpicePortNames; typedef int Stage; @@ -71,6 +69,7 @@ public: const char *subckt_filename, const char *lib_subckt_filename, const char *model_filename, + StdStringSet *off_path_pin_names, const char *power_name, const char *gnd_name, const StaState *sta); @@ -100,8 +99,8 @@ private: DcalcAPIndex dcalc_ap_index); void writeStageParasitics(Stage stage); void writeSubckts(); - set findPathCellnames(); - void findPathCellSubckts(set &path_cell_names); + StdStringSet findPathCellnames(); + void findPathCellSubckts(StdStringSet &path_cell_names); void recordSpicePortNames(const char *cell_name, StringVector &tokens); float maxTime(); @@ -209,12 +208,14 @@ private: const char *stageLoadPinName(Stage stage); LibertyCell *stageLibertyCell(Stage stage); Instance *stageInstance(Stage stage); + StdStringSet stageOffPathPinNames(Stage stage); Path *path_; const char *spice_filename_; const char *subckt_filename_; const char *lib_subckt_filename_; const char *model_filename_; + StdStringSet *off_path_pin_names_; const char *power_name_; const char *gnd_name_; @@ -270,13 +271,14 @@ writePathSpice(Path *path, const char *subckt_filename, const char *lib_subckt_filename, const char *model_filename, - const char *power_name, + StdStringSet *off_path_pin_names, + const char *power_name, const char *gnd_name, StaState *sta) { WritePathSpice writer(path, spice_filename, subckt_filename, lib_subckt_filename, model_filename, - power_name, gnd_name, sta); + off_path_pin_names, power_name, gnd_name, sta); writer.writeSpice(); } @@ -285,6 +287,7 @@ WritePathSpice::WritePathSpice(Path *path, const char *subckt_filename, const char *lib_subckt_filename, const char *model_filename, + StdStringSet *off_path_pin_names, const char *power_name, const char *gnd_name, const StaState *sta) : @@ -294,6 +297,7 @@ WritePathSpice::WritePathSpice(Path *path, subckt_filename_(subckt_filename), lib_subckt_filename_(lib_subckt_filename), model_filename_(model_filename), + off_path_pin_names_(off_path_pin_names), power_name_(power_name), gnd_name_(gnd_name), path_expanded_(sta), @@ -388,6 +392,9 @@ WritePathSpice::writePrintStmt() for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { streamPrint(spice_stream_, " v(%s)", stageDrvrPinName(stage)); streamPrint(spice_stream_, " v(%s)", stageLoadPinName(stage)); + StdStringSet off_path_names = stageOffPathPinNames(stage); + for (const string &off_path_name : off_path_names) + streamPrint(spice_stream_, " v(%s)", off_path_name.c_str()); } streamPrint(spice_stream_, "\n\n"); } @@ -441,13 +448,17 @@ WritePathSpice::writeStageInstances() stageDrvrPinName(stage), stageLoadPinName(stage), stage_cname); - else - streamPrint(spice_stream_, "x%s %s %s %s %s\n", + else { + streamPrint(spice_stream_, "x%s %s %s %s", stage_cname, stageGateInputPinName(stage), stageDrvrPinName(stage), - stageLoadPinName(stage), - stage_cname); + stageLoadPinName(stage)); + StdStringSet off_path_names = stageOffPathPinNames(stage); + for (const string &off_path_name : off_path_names) + streamPrint(spice_stream_, " %s", off_path_name.c_str()); + streamPrint(spice_stream_, " %s\n", stage_cname); + } } streamPrint(spice_stream_, "\n"); } @@ -855,11 +866,16 @@ WritePathSpice::writeGateStage(Stage stage) const char *drvr_pin_name = stageDrvrPinName(stage); const Pin *load_pin = stageLoadPin(stage); const char *load_pin_name = stageLoadPinName(stage); - streamPrint(spice_stream_, ".subckt stage%d %s %s %s\n", + streamPrint(spice_stream_, ".subckt stage%d %s %s %s", stage, input_pin_name, drvr_pin_name, load_pin_name); + StdStringSet off_path_names = stageOffPathPinNames(stage); + for (const string &off_path_name : off_path_names) + streamPrint(spice_stream_, " %s", off_path_name.c_str()); + streamPrint(spice_stream_, "\n"); + // Driver subckt call. Instance *inst = stageInstance(stage); LibertyPort *input_port = stageGateInputPort(stage); @@ -1433,7 +1449,7 @@ WritePathSpice::nodeName(ParasiticNode *node) void WritePathSpice::writeSubckts() { - set path_cell_names = findPathCellnames(); + StdStringSet path_cell_names = findPathCellnames(); findPathCellSubckts(path_cell_names); ifstream lib_subckts_stream(lib_subckt_filename_); @@ -1489,10 +1505,10 @@ WritePathSpice::writeSubckts() throw FileNotReadable(lib_subckt_filename_); } -set +StdStringSet WritePathSpice::findPathCellnames() { - set path_cell_names; + StdStringSet path_cell_names; for (Stage stage = stageFirst(); stage <= stageLast(); stage++) { TimingArc *arc = stageGateArc(stage); if (arc) { @@ -1520,7 +1536,7 @@ WritePathSpice::findPathCellnames() // Subckts can call subckts (asap7). void -WritePathSpice::findPathCellSubckts(set &path_cell_names) +WritePathSpice::findPathCellSubckts(StdStringSet &path_cell_names) { ifstream lib_subckts_stream(lib_subckt_filename_); if (lib_subckts_stream.is_open()) { @@ -1731,6 +1747,26 @@ WritePathSpice::stageLoadPinName(Stage stage) return network_->pathName(pin); } +StdStringSet +WritePathSpice::stageOffPathPinNames(Stage stage) +{ + StdStringSet pin_names; + if (off_path_pin_names_) { + const PathRef *path = stageDrvrPath(stage); + Vertex *drvr = path->vertex(this); + VertexOutEdgeIterator edge_iter(drvr, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *load = edge->to(graph_); + const Pin *load_pin = load->pin(); + string load_pin_name = network_->pathName(load_pin); + if (off_path_pin_names_->find(load_pin_name) != off_path_pin_names_->end()) + pin_names.insert(load_pin_name); + } + } + return pin_names; +} + Instance * WritePathSpice::stageInstance(Stage stage) { diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index 86ec7975..08946e4b 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -254,6 +254,26 @@ tclListSeqConstChar(Tcl_Obj *const source, return nullptr; } +StdStringSet * +tclListSetStdString(Tcl_Obj *const source, + Tcl_Interp *interp) +{ + int argc; + Tcl_Obj **argv; + + if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) { + StdStringSet *set = new StdStringSet; + for (int i = 0; i < argc; i++) { + int length; + const char *str = Tcl_GetStringFromObj(argv[i], &length); + set->insert(str); + } + return set; + } + else + return nullptr; +} + //////////////////////////////////////////////////////////////// // Sequence out to tcl list. @@ -425,6 +445,10 @@ using namespace sta; $1 = tclListSeqConstChar($input, interp); } +%typemap(in) StdStringSet* { + $1 = tclListSetStdString($input, interp); +} + %typemap(out) StringSeq* { StringSeq *strs = $1; Tcl_Obj *list = Tcl_NewListObj(0, nullptr); @@ -4865,12 +4889,13 @@ write_path_spice_cmd(PathRef *path, const char *subckt_filename, const char *lib_subckt_filename, const char *model_filename, + StdStringSet *off_path_pins, const char *power_name, const char *gnd_name) { Sta *sta = Sta::sta(); writePathSpice(path, spice_filename, subckt_filename, - lib_subckt_filename, model_filename, + lib_subckt_filename, model_filename, off_path_pins, power_name, gnd_name, sta); } diff --git a/tcl/WritePathSpice.tcl b/tcl/WritePathSpice.tcl index a8cf9c5a..0ba7da65 100644 --- a/tcl/WritePathSpice.tcl +++ b/tcl/WritePathSpice.tcl @@ -89,7 +89,7 @@ proc write_path_spice { args } { set spice_file [file join $spice_dir "$path_name.sp"] set subckt_file [file join $spice_dir "$path_name.subckt"] write_path_spice_cmd $path $spice_file $subckt_file \ - $lib_subckt_file $model_file $power $ground + $lib_subckt_file $model_file {} $power $ground incr path_index } } From 54bf27d577e0ffc6a5afbc41cdb02e41a0280619 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 17 Apr 2023 08:52:20 -0700 Subject: [PATCH 02/24] Units::scaledSuffix Signed-off-by: James Cherry --- include/sta/Units.hh | 7 ++++++- liberty/LibertyWriter.cc | 25 ++++++++++--------------- liberty/Units.cc | 17 ++++++++++++++++- tcl/StaTcl.i | 14 ++++++++++++-- 4 files changed, 44 insertions(+), 19 deletions(-) diff --git a/include/sta/Units.hh b/include/sta/Units.hh index b3fd6a19..4e6f5e1f 100644 --- a/include/sta/Units.hh +++ b/include/sta/Units.hh @@ -33,8 +33,10 @@ public: void operator=(const Unit &unit); float scale() const { return scale_; } void setScale(float scale); - const char *scaleAbreviation() const; + const char *scaleAbbreviation() const; const char *suffix() const { return suffix_; } + // scale abbreviation + suffix + const char *scaledSuffix() const { return scaled_suffix_; } void setSuffix(const char *suffix); int digits() const { return digits_; } void setDigits(int digits); @@ -46,8 +48,11 @@ public: int digits) const; private: + void setScaledSuffix(); + float scale_; // multiplier from user units to internal units const char *suffix_; // print suffix + const char *scaled_suffix_; int digits_; // print digits (after decimal pt) }; diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index 7b3216e0..cf04cb5d 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -123,26 +123,21 @@ LibertyWriter::writeHeader() fprintf(stream_, " delay_model : table_lookup;\n"); fprintf(stream_, " simulation : false;\n"); const Unit *cap_unit = library_->units()->capacitanceUnit(); - fprintf(stream_, " capacitive_load_unit (1,%s%s);\n", - cap_unit->scaleAbreviation(), - cap_unit->suffix()); + fprintf(stream_, " capacitive_load_unit (1,%s);\n", + cap_unit->scaledSuffix()); fprintf(stream_, " leakage_power_unit : 1pW;\n"); const Unit *current_unit = library_->units()->currentUnit(); - fprintf(stream_, " current_unit : \"1%s%s\";\n", - current_unit->scaleAbreviation(), - current_unit->suffix()); + fprintf(stream_, " current_unit : \"1%s\";\n", + current_unit->scaledSuffix()); const Unit *res_unit = library_->units()->resistanceUnit(); - fprintf(stream_, " pulling_resistance_unit : \"1%s%s\";\n", - res_unit->scaleAbreviation(), - res_unit->suffix()); + fprintf(stream_, " pulling_resistance_unit : \"1%s\";\n", + res_unit->scaledSuffix()); const Unit *time_unit = library_->units()->timeUnit(); - fprintf(stream_, " time_unit : \"1%s%s\";\n", - time_unit->scaleAbreviation(), - time_unit->suffix()); + fprintf(stream_, " time_unit : \"1%s\";\n", + time_unit->scaledSuffix()); const Unit *volt_unit = library_->units()->voltageUnit(); - fprintf(stream_, " voltage_unit : \"1%s%s\";\n", - volt_unit->scaleAbreviation(), - volt_unit->suffix()); + fprintf(stream_, " voltage_unit : \"1%s\";\n", + volt_unit->scaledSuffix()); fprintf(stream_, " library_features(report_delay_calculation);\n"); fprintf(stream_, "\n"); diff --git a/liberty/Units.cc b/liberty/Units.cc index 3f2825e3..9ff90d79 100644 --- a/liberty/Units.cc +++ b/liberty/Units.cc @@ -31,8 +31,10 @@ using std::abs; Unit::Unit(const char *suffix) : scale_(1.0), suffix_(stringCopy(suffix)), + scaled_suffix_(nullptr), digits_(3) { + setScaledSuffix(); } Unit::Unit(float scale, @@ -40,13 +42,23 @@ Unit::Unit(float scale, int digits) : scale_(scale), suffix_(stringCopy(suffix)), + scaled_suffix_(nullptr), digits_(digits) { + setScaledSuffix(); +} + +void +Unit::setScaledSuffix() +{ + stringDelete(scaled_suffix_); + scaled_suffix_ = stringPrint("%s%s", scaleAbbreviation(), suffix_); } Unit::~Unit() { stringDelete(suffix_); + stringDelete(scaled_suffix_); } void @@ -55,6 +67,7 @@ Unit::operator=(const Unit &unit) scale_ = unit.scale_; stringDelete(suffix_); suffix_ = stringCopy(unit.suffix_); + scaled_suffix_ = stringCopy(unit.scaled_suffix_); digits_ = unit.digits_; } @@ -74,10 +87,11 @@ void Unit::setScale(float scale) { scale_ = scale; + setScaledSuffix(); } const char * -Unit::scaleAbreviation() const +Unit::scaleAbbreviation() const { if (fuzzyEqual(scale_, 1E+6)) return "M"; @@ -104,6 +118,7 @@ Unit::setSuffix(const char *suffix) { stringDelete(suffix_); suffix_ = stringCopy(suffix); + setScaledSuffix(); } void diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index 08946e4b..a50f8aba 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -3987,11 +3987,11 @@ set_cmd_unit_suffix(const char *unit_name, } const char * -unit_scale_abreviation(const char *unit_name) +unit_scale_abbreviation (const char *unit_name) { Unit *unit = Sta::sta()->units()->find(unit_name); if (unit) - return unit->scaleAbreviation(); + return unit->scaleAbbreviation(); else return ""; } @@ -4006,6 +4006,16 @@ unit_suffix(const char *unit_name) return ""; } +const char * +unit_scaled_suffix(const char *unit_name) +{ + Unit *unit = Sta::sta()->units()->find(unit_name); + if (unit) + return unit->scaledSuffix(); + else + return ""; +} + //////////////////////////////////////////////////////////////// VertexIterator * From c5a38e13de724870a05ee0c29a8a7a9ad541788f Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 17 Apr 2023 08:52:33 -0700 Subject: [PATCH 03/24] dmp dcalc error handling Signed-off-by: James Cherry --- dcalc/DmpCeff.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index ff3a9f16..7ebdf5a6 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -601,6 +601,7 @@ DmpAlg::loadDelaySlew(const Pin *, slew = slew1; } catch (DmpError &error) { + fail(error.what()); delay = elmore_; slew = drvr_slew_; } @@ -620,7 +621,7 @@ DmpAlg::findVlCrossing(double vth) double DmpAlg::vlCrossingUpperBound() { - return voCrossingUpperBound() + elmore_; + return voCrossingUpperBound() + elmore_ * 2.0; } static void @@ -670,8 +671,8 @@ DmpAlg::showVl() void DmpAlg::fail(const char *reason) { - // Allow only failures to be reported with a unique debug flag. - if (debug_->check("dmp_ceff", 1) || debug_->check("dmp_ceff_fail", 1)) + // Report failures with a unique debug flag. + if (debug_->check("dmp_ceff", 1) || debug_->check("dcalc_error", 1)) report_->reportLine("delay_calc: DMP failed - %s c2=%s rpi=%s c1=%s rd=%s", reason, units_->capacitanceUnit()->asString(c2_), From 66c4c8f7b36baafed0e25ada134ef40a36969b9b Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 17 Apr 2023 08:58:41 -0700 Subject: [PATCH 04/24] report_units Signed-off-by: James Cherry --- tcl/CmdUtil.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcl/CmdUtil.tcl b/tcl/CmdUtil.tcl index fe62c4d6..8fe51a62 100644 --- a/tcl/CmdUtil.tcl +++ b/tcl/CmdUtil.tcl @@ -114,7 +114,7 @@ define_cmd_args "report_units" {} proc report_units { args } { check_argc_eq0 "report_units" $args foreach unit {"time" "capacitance" "resistance" "voltage" "current" "power" "distance"} { - report_line " $unit 1[unit_scale_abreviation $unit][unit_suffix $unit]" + report_line " $unit 1[unit_scaled_suffix $unit]" } } From 582131889a5eff87c860450e13014a175d06c36d Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 19 Apr 2023 16:44:52 -0700 Subject: [PATCH 05/24] write_path_spice max time Signed-off-by: James Cherry --- search/WritePathSpice.cc | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/search/WritePathSpice.cc b/search/WritePathSpice.cc index ee68a312..8c33e46c 100644 --- a/search/WritePathSpice.cc +++ b/search/WritePathSpice.cc @@ -104,6 +104,7 @@ private: void recordSpicePortNames(const char *cell_name, StringVector &tokens); float maxTime(); + float pathMaxTime(); const char *nodeName(ParasiticNode *node); void initNodeMap(const char *net_name); const char *spiceTrans(const RiseFall *rf); @@ -404,9 +405,6 @@ WritePathSpice::maxTime() { Stage input_stage = stageFirst(); PathRef *input_path = stageDrvrPath(input_stage); - const RiseFall *rf = input_path->transition(this); - TimingArc *next_arc = stageGateArc(input_stage + 1); - float input_slew = findSlew(input_path, rf, next_arc); if (input_path->isClock(this)) { const Clock *clk = input_path->clock(this); float period = clk->period(); @@ -414,13 +412,36 @@ WritePathSpice::maxTime() float max_time = period * clk_cycle_count_ + first_edge_offset; return max_time; } - else { - float end_slew = findSlew(path_); - float arrival = delayAsFloat(path_->arrival(this)); - float max_time = railToRailSlew(input_slew, rf) + arrival - + railToRailSlew(end_slew, rf); - return max_time; + else + return pathMaxTime(); +} + +// Make sure run time is long enough to see side load transitions along the path. +float +WritePathSpice::pathMaxTime() +{ + float max_time = 0.0; + DcalcAPIndex dcalc_ap_index = path_->dcalcAnalysisPt(this)->index(); + for (size_t i = 0; i < path_expanded_.size(); i++) { + PathRef *path = path_expanded_.path(i); + const RiseFall *rf = path->transition(this); + Vertex *vertex = path->vertex(this); + Slew path_max_slew = railToRailSlew(findSlew(vertex, rf, nullptr, dcalc_ap_index),rf); + if (vertex->isDriver(network_)) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *load = edge->to(graph_); + Slew load_slew = railToRailSlew(findSlew(load, rf, nullptr, dcalc_ap_index),rf); + if (load_slew > path_max_slew) + path_max_slew = load_slew; + } + } + float path_max_time = path->arrival(this) + path_max_slew * 2.0; + if (path_max_time > max_time) + max_time = path_max_time; } + return max_time; } float From dcecf54641efc8409d9715fd7970b3619f2fa452 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 19 Apr 2023 18:12:32 -0700 Subject: [PATCH 06/24] set_load / disconnect_pin net_drvr Signed-off-by: James Cherry --- sdc/Sdc.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 2cf82c33..b0c512c8 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -5697,6 +5697,8 @@ Sdc::disconnectPinBefore(const Pin *pin) } } } + for (int corner_index = 0; corner_index < corners_->count(); corner_index++) + drvr_pin_wire_cap_maps_[corner_index].erase(pin); } void From 917f45365a36e90f0441db776a5995733ffb617f Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 19 Apr 2023 18:46:12 -0700 Subject: [PATCH 07/24] rm WriteSpice.cc,hh Signed-off-by: James Cherry --- search/WriteSpice.cc | 927 ------------------------------------------- search/WriteSpice.hh | 36 -- 2 files changed, 963 deletions(-) delete mode 100644 search/WriteSpice.cc delete mode 100644 search/WriteSpice.hh diff --git a/search/WriteSpice.cc b/search/WriteSpice.cc deleted file mode 100644 index 65bdb990..00000000 --- a/search/WriteSpice.cc +++ /dev/null @@ -1,927 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2023, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General 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))); - -//////////////////////////////////////////////////////////////// - -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); -} - -} // namespace diff --git a/search/WriteSpice.hh b/search/WriteSpice.hh deleted file mode 100644 index c6d7465c..00000000 --- a/search/WriteSpice.hh +++ /dev/null @@ -1,36 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2023, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#pragma once - -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 From e5c95ac382b0888c5025cb66ff0a86de45bef8ec Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 24 Apr 2023 11:35:58 -0700 Subject: [PATCH 08/24] issue161 vcd var type integer Signed-off-by: James Cherry --- power/Vcd.hh | 19 +++++++++++++- power/VcdReader.cc | 62 +++++++++++++++++++++++++++------------------- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/power/Vcd.hh b/power/Vcd.hh index 54e64cc2..2ebe6ee1 100644 --- a/power/Vcd.hh +++ b/power/Vcd.hh @@ -38,7 +38,24 @@ typedef int64_t VcdTime; typedef vector VcdScope; typedef map VcdNameMap; -enum class VcdVarType { wire, reg, parameter, real }; +enum class VcdVarType { + wire, + reg, + parameter, + integer, + real, + supply0, + supply1, + tri, + triand, + trior, + trireg, + tri0, + tri1, + wand, + wor, + unknown +}; class Vcd : public StaState { diff --git a/power/VcdReader.cc b/power/VcdReader.cc index 0e4e20b6..cd7e5e77 100644 --- a/power/VcdReader.cc +++ b/power/VcdReader.cc @@ -23,6 +23,7 @@ #include "Report.hh" #include "Error.hh" #include "StringUtil.hh" +#include "EnumNameMap.hh" namespace sta { @@ -160,6 +161,24 @@ VcdReader::setTimeUnit(const string &time_unit) vcd_->setTimeUnit(time_unit, time_unit_scale);; } +static EnumNameMap vcd_var_type_map = + {{VcdVarType::wire, "wire"}, + {VcdVarType::reg, "reg"}, + {VcdVarType::parameter, "parameter"}, + {VcdVarType::integer, "integer"}, + {VcdVarType::real, "real"}, + {VcdVarType::supply0, "supply0"}, + {VcdVarType::supply1, "supply1"}, + {VcdVarType::tri, "tri"}, + {VcdVarType::triand, "triand"}, + {VcdVarType::trior, "trior"}, + {VcdVarType::trireg, "trireg"}, + {VcdVarType::tri0, "tri0"}, + {VcdVarType::tri1, "tri1"}, + {VcdVarType::wand, "wand"}, + {VcdVarType::wor, "wor"} + }; + void VcdReader::parseVar() { @@ -167,34 +186,27 @@ VcdReader::parseVar() if (tokens.size() == 4 || tokens.size() == 5) { string type_name = tokens[0]; - VcdVarType type = VcdVarType::wire; - if (type_name == "wire") - type = VcdVarType::wire; - else if (type_name == "reg") - type = VcdVarType::reg; - else if (type_name == "parameter") - type = VcdVarType::parameter; - else if (type_name == "real") - type = VcdVarType::real; - else - report_->fileError(803, filename_, stmt_line_, - "Unknown variable type %s.", - type_name.c_str()); + VcdVarType type = vcd_var_type_map.find(type_name, VcdVarType::unknown); + if (type == VcdVarType::unknown) + report_->fileWarn(803, filename_, stmt_line_, + "Unknown variable type %s.", + type_name.c_str()); + else { + int width = stoi(tokens[1]); + string id = tokens[2]; + string name; - int width = stoi(tokens[1]); - string id = tokens[2]; - string name; + for (string &context : scope_) { + name += context; + name += '/'; + } + name += tokens[3]; + // iverilog separates bus base name from bit range. + if (tokens.size() == 5) + name += tokens[4]; - for (string &context : scope_) { - name += context; - name += '/'; + vcd_->makeVar(name, type, width, id); } - name += tokens[3]; - // iverilog separates bus base name from bit range. - if (tokens.size() == 5) - name += tokens[4]; - - vcd_->makeVar(name, type, width, id); } else report_->fileError(804, filename_, stmt_line_, "Variable syntax error."); From d6aaff86e7125c59aefaca3b599f19be7d9d3ef2 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 27 Apr 2023 15:25:18 -0700 Subject: [PATCH 09/24] ccs liberty Signed-off-by: James Cherry --- liberty/LibertyReader.cc | 2 +- liberty/TableModel.cc | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 5e811226..741ab864 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -2522,7 +2522,7 @@ LibertyReader::endOutputCurrentRiseFall(LibertyGroup *group) slew_axis->findAxisIndex(waveform->slew(), slew_index, slew_exists); cap_axis->findAxisIndex(waveform->cap(), cap_index, cap_exists); if (slew_exists && cap_exists) { - size_t index = slew_index * cap_axis->size() + cap_index; + size_t index = slew_index * slew_axis->size() + cap_index; current_waveforms[index] = waveform->stealCurrents(); (*ref_times)[slew_index] = waveform->referenceTime(); } diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 8d6dd3fe..397ae233 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -1621,10 +1621,10 @@ OutputWaveforms::voltageWaveform(float slew, { size_t slew_index = slew_axis_->findAxisIndex(slew); size_t cap_index = cap_axis_->findAxisIndex(cap); - size_t wave_index00 = slew_index * cap_axis_->size() + cap_index; - size_t wave_index01 = slew_index * cap_axis_->size() + (cap_index + 1); - size_t wave_index10 = (slew_index + 1) * cap_axis_->size() + cap_index; - size_t wave_index11 = (slew_index + 1) * cap_axis_->size() + (cap_index + 1); + size_t wave_index00 = slew_index * slew_axis_->size() + cap_index; + size_t wave_index01 = slew_index * slew_axis_->size() + (cap_index + 1); + size_t wave_index10 = (slew_index + 1) * slew_axis_->size() + cap_index; + size_t wave_index11 = (slew_index + 1) * slew_axis_->size() + (cap_index + 1); float cap0 = cap_axis_->axisValue(cap_index); float cap1 = cap_axis_->axisValue(cap_index + 1); const Table1 *values00 = voltageWaveform(wave_index00, cap0); @@ -1730,10 +1730,10 @@ OutputWaveforms::currentWaveform(float slew, { size_t slew_index = slew_axis_->findAxisIndex(slew); size_t cap_index = cap_axis_->findAxisIndex(cap); - size_t wave_index00 = slew_index * cap_axis_->size() + cap_index; - size_t wave_index01 = slew_index * cap_axis_->size() + (cap_index + 1); - size_t wave_index10 = (slew_index + 1) * cap_axis_->size() + cap_index; - size_t wave_index11 = (slew_index + 1) * cap_axis_->size() + (cap_index + 1); + size_t wave_index00 = slew_index * slew_axis_->size() + cap_index; + size_t wave_index01 = slew_index * slew_axis_->size() + (cap_index + 1); + size_t wave_index10 = (slew_index + 1) * slew_axis_->size() + cap_index; + size_t wave_index11 = (slew_index + 1) * slew_axis_->size() + (cap_index + 1); const Table1 *values00 = current_waveforms_[wave_index00]; const Table1 *values01 = current_waveforms_[wave_index01]; const Table1 *values10 = current_waveforms_[wave_index10]; From db7da0afc4044b6c174382db8ccbab46275e1d9f Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 1 May 2023 14:19:06 -0700 Subject: [PATCH 10/24] write_path_spice rspf Signed-off-by: James Cherry --- search/WritePathSpice.cc | 234 ++++++++++++++++++++++++++++----------- 1 file changed, 167 insertions(+), 67 deletions(-) diff --git a/search/WritePathSpice.cc b/search/WritePathSpice.cc index 8c33e46c..b9f1fcd4 100644 --- a/search/WritePathSpice.cc +++ b/search/WritePathSpice.cc @@ -98,6 +98,12 @@ private: const Clock *clk, DcalcAPIndex dcalc_ap_index); void writeStageParasitics(Stage stage); + void writeStageParasiticNetwork(Pin *drvr_pin, + Parasitic *parasitic, + ParasiticAnalysisPt *parasitic_ap); + void writeStagePiElmore(Pin *drvr_pin, + Parasitic *parasitic); + void writeNullParasitics(Pin *drvr_pin); void writeSubckts(); StdStringSet findPathCellnames(); void findPathCellSubckts(StdStringSet &path_cell_names); @@ -1337,60 +1343,77 @@ WritePathSpice::writeStageParasitics(Stage stage) { PathRef *drvr_path = stageDrvrPath(stage); Pin *drvr_pin = stageDrvrPin(stage); + + Net *net = network_->net(drvr_pin); + const char *net_name = net ? network_->pathName(net) : network_->pathName(drvr_pin); + initNodeMap(net_name); + streamPrint(spice_stream_, "* Net %s\n", net_name); + DcalcAnalysisPt *dcalc_ap = drvr_path->dcalcAnalysisPt(this); ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + if (parasitic) + writeStageParasiticNetwork(drvr_pin, parasitic, parasitic_ap); + else { + const RiseFall *drvr_rf = drvr_path->transition(this); + parasitic = parasitics_->findPiElmore(drvr_pin, drvr_rf, parasitic_ap); + if (parasitic) + writeStagePiElmore(drvr_pin, parasitic); + else { + streamPrint(spice_stream_, "* No parasitics found for this net.\n"); + writeNullParasitics(drvr_pin); + } + } +} + +void +WritePathSpice::writeStageParasiticNetwork(Pin *drvr_pin, + Parasitic *parasitic, + ParasiticAnalysisPt *parasitic_ap) +{ Set reachable_pins; int res_index = 1; int cap_index = 1; - if (parasitic) { - Net *net = network_->net(drvr_pin); - const char *net_name = net ? network_->pathName(net) : network_->pathName(drvr_pin); - initNodeMap(net_name); - streamPrint(spice_stream_, "* Net %s\n", net_name); - // Sort devices for consistent regression results. - Vector devices; - ParasiticDeviceIterator *device_iter1 = parasitics_->deviceIterator(parasitic); - while (device_iter1->hasNext()) { - ParasiticDevice *device = device_iter1->next(); - devices.push_back(device); + // Sort devices for consistent regression results. + Vector devices; + ParasiticDeviceIterator *device_iter1 = parasitics_->deviceIterator(parasitic); + while (device_iter1->hasNext()) { + ParasiticDevice *device = device_iter1->next(); + devices.push_back(device); + } + delete device_iter1; + + sort(devices, ParasiticDeviceLess(parasitics_)); + + for (ParasiticDevice *device : devices) { + float resistance = parasitics_->value(device, parasitic_ap); + if (parasitics_->isResistor(device)) { + ParasiticNode *node1 = parasitics_->node1(device); + ParasiticNode *node2 = parasitics_->node2(device); + streamPrint(spice_stream_, "R%d %s %s %.3e\n", + res_index, + nodeName(node1), + nodeName(node2), + resistance); + res_index++; + + const Pin *pin1 = parasitics_->connectionPin(node1); + reachable_pins.insert(pin1); + const Pin *pin2 = parasitics_->connectionPin(node2); + reachable_pins.insert(pin2); } - delete device_iter1; - - sort(devices, ParasiticDeviceLess(parasitics_)); - - for (ParasiticDevice *device : devices) { - float resistance = parasitics_->value(device, parasitic_ap); - if (parasitics_->isResistor(device)) { - ParasiticNode *node1 = parasitics_->node1(device); - ParasiticNode *node2 = parasitics_->node2(device); - streamPrint(spice_stream_, "R%d %s %s %.3e\n", - res_index, - nodeName(node1), - nodeName(node2), - resistance); - res_index++; - - const Pin *pin1 = parasitics_->connectionPin(node1); - reachable_pins.insert(pin1); - const Pin *pin2 = parasitics_->connectionPin(node2); - reachable_pins.insert(pin2); - } - else if (parasitics_->isCouplingCap(device)) { - // Ground coupling caps for now. - ParasiticNode *node1 = parasitics_->node1(device); - float cap = parasitics_->value(device, parasitic_ap); - streamPrint(spice_stream_, "C%d %s 0 %.3e\n", - cap_index, - nodeName(node1), - cap); - cap_index++; - } + else if (parasitics_->isCouplingCap(device)) { + // Ground coupling caps for now. + ParasiticNode *node1 = parasitics_->node1(device); + float cap = parasitics_->value(device, parasitic_ap); + streamPrint(spice_stream_, "C%d %s 0 %.3e\n", + cap_index, + nodeName(node1), + cap); + cap_index++; } } - else - streamPrint(spice_stream_, "* No parasitics found for this net.\n"); // Add resistors from drvr to load for missing parasitic connections. auto pin_iter = network_->connectedPinIterator(drvr_pin); @@ -1410,30 +1433,107 @@ WritePathSpice::writeStageParasitics(Stage stage) } delete pin_iter; - if (parasitic) { - // Sort node capacitors for consistent regression results. - Vector nodes; - ParasiticNodeIterator *node_iter = parasitics_->nodeIterator(parasitic); - while (node_iter->hasNext()) { - ParasiticNode *node = node_iter->next(); - nodes.push_back(node); - } - - sort(nodes, ParasiticNodeLess(parasitics_)); - - for (ParasiticNode *node : nodes) { - float cap = parasitics_->nodeGndCap(node, parasitic_ap); - // Spice has a cow over zero value caps. - if (cap > 0.0) { - streamPrint(spice_stream_, "C%d %s 0 %.3e\n", - cap_index, - nodeName(node), - cap); - cap_index++; - } - } - delete node_iter; + // Sort node capacitors for consistent regression results. + Vector nodes; + ParasiticNodeIterator *node_iter = parasitics_->nodeIterator(parasitic); + while (node_iter->hasNext()) { + ParasiticNode *node = node_iter->next(); + nodes.push_back(node); } + + sort(nodes, ParasiticNodeLess(parasitics_)); + + for (ParasiticNode *node : nodes) { + float cap = parasitics_->nodeGndCap(node, parasitic_ap); + // Spice has a cow over zero value caps. + if (cap > 0.0) { + streamPrint(spice_stream_, "C%d %s 0 %.3e\n", + cap_index, + nodeName(node), + cap); + cap_index++; + } + } + delete node_iter; +} + +void +WritePathSpice::writeStagePiElmore(Pin *drvr_pin, + Parasitic *parasitic) +{ + float c2, rpi, c1; + parasitics_->piModel(parasitic, c2, rpi, c1); + const char *c1_node = "n1"; + streamPrint(spice_stream_, "Rpi %s %s %.3e\n", + network_->pathName(drvr_pin), + c1_node, + rpi); + if (c2 > 0.0) + streamPrint(spice_stream_, "C2 %s 0 %.3e\n", + network_->pathName(drvr_pin), + c2); + if (c1 > 0.0) + streamPrint(spice_stream_, "C1 %s 0 %.3e\n", + c1_node, + c1); + + int load_index = 3; + auto pin_iter = network_->connectedPinIterator(drvr_pin); + while (pin_iter->hasNext()) { + const Pin *load_pin = pin_iter->next(); + if (load_pin != drvr_pin + && network_->isLoad(load_pin) + && !network_->isHierarchical(load_pin)) { + float elmore; + bool exists; + parasitics_->findElmore(parasitic, load_pin, elmore, exists); + if (exists) { + streamPrint(spice_stream_, "E%d el%d 0 %s 0 1.0\n", + load_index, + load_index, + network_->pathName(drvr_pin)); + streamPrint(spice_stream_, "R%d el%d %s 1.0\n", + load_index, + load_index, + network_->pathName(load_pin)); + streamPrint(spice_stream_, "C%d %s 0 %.3e\n", + load_index, + network_->pathName(load_pin), + elmore); + } + else + // Add resistor from drvr to load for missing elmore. + streamPrint(spice_stream_, "R%d %s %s %.3e\n", + load_index, + network_->pathName(drvr_pin), + network_->pathName(load_pin), + short_ckt_resistance_); + load_index++; + } + } + delete pin_iter; +} + +void +WritePathSpice::writeNullParasitics(Pin *drvr_pin) +{ + int res_index = 1; + // Add resistors from drvr to load for missing parasitic connections. + auto pin_iter = network_->connectedPinIterator(drvr_pin); + while (pin_iter->hasNext()) { + const Pin *load_pin = pin_iter->next(); + if (load_pin != drvr_pin + && network_->isLoad(load_pin) + && !network_->isHierarchical(load_pin)) { + streamPrint(spice_stream_, "R%d %s %s %.3e\n", + res_index, + network_->pathName(drvr_pin), + network_->pathName(load_pin), + short_ckt_resistance_); + res_index++; + } + } + delete pin_iter; } void From 2e6c70db98e24017f3874be29aeb2689fbd84114 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 3 May 2023 22:08:51 -0700 Subject: [PATCH 11/24] ccs tables Signed-off-by: James Cherry --- include/sta/TableModel.hh | 11 +- liberty/TableModel.cc | 249 +++++++++++++++++++++----------------- search/WritePathSpice.cc | 2 +- tcl/StaTcl.i | 38 +++--- 4 files changed, 165 insertions(+), 135 deletions(-) diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index ca75d838..33d92236 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -507,12 +507,19 @@ public: const RiseFall *rf() const { return rf_; } Table1 voltageWaveform(float in_slew, float load_cap); - Table1 currentWaveform(float slew, - float cap); + float timeToVoltage(float in_slew, + float load_cap, + float voltage); + const Table1 *currentWaveform(float slew, + float cap); float referenceTime(float slew); static bool checkAxes(TableTemplate *tbl_template); private: + float timeToVoltage(const Table1 *waveform, + float voltage); + size_t findValueIndex(const Table1 *table, + float value); Table1 *voltageWaveform(size_t wave_index, float cap); diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 397ae233..7c97e66d 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -1615,43 +1615,95 @@ OutputWaveforms::checkAxes(TableTemplate *tbl_template) && axis3->variable() == TableAxisVariable::time); } -Table1 -OutputWaveforms::voltageWaveform(float slew, +const Table1 * +OutputWaveforms::currentWaveform(float slew, float cap) { size_t slew_index = slew_axis_->findAxisIndex(slew); size_t cap_index = cap_axis_->findAxisIndex(cap); - size_t wave_index00 = slew_index * slew_axis_->size() + cap_index; - size_t wave_index01 = slew_index * slew_axis_->size() + (cap_index + 1); - size_t wave_index10 = (slew_index + 1) * slew_axis_->size() + cap_index; - size_t wave_index11 = (slew_index + 1) * slew_axis_->size() + (cap_index + 1); + size_t wave_index = slew_index * cap_axis_->size() + cap_index; + return current_waveforms_[wave_index]; +} + +float +OutputWaveforms::referenceTime(float slew) +{ + return ref_times_->findValue(slew); +} + +float +OutputWaveforms::timeToVoltage(float slew, + float cap, + float volt) +{ + size_t slew_index = slew_axis_->findAxisIndex(slew); + size_t cap_index = cap_axis_->findAxisIndex(cap); + size_t slew_count = slew_axis_->size(); + size_t wave_index00 = slew_index * slew_count + cap_index; + size_t wave_index01 = slew_index * slew_count + (cap_index + 1); + size_t wave_index10 = (slew_index + 1) * slew_count + cap_index; + size_t wave_index11 = (slew_index + 1) * slew_count + (cap_index + 1); float cap0 = cap_axis_->axisValue(cap_index); float cap1 = cap_axis_->axisValue(cap_index + 1); const Table1 *values00 = voltageWaveform(wave_index00, cap0); const Table1 *values01 = voltageWaveform(wave_index01, cap1); const Table1 *values10 = voltageWaveform(wave_index10, cap0); const Table1 *values11 = voltageWaveform(wave_index11, cap1); - TableAxisPtr time_axis00 = values00->axis1(); - TableAxisPtr time_axis01 = values01->axis1(); - TableAxisPtr time_axis10 = values10->axis1(); - TableAxisPtr time_axis11 = values11->axis1(); - // Find time axis min/max. - size_t time_step_count = 20; - float time_min = time_axis00->min(); - time_min = min(time_min, time_axis01->min()); - time_min = min(time_min, time_axis10->min()); - time_min = min(time_min, time_axis11->min()); - float time_max = time_axis00->max(); - time_max = max(time_max, time_axis01->max()); - time_max = max(time_max, time_axis10->max()); - time_max = max(time_max, time_axis11->max()); - float time_step = (time_max - time_min) / time_step_count; + TableAxisPtr time_axis00 = values00->axis1(); + + // Interpolate waveform samples at voltage steps. + size_t index1 = slew_index; + size_t index2 = cap_index; + float x1 = slew; + float x2 = cap; + float x1l = slew_axis_->axisValue(index1); + float x1u = slew_axis_->axisValue(index1 + 1); + float dx1 = (x1 - x1l) / (x1u - x1l); + float x2l = cap_axis_->axisValue(index2); + float x2u = cap_axis_->axisValue(index2 + 1); + float dx2 = (x2 - x2l) / (x2u - x2l); + + float y00 = timeToVoltage(values00, volt); + float y01 = timeToVoltage(values01, volt); + float y10 = timeToVoltage(values10, volt); + float y11 = timeToVoltage(values11, volt); + float time + = (1 - dx1) * (1 - dx2) * y00 + + dx1 * (1 - dx2) * y10 + + dx1 * dx2 * y11 + + (1 - dx1) * dx2 * y01; + return time; +} + +Table1 +OutputWaveforms::voltageWaveform(float slew, + float cap) +{ + size_t slew_index = slew_axis_->findAxisIndex(slew); + size_t cap_index = cap_axis_->findAxisIndex(cap); + size_t slew_count = slew_axis_->size(); + size_t wave_index00 = slew_index * slew_count + cap_index; + size_t wave_index01 = slew_index * slew_count + (cap_index + 1); + size_t wave_index10 = (slew_index + 1) * slew_count + cap_index; + size_t wave_index11 = (slew_index + 1) * slew_count + (cap_index + 1); + float cap0 = cap_axis_->axisValue(cap_index); + float cap1 = cap_axis_->axisValue(cap_index + 1); + const Table1 *values00 = voltageWaveform(wave_index00, cap0); + const Table1 *values01 = voltageWaveform(wave_index01, cap1); + const Table1 *values10 = voltageWaveform(wave_index10, cap0); + const Table1 *values11 = voltageWaveform(wave_index11, cap1); + + TableAxisPtr time_axis00 = values00->axis1(); + + size_t volt_step_count = 20; + float volt_max = values00->value(time_axis00->size() - 1); + float volt_step = volt_max / volt_step_count; FloatSeq *time_values = new FloatSeq; TableAxisPtr time_axis = make_shared(time_axis00->variable(), time_values); - // Interpolate waveform samples at time steps. + // Interpolate waveform samples at voltage steps. size_t index1 = slew_index; size_t index2 = cap_index; float x1 = slew; @@ -1663,30 +1715,77 @@ OutputWaveforms::voltageWaveform(float slew, float x2u = cap_axis_->axisValue(index2 + 1); float dx2 = (x2 - x2l) / (x2u - x2l); FloatSeq *values = new FloatSeq; - float prev_value = 0.0; - constexpr float value_tol = .0001; - for (size_t i = 0; i <= time_step_count; i++) { - float time = time_min + time_step * i; - if (time > time_max) - break; - float y00 = values00->findValueClip(time); - float y10 = values10->findValueClip(time); - float y11 = values11->findValueClip(time); - float y01 = values01->findValueClip(time); - float value + float prev_time = 0.0; + for (size_t i = 0; i <= volt_step_count; i++) { + float volt = volt_step * i; + + float y00 = timeToVoltage(values00, volt); + float y01 = timeToVoltage(values01, volt); + float y10 = timeToVoltage(values10, volt); + float y11 = timeToVoltage(values11, volt); + float time = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 + dx1 * dx2 * y11 + (1 - dx1) * dx2 * y01; - if (i == 0 || abs(value - prev_value) > value_tol) { + if (i == 0 || time > prev_time) { time_values->push_back(time); - values->push_back(value); + values->push_back(volt); } - prev_value = value; + prev_time = time; } return Table1(values, time_axis); } +float +OutputWaveforms::timeToVoltage(const Table1 *waveform, + float voltage) +{ + size_t index1 = findValueIndex(waveform, voltage); + TableAxisPtr axis = waveform->axis1(); + float axis_value1 = axis->axisValue(index1); + float axis_value2 = axis->axisValue(index1 + 1); + + float x1 = voltage; + float x1l = waveform->value(index1); + float x1u = waveform->value(index1 + 1); + float y1 = axis_value1; + float y2 = axis_value2; + float dx1 = (x1 - x1l) / (x1u - x1l); + return (1 - dx1) * y1 + dx1 * y2; +} + +// Bisection search. +// Assumes monotonic table entries +// Copies TableAxis::findAxisIndex. +size_t +OutputWaveforms::findValueIndex(const Table1 *table, + float value) +{ + FloatSeq *values_ = table->values(); + int index_max = static_cast(values_->size()) - 1; + if (value <= (*values_)[0] || index_max == 0) + return 0; + else if (value >= (*values_)[index_max]) + // Return max-1 for value too large so interpolation pts are index,index+1. + return index_max - 1; + else { + int lower = -1; + int upper = index_max + 1; + bool ascend = ((*values_)[index_max] >= (*values_)[0]); + while (upper - lower > 1) { + int mid = (upper + lower) / 2; + if ((value >= (*values_)[mid]) == ascend) + lower = mid; + else + upper = mid; + } + return lower; + } +} + +// Integrate current waveform to find voltage waveform. +// i = C dv/dt Table1 * OutputWaveforms::voltageWaveform(size_t wave_index, float cap) @@ -1698,8 +1797,6 @@ OutputWaveforms::voltageWaveform(size_t wave_index, voltages = new Table1(voltages1, currents->axis1()); voltage_waveforms_[wave_index] = voltages; - // i = C dv/dt - // Integrate current waveform to find voltage waveform. TableAxisPtr time_axis = currents->axis1(); float prev_time = time_axis->axisValue(0); float prev_current = currents->value(0); @@ -1724,82 +1821,6 @@ OutputWaveforms::voltageWaveform(size_t wave_index, return voltages; } -Table1 -OutputWaveforms::currentWaveform(float slew, - float cap) -{ - size_t slew_index = slew_axis_->findAxisIndex(slew); - size_t cap_index = cap_axis_->findAxisIndex(cap); - size_t wave_index00 = slew_index * slew_axis_->size() + cap_index; - size_t wave_index01 = slew_index * slew_axis_->size() + (cap_index + 1); - size_t wave_index10 = (slew_index + 1) * slew_axis_->size() + cap_index; - size_t wave_index11 = (slew_index + 1) * slew_axis_->size() + (cap_index + 1); - const Table1 *values00 = current_waveforms_[wave_index00]; - const Table1 *values01 = current_waveforms_[wave_index01]; - const Table1 *values10 = current_waveforms_[wave_index10]; - const Table1 *values11 = current_waveforms_[wave_index11]; - TableAxisPtr time_axis00 = values00->axis1(); - TableAxisPtr time_axis01 = values01->axis1(); - TableAxisPtr time_axis10 = values10->axis1(); - TableAxisPtr time_axis11 = values11->axis1(); - - // Find time axis min/max. - size_t time_step_count = 20; - float time_min = time_axis00->min(); - time_min = min(time_min, time_axis01->min()); - time_min = min(time_min, time_axis10->min()); - time_min = min(time_min, time_axis11->min()); - float time_max = time_axis00->max(); - time_max = max(time_max, time_axis01->max()); - time_max = max(time_max, time_axis10->max()); - time_max = max(time_max, time_axis11->max()); - float time_step = (time_max - time_min) / time_step_count; - FloatSeq *time_values = new FloatSeq; - TableAxisPtr time_axis = make_shared(time_axis00->variable(), - time_values); - - // Interpolate waveform samples at time steps. - size_t index1 = slew_index; - size_t index2 = cap_index; - float x1 = slew; - float x2 = cap; - float x1l = slew_axis_->axisValue(index1); - float x1u = slew_axis_->axisValue(index1 + 1); - float dx1 = (x1 - x1l) / (x1u - x1l); - float x2l = cap_axis_->axisValue(index2); - float x2u = cap_axis_->axisValue(index2 + 1); - float dx2 = (x2 - x2l) / (x2u - x2l); - FloatSeq *values = new FloatSeq; - float prev_value = 0.0; - constexpr float value_tol = 1e-6; - for (size_t i = 0; i <= time_step_count; i++) { - float time = time_min + time_step * i; - if (time > time_max) - break; - float y00 = values00->findValueClip(time); - float y10 = values10->findValueClip(time); - float y11 = values11->findValueClip(time); - float y01 = values01->findValueClip(time); - float value - = (1 - dx1) * (1 - dx2) * y00 - + dx1 * (1 - dx2) * y10 - + dx1 * dx2 * y11 - + (1 - dx1) * dx2 * y01; - if (i == 0 || abs(value - prev_value) > value_tol) { - time_values->push_back(time); - values->push_back(value); - } - prev_value = value; - } - return Table1(values, time_axis); -} - -float -OutputWaveforms::referenceTime(float slew) -{ - return ref_times_->findValue(slew); -} - //////////////////////////////////////////////////////////////// DriverWaveform::DriverWaveform(const char *name, diff --git a/search/WritePathSpice.cc b/search/WritePathSpice.cc index b9f1fcd4..5cf43181 100644 --- a/search/WritePathSpice.cc +++ b/search/WritePathSpice.cc @@ -1464,7 +1464,7 @@ WritePathSpice::writeStagePiElmore(Pin *drvr_pin, float c2, rpi, c1; parasitics_->piModel(parasitic, c2, rpi, c1); const char *c1_node = "n1"; - streamPrint(spice_stream_, "Rpi %s %s %.3e\n", + streamPrint(spice_stream_, "RPI %s %s %.3e\n", network_->pathName(drvr_pin), c1_node, rpi); diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index a50f8aba..0dd0b32a 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -909,24 +909,26 @@ using namespace sta; %typemap(out) Table1 { Table1 &table = $1; - Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr); - Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr); - for (float f : *table.axis1()->values()) { - Tcl_Obj *obj = Tcl_NewDoubleObj(f); - Tcl_ListObjAppendElement(interp, list1, obj); + if (table.axis1()) { + Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr); + Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr); + for (float f : *table.axis1()->values()) { + Tcl_Obj *obj = Tcl_NewDoubleObj(f); + Tcl_ListObjAppendElement(interp, list1, obj); + } + Tcl_Obj *list2 = Tcl_NewListObj(0, nullptr); + for (float f : *table.values()) { + Tcl_Obj *obj = Tcl_NewDoubleObj(f); + Tcl_ListObjAppendElement(interp, list2, obj); + } + Tcl_ListObjAppendElement(interp, list3, list1); + Tcl_ListObjAppendElement(interp, list3, list2); + Tcl_SetObjResult(interp, list3); } - Tcl_Obj *list2 = Tcl_NewListObj(0, nullptr); - for (float f : *table.values()) { - Tcl_Obj *obj = Tcl_NewDoubleObj(f); - Tcl_ListObjAppendElement(interp, list2, obj); - } - Tcl_ListObjAppendElement(interp, list3, list1); - Tcl_ListObjAppendElement(interp, list3, list2); - Tcl_SetObjResult(interp, list3); } -%typemap(out) Table1* { - Table1 *table = $1; +%typemap(out) const Table1* { + const Table1 *table = $1; Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr); if (table) { Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr); @@ -5708,7 +5710,7 @@ voltage_waveform(float in_slew, return Table1(); } -Table1 +const Table1 * current_waveform(float in_slew, float load_cap) { @@ -5716,11 +5718,11 @@ current_waveform(float in_slew, if (gate_model) { OutputWaveforms *waveforms = gate_model->outputWaveforms(); if (waveforms) { - Table1 waveform = waveforms->currentWaveform(in_slew, load_cap); + const Table1 *waveform = waveforms->currentWaveform(in_slew, load_cap); return waveform; } } - return Table1(); + return nullptr; } } // TimingArc methods From 7f82c152d9af3ce92e362eeda69d6c960b287bff Mon Sep 17 00:00:00 2001 From: James Cherry Date: Fri, 5 May 2023 10:21:12 -0700 Subject: [PATCH 12/24] write spice trans Signed-off-by: James Cherry --- search/WritePathSpice.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/search/WritePathSpice.cc b/search/WritePathSpice.cc index 5cf43181..6aac2fa3 100644 --- a/search/WritePathSpice.cc +++ b/search/WritePathSpice.cc @@ -386,7 +386,7 @@ WritePathSpice::writeHeader() streamPrint(spice_stream_, ".include \"%s\"\n", subckt_filename_stem.c_str()); float max_time = maxTime(); - float time_step = max_time / 1e+3; + float time_step = 1e-13; streamPrint(spice_stream_, ".tran %.3g %.3g\n\n", time_step, max_time); streamPrint(spice_stream_, ".options nomod\n"); From ffec2a1db080142f1050b2a255e74dc3fc38dcc6 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Sat, 6 May 2023 15:34:41 -0700 Subject: [PATCH 13/24] ccs table model Signed-off-by: James Cherry --- include/sta/TableModel.hh | 21 +++-- liberty/TableModel.cc | 188 +++++++++++++------------------------- 2 files changed, 73 insertions(+), 136 deletions(-) diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 33d92236..144f764d 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -507,21 +507,20 @@ public: const RiseFall *rf() const { return rf_; } Table1 voltageWaveform(float in_slew, float load_cap); - float timeToVoltage(float in_slew, - float load_cap, - float voltage); + float voltageTime(float in_slew, + float load_cap, + float voltage); const Table1 *currentWaveform(float slew, float cap); float referenceTime(float slew); static bool checkAxes(TableTemplate *tbl_template); private: - float timeToVoltage(const Table1 *waveform, - float voltage); - size_t findValueIndex(const Table1 *table, - float value); - Table1 *voltageWaveform(size_t wave_index, - float cap); + float voltageTime1(float voltage, + size_t wave_index, + float cap); + FloatSeq *voltageTimes(size_t wave_index, + float cap); // Row. TableAxisPtr slew_axis_; @@ -529,8 +528,10 @@ private: TableAxisPtr cap_axis_; const RiseFall *rf_; Table1Seq current_waveforms_; - Table1Seq voltage_waveforms_; + FloatTable voltage_times_; Table1 *ref_times_; + float voltage_max_; + static constexpr size_t voltage_waveform_step_count_ = 20; }; class DriverWaveform diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 7c97e66d..605f9086 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -1586,7 +1586,7 @@ OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis, cap_axis_(cap_axis), rf_(rf), current_waveforms_(current_waveforms), - voltage_waveforms_(current_waveforms.size()), + voltage_times_(current_waveforms.size()), ref_times_(ref_times) { } @@ -1594,7 +1594,7 @@ OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis, OutputWaveforms::~OutputWaveforms() { current_waveforms_.deleteContents(); - voltage_waveforms_.deleteContents(); + voltage_times_.deleteContents(); delete ref_times_; } @@ -1632,9 +1632,9 @@ OutputWaveforms::referenceTime(float slew) } float -OutputWaveforms::timeToVoltage(float slew, - float cap, - float volt) +OutputWaveforms::voltageTime(float slew, + float cap, + float volt) { size_t slew_index = slew_axis_->findAxisIndex(slew); size_t cap_index = cap_axis_->findAxisIndex(cap); @@ -1645,12 +1645,6 @@ OutputWaveforms::timeToVoltage(float slew, size_t wave_index11 = (slew_index + 1) * slew_count + (cap_index + 1); float cap0 = cap_axis_->axisValue(cap_index); float cap1 = cap_axis_->axisValue(cap_index + 1); - const Table1 *values00 = voltageWaveform(wave_index00, cap0); - const Table1 *values01 = voltageWaveform(wave_index01, cap1); - const Table1 *values10 = voltageWaveform(wave_index10, cap0); - const Table1 *values11 = voltageWaveform(wave_index11, cap1); - - TableAxisPtr time_axis00 = values00->axis1(); // Interpolate waveform samples at voltage steps. size_t index1 = slew_index; @@ -1664,10 +1658,10 @@ OutputWaveforms::timeToVoltage(float slew, float x2u = cap_axis_->axisValue(index2 + 1); float dx2 = (x2 - x2l) / (x2u - x2l); - float y00 = timeToVoltage(values00, volt); - float y01 = timeToVoltage(values01, volt); - float y10 = timeToVoltage(values10, volt); - float y11 = timeToVoltage(values11, volt); + float y00 = voltageTime1(volt, wave_index00, cap0); + float y01 = voltageTime1(volt, wave_index01, cap1); + float y10 = voltageTime1(volt, wave_index10, cap0); + float y11 = voltageTime1(volt, wave_index11, cap1); float time = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 @@ -1680,128 +1674,49 @@ Table1 OutputWaveforms::voltageWaveform(float slew, float cap) { - size_t slew_index = slew_axis_->findAxisIndex(slew); - size_t cap_index = cap_axis_->findAxisIndex(cap); - size_t slew_count = slew_axis_->size(); - size_t wave_index00 = slew_index * slew_count + cap_index; - size_t wave_index01 = slew_index * slew_count + (cap_index + 1); - size_t wave_index10 = (slew_index + 1) * slew_count + cap_index; - size_t wave_index11 = (slew_index + 1) * slew_count + (cap_index + 1); - float cap0 = cap_axis_->axisValue(cap_index); - float cap1 = cap_axis_->axisValue(cap_index + 1); - const Table1 *values00 = voltageWaveform(wave_index00, cap0); - const Table1 *values01 = voltageWaveform(wave_index01, cap1); - const Table1 *values10 = voltageWaveform(wave_index10, cap0); - const Table1 *values11 = voltageWaveform(wave_index11, cap1); - - TableAxisPtr time_axis00 = values00->axis1(); - - size_t volt_step_count = 20; - float volt_max = values00->value(time_axis00->size() - 1); - float volt_step = volt_max / volt_step_count; - FloatSeq *time_values = new FloatSeq; - TableAxisPtr time_axis = make_shared(time_axis00->variable(), - time_values); - - // Interpolate waveform samples at voltage steps. - size_t index1 = slew_index; - size_t index2 = cap_index; - float x1 = slew; - float x2 = cap; - float x1l = slew_axis_->axisValue(index1); - float x1u = slew_axis_->axisValue(index1 + 1); - float dx1 = (x1 - x1l) / (x1u - x1l); - float x2l = cap_axis_->axisValue(index2); - float x2u = cap_axis_->axisValue(index2 + 1); - float dx2 = (x2 - x2l) / (x2u - x2l); - FloatSeq *values = new FloatSeq; - float prev_time = 0.0; - for (size_t i = 0; i <= volt_step_count; i++) { - float volt = volt_step * i; - - float y00 = timeToVoltage(values00, volt); - float y01 = timeToVoltage(values01, volt); - float y10 = timeToVoltage(values10, volt); - float y11 = timeToVoltage(values11, volt); - float time - = (1 - dx1) * (1 - dx2) * y00 - + dx1 * (1 - dx2) * y10 - + dx1 * dx2 * y11 - + (1 - dx1) * dx2 * y01; - if (i == 0 || time > prev_time) { - time_values->push_back(time); - values->push_back(volt); - } - prev_time = time; + float volt_step = voltage_max_ / voltage_waveform_step_count_; + FloatSeq *times = new FloatSeq; + FloatSeq *volts = new FloatSeq; + for (size_t v = 0; v <= voltage_waveform_step_count_; v++) { + float volt = v * volt_step; + float time = voltageTime(slew, cap, volt); + times->push_back(time); + volts->push_back(volt); } - return Table1(values, time_axis); + TableAxisPtr time_axis = make_shared(TableAxisVariable::time, times); + return Table1(volts, time_axis); } float -OutputWaveforms::timeToVoltage(const Table1 *waveform, - float voltage) +OutputWaveforms::voltageTime1(float voltage, + size_t wave_index, + float cap) { - size_t index1 = findValueIndex(waveform, voltage); - TableAxisPtr axis = waveform->axis1(); - float axis_value1 = axis->axisValue(index1); - float axis_value2 = axis->axisValue(index1 + 1); - - float x1 = voltage; - float x1l = waveform->value(index1); - float x1u = waveform->value(index1 + 1); - float y1 = axis_value1; - float y2 = axis_value2; - float dx1 = (x1 - x1l) / (x1u - x1l); - return (1 - dx1) * y1 + dx1 * y2; -} - -// Bisection search. -// Assumes monotonic table entries -// Copies TableAxis::findAxisIndex. -size_t -OutputWaveforms::findValueIndex(const Table1 *table, - float value) -{ - FloatSeq *values_ = table->values(); - int index_max = static_cast(values_->size()) - 1; - if (value <= (*values_)[0] || index_max == 0) - return 0; - else if (value >= (*values_)[index_max]) - // Return max-1 for value too large so interpolation pts are index,index+1. - return index_max - 1; - else { - int lower = -1; - int upper = index_max + 1; - bool ascend = ((*values_)[index_max] >= (*values_)[0]); - while (upper - lower > 1) { - int mid = (upper + lower) / 2; - if ((value >= (*values_)[mid]) == ascend) - lower = mid; - else - upper = mid; - } - return lower; - } + FloatSeq *voltage_times = voltageTimes(wave_index, cap); + float volt_step = voltage_max_ / voltage_waveform_step_count_; + size_t volt_idx = voltage / volt_step; + float time0 = (*voltage_times)[volt_idx]; + float time1 = (*voltage_times)[volt_idx + 1]; + float time = time0 + (time1 - time0) * (voltage - volt_step * volt_idx); + return time; } // Integrate current waveform to find voltage waveform. // i = C dv/dt -Table1 * -OutputWaveforms::voltageWaveform(size_t wave_index, - float cap) +FloatSeq * +OutputWaveforms::voltageTimes(size_t wave_index, + float cap) { - Table1 *voltages = voltage_waveforms_[wave_index]; - if (voltages == nullptr) { - FloatSeq *voltages1 = new FloatSeq; + FloatSeq *voltage_times = voltage_times_[wave_index]; + if (voltage_times == nullptr) { + FloatSeq volts; Table1 *currents = current_waveforms_[wave_index]; - voltages = new Table1(voltages1, currents->axis1()); - voltage_waveforms_[wave_index] = voltages; TableAxisPtr time_axis = currents->axis1(); float prev_time = time_axis->axisValue(0); float prev_current = currents->value(0); float voltage = 0.0; - voltages1->push_back(voltage); + volts.push_back(voltage); bool always_rise = true; bool invert = (always_rise && rf_ == RiseFall::fall()); for (size_t i = 1; i < time_axis->size(); i++) { @@ -1809,16 +1724,37 @@ OutputWaveforms::voltageWaveform(size_t wave_index, float current = currents->value(i); float dv = (current + prev_current) / 2.0 * (time - prev_time) / cap; voltage += invert ? -dv : dv; - voltages1->push_back(voltage); + volts.push_back(voltage); prev_time = time; prev_current = current; } - if (!always_rise && rf_ == RiseFall::fall()) { - for (size_t i = 0; i < voltages1->size(); i++) - (*voltages1)[i] -= voltage; + voltage_max_ = volts[volts.size() - 1]; + + // Sample voltage waveform a uniform intervals to speed up time to voltage lookup. + voltage_times = new FloatSeq; + float volt_step = voltage_max_ / voltage_waveform_step_count_; + size_t i = 0; + float time0 = time_axis->axisValue(i); + float volt0 = volts[i]; + i = 1; + float time1 = time_axis->axisValue(i); + float volt1 = volts[i]; + for (size_t v = 0; v <= voltage_waveform_step_count_; v++) { + float volt3 = v * volt_step; + while (volt3 > volt1 && i < volts.size()) { + time0 = time1; + volt0 = volt1; + i++; + time1 = time_axis->axisValue(i); + volt1 = volts[i]; + } + float time3 = time0 + (time1 - time0) * (volt3 - volt0) / (volt1 - volt0); + //printf("%.2f %.2e\n", volt3, time3); + voltage_times->push_back(time3); } + voltage_times_[wave_index] = voltage_times; } - return voltages; + return voltage_times; } //////////////////////////////////////////////////////////////// From 4f013dabd741570c7aa3b6e817b2b2d59801b26c Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 8 May 2023 11:02:10 -0700 Subject: [PATCH 14/24] ccs Signed-off-by: James Cherry --- dcalc/DmpDelayCalc.cc | 3 ++- dcalc/LumpedCapDelayCalc.hh | 2 +- dcalc/RCDelayCalc.cc | 14 ++++---------- include/sta/ArcDelayCalc.hh | 1 + liberty/TableModel.cc | 4 +--- 5 files changed, 9 insertions(+), 15 deletions(-) diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index 4d5d4b14..973b20d3 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -24,6 +24,7 @@ #include "DcalcAnalysisPt.hh" #include "GraphDelayCalc.hh" #include "DmpCeff.hh" +#include "Network.hh" namespace sta { @@ -302,7 +303,7 @@ DmpCeffTwoPoleDelayCalc::loadDelay(const Pin *load_pin, ArcDelay &wire_delay, Slew &load_slew) { - // NEED to handle PiElmore parasitic. + // Should handle PiElmore parasitic. ArcDelay wire_delay1 = 0.0; Slew load_slew1 = drvr_slew_; Parasitic *pole_residue = 0; diff --git a/dcalc/LumpedCapDelayCalc.hh b/dcalc/LumpedCapDelayCalc.hh index 0f502e33..ff92dea4 100644 --- a/dcalc/LumpedCapDelayCalc.hh +++ b/dcalc/LumpedCapDelayCalc.hh @@ -47,7 +47,7 @@ public: // Return values. ArcDelay &gate_delay, Slew &drvr_slew) override; - void setMultiDrvrSlewFactor(float) override; + void setMultiDrvrSlewFactor(float factor) override; float ceff(const LibertyCell *drvr_cell, const TimingArc *arc, const Slew &in_slew, diff --git a/dcalc/RCDelayCalc.cc b/dcalc/RCDelayCalc.cc index 3aec6224..5f852e14 100644 --- a/dcalc/RCDelayCalc.cc +++ b/dcalc/RCDelayCalc.cc @@ -62,16 +62,10 @@ RCDelayCalc::dspfWireDelaySlew(const Pin *, ArcDelay &wire_delay, Slew &load_slew) { - float vth = .5; - float vl = .2; - float vh = .8; - float slew_derate = 1.0; - if (drvr_library_) { - vth = drvr_library_->inputThreshold(drvr_rf_); - vl = drvr_library_->slewLowerThreshold(drvr_rf_); - vh = drvr_library_->slewUpperThreshold(drvr_rf_); - slew_derate = drvr_library_->slewDerateFromLibrary(); - } + float vth = drvr_library_->inputThreshold(drvr_rf_); + float vl = drvr_library_->slewLowerThreshold(drvr_rf_); + float vh = drvr_library_->slewUpperThreshold(drvr_rf_); + float slew_derate = drvr_library_->slewDerateFromLibrary(); wire_delay = static_cast(-elmore * log(1.0 - vth)); load_slew = (drvr_slew_ + elmore * log((1.0 - vl) / (1.0 - vh)) / slew_derate) * multi_drvr_slew_factor_; diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index 7eb117ef..045557bb 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -40,6 +40,7 @@ class DcalcAnalysisPt; // DmpCeffDelayCalc // DmpCeffElmoreDelayCalc // DmpCeffTwoPoleDelayCalc +// ArnoldiDelayCalc // Abstract class to interface to a delay calculator primitive. class ArcDelayCalc : public StaState diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 605f9086..57c8c446 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -1717,13 +1717,11 @@ OutputWaveforms::voltageTimes(size_t wave_index, float prev_current = currents->value(0); float voltage = 0.0; volts.push_back(voltage); - bool always_rise = true; - bool invert = (always_rise && rf_ == RiseFall::fall()); for (size_t i = 1; i < time_axis->size(); i++) { float time = time_axis->axisValue(i); float current = currents->value(i); float dv = (current + prev_current) / 2.0 * (time - prev_time) / cap; - voltage += invert ? -dv : dv; + voltage += dv; volts.push_back(voltage); prev_time = time; prev_current = current; From 647b3a5fb3d75c1d4389a8c24d78c7aad0ad00e0 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 8 May 2023 14:56:43 -0700 Subject: [PATCH 15/24] ccs table Signed-off-by: James Cherry --- liberty/TableModel.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 57c8c446..605f9086 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -1717,11 +1717,13 @@ OutputWaveforms::voltageTimes(size_t wave_index, float prev_current = currents->value(0); float voltage = 0.0; volts.push_back(voltage); + bool always_rise = true; + bool invert = (always_rise && rf_ == RiseFall::fall()); for (size_t i = 1; i < time_axis->size(); i++) { float time = time_axis->axisValue(i); float current = currents->value(i); float dv = (current + prev_current) / 2.0 * (time - prev_time) / cap; - voltage += dv; + voltage += invert ? -dv : dv; volts.push_back(voltage); prev_time = time; prev_current = current; From 3c4995ce8b4ee22965d617c02e16afe20c3f9dd8 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 8 May 2023 15:16:10 -0700 Subject: [PATCH 16/24] override Signed-Off-by: James Cherry --- dcalc/DmpDelayCalc.cc | 48 +++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index 973b20d3..25fcd3f9 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -126,30 +126,30 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc { public: DmpCeffTwoPoleDelayCalc(StaState *sta); - virtual ArcDelayCalc *copy(); - virtual Parasitic *findParasitic(const Pin *drvr_pin, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap); - virtual ReducedParasiticType reducedParasiticType() const; - virtual void inputPortDelay(const Pin *port_pin, - float in_slew, - const RiseFall *rf, - const Parasitic *parasitic, - const DcalcAnalysisPt *dcalc_ap); - virtual void gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *drvr_parasitic, - float related_out_cap, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew); - virtual void loadDelay(const Pin *load_pin, - ArcDelay &wire_delay, - Slew &load_slew); + ArcDelayCalc *copy() override; + Parasitic *findParasitic(const Pin *drvr_pin, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap) override; + ReducedParasiticType reducedParasiticType() const override; + void inputPortDelay(const Pin *port_pin, + float in_slew, + const RiseFall *rf, + const Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap) override; + void gateDelay(const LibertyCell *drvr_cell, + const TimingArc *arc, + const Slew &in_slew, + float load_cap, + const Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) override; + void loadDelay(const Pin *load_pin, + ArcDelay &wire_delay, + Slew &load_slew) override; private: void loadDelay(Parasitic *pole_residue, From 63df5b3058996691ea0a528d82cd5097a6809387 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 8 May 2023 15:26:10 -0700 Subject: [PATCH 17/24] SimpleRCDelayCalc -> SlewDegradeDelayCalc Signed-off-by: James Cherry --- CMakeLists.txt | 2 +- dcalc/DelayCalc.cc | 4 +- dcalc/SimpleRCDelayCalc.hh | 63 ------------- ...RCDelayCalc.cc => SlewDegradeDelayCalc.cc} | 89 +++++++++++++------ dcalc/SlewDegradeDelayCalc.hh | 26 ++++++ include/sta/ArcDelayCalc.hh | 2 +- 6 files changed, 93 insertions(+), 93 deletions(-) delete mode 100644 dcalc/SimpleRCDelayCalc.hh rename dcalc/{SimpleRCDelayCalc.cc => SlewDegradeDelayCalc.cc} (50%) create mode 100644 dcalc/SlewDegradeDelayCalc.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index 49218a63..0e5353c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,7 @@ set(STA_SOURCE dcalc/LumpedCapDelayCalc.cc dcalc/NetCaps.cc dcalc/RCDelayCalc.cc - dcalc/SimpleRCDelayCalc.cc + dcalc/SlewDegradeDelayCalc.cc dcalc/UnitDelayCalc.cc graph/DelayFloat.cc diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc index f0a89735..2c5c1ba0 100644 --- a/dcalc/DelayCalc.cc +++ b/dcalc/DelayCalc.cc @@ -20,7 +20,7 @@ #include "StringUtil.hh" #include "UnitDelayCalc.hh" #include "LumpedCapDelayCalc.hh" -#include "SimpleRCDelayCalc.hh" +#include "SlewDegradeDelayCalc.hh" #include "DmpDelayCalc.hh" #include "ArnoldiDelayCalc.hh" @@ -35,7 +35,7 @@ registerDelayCalcs() { registerDelayCalc("unit", makeUnitDelayCalc); registerDelayCalc("lumped_cap", makeLumpedCapDelayCalc); - registerDelayCalc("simple_rc", makeSimpleRCDelayCalc); + registerDelayCalc("slew_degrade", makeSlewDegradeDelayCalc); registerDelayCalc("dmp_ceff_elmore", makeDmpCeffElmoreDelayCalc); registerDelayCalc("dmp_ceff_two_pole", makeDmpCeffTwoPoleDelayCalc); registerDelayCalc("arnoldi", makeArnoldiDelayCalc); diff --git a/dcalc/SimpleRCDelayCalc.hh b/dcalc/SimpleRCDelayCalc.hh deleted file mode 100644 index aa2f4452..00000000 --- a/dcalc/SimpleRCDelayCalc.hh +++ /dev/null @@ -1,63 +0,0 @@ -// OpenSTA, Static Timing Analyzer -// Copyright (c) 2023, Parallax Software, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#pragma once - -#include "RCDelayCalc.hh" - -namespace sta { - -// Liberty table model lumped capacitance arc delay calculator. -// Effective capacitance is the pi model total capacitance (C1+C2). -// Wire delays are elmore delays. -// Driver slews are degraded to loads by rise/fall transition_degradation -// tables. -class SimpleRCDelayCalc : public RCDelayCalc -{ -public: - SimpleRCDelayCalc(StaState *sta); - virtual ArcDelayCalc *copy(); - virtual void inputPortDelay(const Pin *port_pin, - float in_slew, - const RiseFall *rf, - const Parasitic *parasitic, - const DcalcAnalysisPt *dcalc_ap); - virtual void gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *drvr_parasitic, - float related_out_cap, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew); - virtual void loadDelay(const Pin *load_pin, - ArcDelay &wire_delay, - Slew &load_slew); - - using RCDelayCalc::gateDelay; - using RCDelayCalc::reportGateDelay; - -private: - const Pvt *pvt_; -}; - -ArcDelayCalc * -makeSimpleRCDelayCalc(StaState *sta); - -} // namespace diff --git a/dcalc/SimpleRCDelayCalc.cc b/dcalc/SlewDegradeDelayCalc.cc similarity index 50% rename from dcalc/SimpleRCDelayCalc.cc rename to dcalc/SlewDegradeDelayCalc.cc index 394e10e5..1e61ebe0 100644 --- a/dcalc/SimpleRCDelayCalc.cc +++ b/dcalc/SlewDegradeDelayCalc.cc @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "SimpleRCDelayCalc.hh" +#include "SlewDegradeDelayCalc.hh" #include "TimingArc.hh" #include "Liberty.hh" @@ -25,46 +25,83 @@ namespace sta { -ArcDelayCalc * -makeSimpleRCDelayCalc(StaState *sta) +// Liberty table model lumped capacitance arc delay calculator. +// Effective capacitance is the pi model total capacitance (C1+C2). +// Wire delays are elmore delays. +// Driver slews are degraded to loads by rise/fall transition_degradation +// tables. +class SlewDegradeDelayCalc : public RCDelayCalc { - return new SimpleRCDelayCalc(sta); +public: + SlewDegradeDelayCalc(StaState *sta); + virtual ArcDelayCalc *copy(); + virtual void inputPortDelay(const Pin *port_pin, + float in_slew, + const RiseFall *rf, + const Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap); + virtual void gateDelay(const LibertyCell *drvr_cell, + const TimingArc *arc, + const Slew &in_slew, + float load_cap, + const Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew); + virtual void loadDelay(const Pin *load_pin, + ArcDelay &wire_delay, + Slew &load_slew); + + using RCDelayCalc::gateDelay; + using RCDelayCalc::reportGateDelay; + +private: + const Pvt *pvt_; +}; + +ArcDelayCalc * +makeSlewDegradeDelayCalc(StaState *sta) +{ + return new SlewDegradeDelayCalc(sta); } -SimpleRCDelayCalc::SimpleRCDelayCalc(StaState *sta) : +SlewDegradeDelayCalc::SlewDegradeDelayCalc(StaState *sta) : RCDelayCalc(sta) { } ArcDelayCalc * -SimpleRCDelayCalc::copy() +SlewDegradeDelayCalc::copy() { - return new SimpleRCDelayCalc(this); + return new SlewDegradeDelayCalc(this); } void -SimpleRCDelayCalc::inputPortDelay(const Pin *port_pin, - float in_slew, - const RiseFall *rf, - const Parasitic *parasitic, - const DcalcAnalysisPt *dcalc_ap) +SlewDegradeDelayCalc::inputPortDelay(const Pin *port_pin, + float in_slew, + const RiseFall *rf, + const Parasitic *parasitic, + const DcalcAnalysisPt *dcalc_ap) { pvt_ = dcalc_ap->operatingConditions(); RCDelayCalc::inputPortDelay(port_pin, in_slew, rf, parasitic, dcalc_ap); } void -SimpleRCDelayCalc::gateDelay(const LibertyCell *drvr_cell, - const TimingArc *arc, - const Slew &in_slew, - float load_cap, - const Parasitic *drvr_parasitic, - float related_out_cap, - const Pvt *pvt, - const DcalcAnalysisPt *dcalc_ap, - // Return values. - ArcDelay &gate_delay, - Slew &drvr_slew) +SlewDegradeDelayCalc::gateDelay(const LibertyCell *drvr_cell, + const TimingArc *arc, + const Slew &in_slew, + float load_cap, + const Parasitic *drvr_parasitic, + float related_out_cap, + const Pvt *pvt, + const DcalcAnalysisPt *dcalc_ap, + // Return values. + ArcDelay &gate_delay, + Slew &drvr_slew) { input_port_ = false; drvr_parasitic_ = drvr_parasitic; @@ -79,9 +116,9 @@ SimpleRCDelayCalc::gateDelay(const LibertyCell *drvr_cell, } void -SimpleRCDelayCalc::loadDelay(const Pin *load_pin, - ArcDelay &wire_delay, - Slew &load_slew) +SlewDegradeDelayCalc::loadDelay(const Pin *load_pin, + ArcDelay &wire_delay, + Slew &load_slew) { ArcDelay wire_delay1 = 0.0; Slew load_slew1 = drvr_slew_; diff --git a/dcalc/SlewDegradeDelayCalc.hh b/dcalc/SlewDegradeDelayCalc.hh new file mode 100644 index 00000000..97b39c5f --- /dev/null +++ b/dcalc/SlewDegradeDelayCalc.hh @@ -0,0 +1,26 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2023, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include "RCDelayCalc.hh" + +namespace sta { + +ArcDelayCalc * +makeSlewDegradeDelayCalc(StaState *sta); + +} // namespace diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index 045557bb..420f5951 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -36,7 +36,7 @@ class DcalcAnalysisPt; // UnitDelayCalc // LumpedCapDelayCalc // RCDelayCalc -// SimpleRCDelayCalc +// SlewDegradeDelayCalc // DmpCeffDelayCalc // DmpCeffElmoreDelayCalc // DmpCeffTwoPoleDelayCalc From c2e2846f4ca4175da8d3f15532d21fc0447eb555 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 8 May 2023 15:28:25 -0700 Subject: [PATCH 18/24] comment Signed-off-by: James Cherry --- dcalc/ArnoldiDelayCalc.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index 6afcaf20..368740e0 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -439,6 +439,7 @@ ArnoldiDelayCalc::loadDelay(const Pin *load_pin, ArcDelay &wire_delay, Slew &load_slew) { + // This does not appear to handle input port parasitics correctly. wire_delay = 0.0; load_slew = drvr_slew_ * multi_drvr_slew_factor_; if (rcmodel_) { From 9bda639d9cf83c490d4dbec04b507b4255db183b Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 9 May 2023 19:42:53 -0700 Subject: [PATCH 19/24] ccs waveform vdd Signed-off-by: James Cherry --- include/sta/TableModel.hh | 3 ++- liberty/TableModel.cc | 33 +++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 144f764d..9e4abffe 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -513,6 +513,7 @@ public: const Table1 *currentWaveform(float slew, float cap); float referenceTime(float slew); + void setVdd(float vdd); static bool checkAxes(TableTemplate *tbl_template); private: @@ -530,7 +531,7 @@ private: Table1Seq current_waveforms_; FloatTable voltage_times_; Table1 *ref_times_; - float voltage_max_; + float vdd_; static constexpr size_t voltage_waveform_step_count_ = 20; }; diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 605f9086..0fe68ee8 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -1572,9 +1572,6 @@ tableVariableUnit(TableAxisVariable variable, return nullptr; } -//////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////// OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis, @@ -1587,7 +1584,8 @@ OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis, rf_(rf), current_waveforms_(current_waveforms), voltage_times_(current_waveforms.size()), - ref_times_(ref_times) + ref_times_(ref_times), + vdd_(0.0) { } @@ -1631,6 +1629,12 @@ OutputWaveforms::referenceTime(float slew) return ref_times_->findValue(slew); } +void +OutputWaveforms::setVdd(float vdd) +{ + vdd_ = vdd; +} + float OutputWaveforms::voltageTime(float slew, float cap, @@ -1663,7 +1667,7 @@ OutputWaveforms::voltageTime(float slew, float y10 = voltageTime1(volt, wave_index10, cap0); float y11 = voltageTime1(volt, wave_index11, cap1); float time - = (1 - dx1) * (1 - dx2) * y00 + = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 + dx1 * dx2 * y11 + (1 - dx1) * dx2 * y01; @@ -1674,7 +1678,7 @@ Table1 OutputWaveforms::voltageWaveform(float slew, float cap) { - float volt_step = voltage_max_ / voltage_waveform_step_count_; + float volt_step = vdd_ / voltage_waveform_step_count_; FloatSeq *times = new FloatSeq; FloatSeq *volts = new FloatSeq; for (size_t v = 0; v <= voltage_waveform_step_count_; v++) { @@ -1693,7 +1697,7 @@ OutputWaveforms::voltageTime1(float voltage, float cap) { FloatSeq *voltage_times = voltageTimes(wave_index, cap); - float volt_step = voltage_max_ / voltage_waveform_step_count_; + float volt_step = vdd_ / voltage_waveform_step_count_; size_t volt_idx = voltage / volt_step; float time0 = (*voltage_times)[volt_idx]; float time1 = (*voltage_times)[volt_idx + 1]; @@ -1701,17 +1705,16 @@ OutputWaveforms::voltageTime1(float voltage, return time; } -// Integrate current waveform to find voltage waveform. -// i = C dv/dt FloatSeq * OutputWaveforms::voltageTimes(size_t wave_index, float cap) { FloatSeq *voltage_times = voltage_times_[wave_index]; if (voltage_times == nullptr) { + // Integrate current waveform to find voltage waveform. + // i = C dv/dt FloatSeq volts; Table1 *currents = current_waveforms_[wave_index]; - TableAxisPtr time_axis = currents->axis1(); float prev_time = time_axis->axisValue(0); float prev_current = currents->value(0); @@ -1728,11 +1731,11 @@ OutputWaveforms::voltageTimes(size_t wave_index, prev_time = time; prev_current = current; } - voltage_max_ = volts[volts.size() - 1]; - // Sample voltage waveform a uniform intervals to speed up time to voltage lookup. + // Sample the voltage waveform at uniform intervals to speed up + // voltage time lookup. voltage_times = new FloatSeq; - float volt_step = voltage_max_ / voltage_waveform_step_count_; + float volt_step = vdd_ / voltage_waveform_step_count_; size_t i = 0; float time0 = time_axis->axisValue(i); float volt0 = volts[i]; @@ -1741,7 +1744,7 @@ OutputWaveforms::voltageTimes(size_t wave_index, float volt1 = volts[i]; for (size_t v = 0; v <= voltage_waveform_step_count_; v++) { float volt3 = v * volt_step; - while (volt3 > volt1 && i < volts.size()) { + while (volt3 > volt1 && i < volts.size() - 1) { time0 = time1; volt0 = volt1; i++; @@ -1749,6 +1752,8 @@ OutputWaveforms::voltageTimes(size_t wave_index, volt1 = volts[i]; } float time3 = time0 + (time1 - time0) * (volt3 - volt0) / (volt1 - volt0); + if (time3 < 0.0) + printf("luse\n"); //printf("%.2f %.2e\n", volt3, time3); voltage_times->push_back(time3); } From e7baf16407fbe5b4b43b8e6c5cb2c30ec25823f9 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 10 May 2023 09:03:17 -0700 Subject: [PATCH 20/24] ccs bounds check Signed-off-by: James Cherry --- include/sta/TableModel.hh | 3 +++ liberty/TableModel.cc | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 9e4abffe..a713bfe4 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -462,6 +462,7 @@ public: const char *variableString() const; const Unit *unit(const Units *units); size_t size() const { return values_->size(); } + bool inBounds(float value) const; float axisValue(size_t index) const { return (*values_)[index]; } // Find the index for value such that axis[index] <= value < axis[index+1]. size_t findAxisIndex(float value) const; @@ -505,6 +506,8 @@ public: Table1 *ref_times); ~OutputWaveforms(); const RiseFall *rf() const { return rf_; } + bool inBounds(float in_slew, + float load_cap) const; Table1 voltageWaveform(float in_slew, float load_cap); float voltageTime(float in_slew, diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 0fe68ee8..48db282a 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -1437,6 +1437,15 @@ TableAxis::~TableAxis() delete values_; } +bool +TableAxis::inBounds(float value) const +{ + size_t size = values_->size(); + return size > 1 + && value >= (*values_)[0] + && value <= (*values_)[size - 1]; +} + // Bisection search. size_t TableAxis::findAxisIndex(float value) const @@ -1613,6 +1622,14 @@ OutputWaveforms::checkAxes(TableTemplate *tbl_template) && axis3->variable() == TableAxisVariable::time); } +bool +OutputWaveforms::inBounds(float in_slew, + float load_cap) const +{ + return slew_axis_->inBounds(in_slew) + && cap_axis_->inBounds(load_cap); +} + const Table1 * OutputWaveforms::currentWaveform(float slew, float cap) From 21de3cf383679f2a5adbe911613a079c1fe6372a Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 10 May 2023 09:07:48 -0700 Subject: [PATCH 21/24] ssta compile Signed-off-by: James Cherry --- search/WritePathSpice.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/search/WritePathSpice.cc b/search/WritePathSpice.cc index 6aac2fa3..dc95c363 100644 --- a/search/WritePathSpice.cc +++ b/search/WritePathSpice.cc @@ -432,18 +432,18 @@ WritePathSpice::pathMaxTime() PathRef *path = path_expanded_.path(i); const RiseFall *rf = path->transition(this); Vertex *vertex = path->vertex(this); - Slew path_max_slew = railToRailSlew(findSlew(vertex, rf, nullptr, dcalc_ap_index),rf); + float path_max_slew = railToRailSlew(findSlew(vertex,rf,nullptr,dcalc_ap_index),rf); if (vertex->isDriver(network_)) { VertexOutEdgeIterator edge_iter(vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *load = edge->to(graph_); - Slew load_slew = railToRailSlew(findSlew(load, rf, nullptr, dcalc_ap_index),rf); + float load_slew = railToRailSlew(findSlew(load, rf, nullptr, dcalc_ap_index),rf); if (load_slew > path_max_slew) path_max_slew = load_slew; } } - float path_max_time = path->arrival(this) + path_max_slew * 2.0; + float path_max_time = delayAsFloat(path->arrival(this)) + path_max_slew * 2.0; if (path_max_time > max_time) max_time = path_max_time; } From 9cedf395ad77c6af1fed7f098a41423156755d18 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 10 May 2023 10:02:48 -0700 Subject: [PATCH 22/24] liberty rm pulling resistance unit Signed-off-by: James Cherry --- include/sta/Units.hh | 3 --- liberty/LibertyReader.cc | 13 ++----------- liberty/LibertyReaderPvt.hh | 1 - liberty/Units.cc | 1 - search/MakeTimingModel.cc | 1 - 5 files changed, 2 insertions(+), 17 deletions(-) diff --git a/include/sta/Units.hh b/include/sta/Units.hh index 4e6f5e1f..61355954 100644 --- a/include/sta/Units.hh +++ b/include/sta/Units.hh @@ -72,8 +72,6 @@ public: const Unit *voltageUnit() const { return &voltage_unit_; } Unit *resistanceUnit() { return &resistance_unit_; } const Unit *resistanceUnit() const { return &resistance_unit_; } - Unit *pullingResistanceUnit() { return &pulling_resistance_unit_; } - const Unit *pullingResistanceUnit() const {return &pulling_resistance_unit_;} Unit *currentUnit() { return ¤t_unit_; } const Unit *currentUnit() const { return ¤t_unit_; } Unit *powerUnit() { return &power_unit_; } @@ -88,7 +86,6 @@ private: Unit capacitance_unit_; Unit voltage_unit_; Unit resistance_unit_; - Unit pulling_resistance_unit_; Unit current_unit_; Unit power_unit_; Unit distance_unit_; diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 741ab864..ad0947eb 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -134,7 +134,6 @@ LibertyReader::readLibertyFile(const char *filename, mode_value_ = nullptr; ocv_derate_ = nullptr; pg_port_ = nullptr; - have_resistance_unit_ = false; default_operating_condition_ = nullptr; receiver_model_ = nullptr; @@ -648,12 +647,6 @@ LibertyReader::endLibrary(LibertyGroup *group) void LibertyReader::endLibraryAttrs(LibertyGroup *group) { - // Default resistance_unit to pulling_resistance_unit. - if (!have_resistance_unit_) { - Units *units = library_->units(); - *units->resistanceUnit() = *units->pullingResistanceUnit(); - } - // These attributes reference named groups in the library so // wait until the end of the library to resolve them. if (default_wireload_) { @@ -727,16 +720,14 @@ LibertyReader::visitPullingResistanceUnit(LibertyAttr *attr) { if (library_) parseUnits(attr, "ohm", res_scale_, - library_->units()->pullingResistanceUnit()); + library_->units()->resistanceUnit()); } void LibertyReader::visitResistanceUnit(LibertyAttr *attr) { - if (library_) { + if (library_) parseUnits(attr, "ohm", res_scale_, library_->units()->resistanceUnit()); - have_resistance_unit_ = true; - } } void diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 4a5cf678..eadc990b 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -624,7 +624,6 @@ protected: float power_scale_; float energy_scale_; float distance_scale_; - bool have_resistance_unit_; const char *default_operating_condition_; ReceiverModelPtr receiver_model_; OutputWaveformSeq output_currents_; diff --git a/liberty/Units.cc b/liberty/Units.cc index 9ff90d79..91d13705 100644 --- a/liberty/Units.cc +++ b/liberty/Units.cc @@ -168,7 +168,6 @@ Units::Units() : capacitance_unit_("F"), voltage_unit_("v"), resistance_unit_("ohm"), - pulling_resistance_unit_("ohm"), current_unit_("A"), power_unit_("W"), distance_unit_("m"), diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index ecf8d0ea..fa168456 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -133,7 +133,6 @@ MakeTimingModel::makeLibrary() *library_->units()->capacitanceUnit() = *default_lib->units()->capacitanceUnit(); *library_->units()->voltageUnit() = *default_lib->units()->voltageUnit(); *library_->units()->resistanceUnit() = *default_lib->units()->resistanceUnit(); - *library_->units()->pullingResistanceUnit() = *default_lib->units()->pullingResistanceUnit(); *library_->units()->powerUnit() = *default_lib->units()->powerUnit(); *library_->units()->distanceUnit() = *default_lib->units()->distanceUnit(); From f7f2b0cadff9a21fb2cb905bb2cd44318ba05747 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 10 May 2023 10:12:43 -0700 Subject: [PATCH 23/24] leak Signed-off-by: James Cherry --- include/sta/Units.hh | 2 +- liberty/Units.cc | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/sta/Units.hh b/include/sta/Units.hh index 61355954..d8b1b509 100644 --- a/include/sta/Units.hh +++ b/include/sta/Units.hh @@ -83,9 +83,9 @@ public: private: Unit time_unit_; + Unit resistance_unit_; Unit capacitance_unit_; Unit voltage_unit_; - Unit resistance_unit_; Unit current_unit_; Unit power_unit_; Unit distance_unit_; diff --git a/liberty/Units.cc b/liberty/Units.cc index 91d13705..ecd17846 100644 --- a/liberty/Units.cc +++ b/liberty/Units.cc @@ -67,6 +67,7 @@ Unit::operator=(const Unit &unit) scale_ = unit.scale_; stringDelete(suffix_); suffix_ = stringCopy(unit.suffix_); + stringDelete(scaled_suffix_); scaled_suffix_ = stringCopy(unit.scaled_suffix_); digits_ = unit.digits_; } @@ -165,9 +166,9 @@ Unit::asString(float value, Units::Units() : time_unit_("s"), + resistance_unit_("ohm"), capacitance_unit_("F"), voltage_unit_("v"), - resistance_unit_("ohm"), current_unit_("A"), power_unit_("W"), distance_unit_("m"), @@ -180,10 +181,10 @@ Units::find(const char *unit_name) { if (stringEq(unit_name, "time")) return &time_unit_; - else if (stringEq(unit_name, "capacitance")) - return &capacitance_unit_; else if (stringEq(unit_name, "resistance")) return &resistance_unit_; + else if (stringEq(unit_name, "capacitance")) + return &capacitance_unit_; else if (stringEq(unit_name, "voltage")) return &voltage_unit_; else if (stringEq(unit_name, "current")) @@ -200,8 +201,8 @@ void Units::operator=(const Units &units) { time_unit_ = *units.timeUnit(); - capacitance_unit_ = *units.capacitanceUnit(); resistance_unit_ = *units.resistanceUnit(); + capacitance_unit_ = *units.capacitanceUnit(); voltage_unit_ = *units.voltageUnit(); current_unit_ = *units.currentUnit(); power_unit_ = *units.powerUnit(); From 358fb135a5273b7e49acc7f96aa09055b4889816 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 15 May 2023 18:22:11 -0700 Subject: [PATCH 24/24] write_verilog wire for buses Signed-off-by: James Cherry --- verilog/VerilogWriter.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/verilog/VerilogWriter.cc b/verilog/VerilogWriter.cc index ca6a9478..7c407921 100644 --- a/verilog/VerilogWriter.cc +++ b/verilog/VerilogWriter.cc @@ -232,7 +232,7 @@ VerilogWriter::writeWireDcls(Instance *inst) { Cell *cell = network_->cell(inst); char escape = network_->pathEscape(); - Map bus_ranges; + Map> bus_ranges; NetIterator *net_iter = network_->netIterator(inst); while (net_iter->hasNext()) { Net *net = net_iter->next(); @@ -243,7 +243,7 @@ VerilogWriter::writeWireDcls(Instance *inst) string bus_name; int index; parseBusName(net_name, '[', ']', escape, is_bus, bus_name, index); - BusIndexRange &range = bus_ranges[bus_name.c_str()]; + BusIndexRange &range = bus_ranges[bus_name]; range.first = max(range.first, index); range.second = min(range.second, index); } @@ -256,7 +256,7 @@ VerilogWriter::writeWireDcls(Instance *inst) delete net_iter; for (auto name_range : bus_ranges) { - const char *bus_name = name_range.first; + const char *bus_name = name_range.first.c_str(); const BusIndexRange &range = name_range.second; string net_vname = netVerilogName(bus_name, network_->pathEscape()); fprintf(stream_, " wire [%d:%d] %s;\n",