commit
88cc3b07bb
|
|
@ -73,7 +73,7 @@ set(STA_SOURCE
|
||||||
dcalc/LumpedCapDelayCalc.cc
|
dcalc/LumpedCapDelayCalc.cc
|
||||||
dcalc/NetCaps.cc
|
dcalc/NetCaps.cc
|
||||||
dcalc/RCDelayCalc.cc
|
dcalc/RCDelayCalc.cc
|
||||||
dcalc/SimpleRCDelayCalc.cc
|
dcalc/SlewDegradeDelayCalc.cc
|
||||||
dcalc/UnitDelayCalc.cc
|
dcalc/UnitDelayCalc.cc
|
||||||
|
|
||||||
graph/DelayFloat.cc
|
graph/DelayFloat.cc
|
||||||
|
|
|
||||||
|
|
@ -439,6 +439,7 @@ ArnoldiDelayCalc::loadDelay(const Pin *load_pin,
|
||||||
ArcDelay &wire_delay,
|
ArcDelay &wire_delay,
|
||||||
Slew &load_slew)
|
Slew &load_slew)
|
||||||
{
|
{
|
||||||
|
// This does not appear to handle input port parasitics correctly.
|
||||||
wire_delay = 0.0;
|
wire_delay = 0.0;
|
||||||
load_slew = drvr_slew_ * multi_drvr_slew_factor_;
|
load_slew = drvr_slew_ * multi_drvr_slew_factor_;
|
||||||
if (rcmodel_) {
|
if (rcmodel_) {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
#include "StringUtil.hh"
|
#include "StringUtil.hh"
|
||||||
#include "UnitDelayCalc.hh"
|
#include "UnitDelayCalc.hh"
|
||||||
#include "LumpedCapDelayCalc.hh"
|
#include "LumpedCapDelayCalc.hh"
|
||||||
#include "SimpleRCDelayCalc.hh"
|
#include "SlewDegradeDelayCalc.hh"
|
||||||
#include "DmpDelayCalc.hh"
|
#include "DmpDelayCalc.hh"
|
||||||
#include "ArnoldiDelayCalc.hh"
|
#include "ArnoldiDelayCalc.hh"
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ registerDelayCalcs()
|
||||||
{
|
{
|
||||||
registerDelayCalc("unit", makeUnitDelayCalc);
|
registerDelayCalc("unit", makeUnitDelayCalc);
|
||||||
registerDelayCalc("lumped_cap", makeLumpedCapDelayCalc);
|
registerDelayCalc("lumped_cap", makeLumpedCapDelayCalc);
|
||||||
registerDelayCalc("simple_rc", makeSimpleRCDelayCalc);
|
registerDelayCalc("slew_degrade", makeSlewDegradeDelayCalc);
|
||||||
registerDelayCalc("dmp_ceff_elmore", makeDmpCeffElmoreDelayCalc);
|
registerDelayCalc("dmp_ceff_elmore", makeDmpCeffElmoreDelayCalc);
|
||||||
registerDelayCalc("dmp_ceff_two_pole", makeDmpCeffTwoPoleDelayCalc);
|
registerDelayCalc("dmp_ceff_two_pole", makeDmpCeffTwoPoleDelayCalc);
|
||||||
registerDelayCalc("arnoldi", makeArnoldiDelayCalc);
|
registerDelayCalc("arnoldi", makeArnoldiDelayCalc);
|
||||||
|
|
|
||||||
|
|
@ -601,6 +601,7 @@ DmpAlg::loadDelaySlew(const Pin *,
|
||||||
slew = slew1;
|
slew = slew1;
|
||||||
}
|
}
|
||||||
catch (DmpError &error) {
|
catch (DmpError &error) {
|
||||||
|
fail(error.what());
|
||||||
delay = elmore_;
|
delay = elmore_;
|
||||||
slew = drvr_slew_;
|
slew = drvr_slew_;
|
||||||
}
|
}
|
||||||
|
|
@ -620,7 +621,7 @@ DmpAlg::findVlCrossing(double vth)
|
||||||
double
|
double
|
||||||
DmpAlg::vlCrossingUpperBound()
|
DmpAlg::vlCrossingUpperBound()
|
||||||
{
|
{
|
||||||
return voCrossingUpperBound() + elmore_;
|
return voCrossingUpperBound() + elmore_ * 2.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -670,8 +671,8 @@ DmpAlg::showVl()
|
||||||
void
|
void
|
||||||
DmpAlg::fail(const char *reason)
|
DmpAlg::fail(const char *reason)
|
||||||
{
|
{
|
||||||
// Allow only failures to be reported with a unique debug flag.
|
// Report failures with a unique debug flag.
|
||||||
if (debug_->check("dmp_ceff", 1) || debug_->check("dmp_ceff_fail", 1))
|
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",
|
report_->reportLine("delay_calc: DMP failed - %s c2=%s rpi=%s c1=%s rd=%s",
|
||||||
reason,
|
reason,
|
||||||
units_->capacitanceUnit()->asString(c2_),
|
units_->capacitanceUnit()->asString(c2_),
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
#include "DcalcAnalysisPt.hh"
|
#include "DcalcAnalysisPt.hh"
|
||||||
#include "GraphDelayCalc.hh"
|
#include "GraphDelayCalc.hh"
|
||||||
#include "DmpCeff.hh"
|
#include "DmpCeff.hh"
|
||||||
|
#include "Network.hh"
|
||||||
|
|
||||||
namespace sta {
|
namespace sta {
|
||||||
|
|
||||||
|
|
@ -125,30 +126,30 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DmpCeffTwoPoleDelayCalc(StaState *sta);
|
DmpCeffTwoPoleDelayCalc(StaState *sta);
|
||||||
virtual ArcDelayCalc *copy();
|
ArcDelayCalc *copy() override;
|
||||||
virtual Parasitic *findParasitic(const Pin *drvr_pin,
|
Parasitic *findParasitic(const Pin *drvr_pin,
|
||||||
const RiseFall *rf,
|
const RiseFall *rf,
|
||||||
const DcalcAnalysisPt *dcalc_ap);
|
const DcalcAnalysisPt *dcalc_ap) override;
|
||||||
virtual ReducedParasiticType reducedParasiticType() const;
|
ReducedParasiticType reducedParasiticType() const override;
|
||||||
virtual void inputPortDelay(const Pin *port_pin,
|
void inputPortDelay(const Pin *port_pin,
|
||||||
float in_slew,
|
float in_slew,
|
||||||
const RiseFall *rf,
|
const RiseFall *rf,
|
||||||
const Parasitic *parasitic,
|
const Parasitic *parasitic,
|
||||||
const DcalcAnalysisPt *dcalc_ap);
|
const DcalcAnalysisPt *dcalc_ap) override;
|
||||||
virtual void gateDelay(const LibertyCell *drvr_cell,
|
void gateDelay(const LibertyCell *drvr_cell,
|
||||||
const TimingArc *arc,
|
const TimingArc *arc,
|
||||||
const Slew &in_slew,
|
const Slew &in_slew,
|
||||||
float load_cap,
|
float load_cap,
|
||||||
const Parasitic *drvr_parasitic,
|
const Parasitic *drvr_parasitic,
|
||||||
float related_out_cap,
|
float related_out_cap,
|
||||||
const Pvt *pvt,
|
const Pvt *pvt,
|
||||||
const DcalcAnalysisPt *dcalc_ap,
|
const DcalcAnalysisPt *dcalc_ap,
|
||||||
// Return values.
|
// Return values.
|
||||||
ArcDelay &gate_delay,
|
ArcDelay &gate_delay,
|
||||||
Slew &drvr_slew);
|
Slew &drvr_slew) override;
|
||||||
virtual void loadDelay(const Pin *load_pin,
|
void loadDelay(const Pin *load_pin,
|
||||||
ArcDelay &wire_delay,
|
ArcDelay &wire_delay,
|
||||||
Slew &load_slew);
|
Slew &load_slew) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadDelay(Parasitic *pole_residue,
|
void loadDelay(Parasitic *pole_residue,
|
||||||
|
|
@ -302,7 +303,7 @@ DmpCeffTwoPoleDelayCalc::loadDelay(const Pin *load_pin,
|
||||||
ArcDelay &wire_delay,
|
ArcDelay &wire_delay,
|
||||||
Slew &load_slew)
|
Slew &load_slew)
|
||||||
{
|
{
|
||||||
// NEED to handle PiElmore parasitic.
|
// Should handle PiElmore parasitic.
|
||||||
ArcDelay wire_delay1 = 0.0;
|
ArcDelay wire_delay1 = 0.0;
|
||||||
Slew load_slew1 = drvr_slew_;
|
Slew load_slew1 = drvr_slew_;
|
||||||
Parasitic *pole_residue = 0;
|
Parasitic *pole_residue = 0;
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ public:
|
||||||
// Return values.
|
// Return values.
|
||||||
ArcDelay &gate_delay,
|
ArcDelay &gate_delay,
|
||||||
Slew &drvr_slew) override;
|
Slew &drvr_slew) override;
|
||||||
void setMultiDrvrSlewFactor(float) override;
|
void setMultiDrvrSlewFactor(float factor) override;
|
||||||
float ceff(const LibertyCell *drvr_cell,
|
float ceff(const LibertyCell *drvr_cell,
|
||||||
const TimingArc *arc,
|
const TimingArc *arc,
|
||||||
const Slew &in_slew,
|
const Slew &in_slew,
|
||||||
|
|
|
||||||
|
|
@ -62,16 +62,10 @@ RCDelayCalc::dspfWireDelaySlew(const Pin *,
|
||||||
ArcDelay &wire_delay,
|
ArcDelay &wire_delay,
|
||||||
Slew &load_slew)
|
Slew &load_slew)
|
||||||
{
|
{
|
||||||
float vth = .5;
|
float vth = drvr_library_->inputThreshold(drvr_rf_);
|
||||||
float vl = .2;
|
float vl = drvr_library_->slewLowerThreshold(drvr_rf_);
|
||||||
float vh = .8;
|
float vh = drvr_library_->slewUpperThreshold(drvr_rf_);
|
||||||
float slew_derate = 1.0;
|
float slew_derate = drvr_library_->slewDerateFromLibrary();
|
||||||
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();
|
|
||||||
}
|
|
||||||
wire_delay = static_cast<float>(-elmore * log(1.0 - vth));
|
wire_delay = static_cast<float>(-elmore * log(1.0 - vth));
|
||||||
load_slew = (drvr_slew_ + elmore * log((1.0 - vl) / (1.0 - vh))
|
load_slew = (drvr_slew_ + elmore * log((1.0 - vl) / (1.0 - vh))
|
||||||
/ slew_derate) * multi_drvr_slew_factor_;
|
/ slew_derate) * multi_drvr_slew_factor_;
|
||||||
|
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include "SimpleRCDelayCalc.hh"
|
#include "SlewDegradeDelayCalc.hh"
|
||||||
|
|
||||||
#include "TimingArc.hh"
|
#include "TimingArc.hh"
|
||||||
#include "Liberty.hh"
|
#include "Liberty.hh"
|
||||||
|
|
@ -25,46 +25,83 @@
|
||||||
|
|
||||||
namespace sta {
|
namespace sta {
|
||||||
|
|
||||||
ArcDelayCalc *
|
// Liberty table model lumped capacitance arc delay calculator.
|
||||||
makeSimpleRCDelayCalc(StaState *sta)
|
// 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)
|
RCDelayCalc(sta)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ArcDelayCalc *
|
ArcDelayCalc *
|
||||||
SimpleRCDelayCalc::copy()
|
SlewDegradeDelayCalc::copy()
|
||||||
{
|
{
|
||||||
return new SimpleRCDelayCalc(this);
|
return new SlewDegradeDelayCalc(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SimpleRCDelayCalc::inputPortDelay(const Pin *port_pin,
|
SlewDegradeDelayCalc::inputPortDelay(const Pin *port_pin,
|
||||||
float in_slew,
|
float in_slew,
|
||||||
const RiseFall *rf,
|
const RiseFall *rf,
|
||||||
const Parasitic *parasitic,
|
const Parasitic *parasitic,
|
||||||
const DcalcAnalysisPt *dcalc_ap)
|
const DcalcAnalysisPt *dcalc_ap)
|
||||||
{
|
{
|
||||||
pvt_ = dcalc_ap->operatingConditions();
|
pvt_ = dcalc_ap->operatingConditions();
|
||||||
RCDelayCalc::inputPortDelay(port_pin, in_slew, rf, parasitic, dcalc_ap);
|
RCDelayCalc::inputPortDelay(port_pin, in_slew, rf, parasitic, dcalc_ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SimpleRCDelayCalc::gateDelay(const LibertyCell *drvr_cell,
|
SlewDegradeDelayCalc::gateDelay(const LibertyCell *drvr_cell,
|
||||||
const TimingArc *arc,
|
const TimingArc *arc,
|
||||||
const Slew &in_slew,
|
const Slew &in_slew,
|
||||||
float load_cap,
|
float load_cap,
|
||||||
const Parasitic *drvr_parasitic,
|
const Parasitic *drvr_parasitic,
|
||||||
float related_out_cap,
|
float related_out_cap,
|
||||||
const Pvt *pvt,
|
const Pvt *pvt,
|
||||||
const DcalcAnalysisPt *dcalc_ap,
|
const DcalcAnalysisPt *dcalc_ap,
|
||||||
// Return values.
|
// Return values.
|
||||||
ArcDelay &gate_delay,
|
ArcDelay &gate_delay,
|
||||||
Slew &drvr_slew)
|
Slew &drvr_slew)
|
||||||
{
|
{
|
||||||
input_port_ = false;
|
input_port_ = false;
|
||||||
drvr_parasitic_ = drvr_parasitic;
|
drvr_parasitic_ = drvr_parasitic;
|
||||||
|
|
@ -79,9 +116,9 @@ SimpleRCDelayCalc::gateDelay(const LibertyCell *drvr_cell,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SimpleRCDelayCalc::loadDelay(const Pin *load_pin,
|
SlewDegradeDelayCalc::loadDelay(const Pin *load_pin,
|
||||||
ArcDelay &wire_delay,
|
ArcDelay &wire_delay,
|
||||||
Slew &load_slew)
|
Slew &load_slew)
|
||||||
{
|
{
|
||||||
ArcDelay wire_delay1 = 0.0;
|
ArcDelay wire_delay1 = 0.0;
|
||||||
Slew load_slew1 = drvr_slew_;
|
Slew load_slew1 = drvr_slew_;
|
||||||
|
|
@ -16,21 +16,11 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "RCDelayCalc.hh"
|
||||||
|
|
||||||
namespace sta {
|
namespace sta {
|
||||||
|
|
||||||
// Write a spice deck for path.
|
ArcDelayCalc *
|
||||||
// Throws FileNotReadable, FileNotWritable, SubcktEndsMissing
|
makeSlewDegradeDelayCalc(StaState *sta);
|
||||||
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
|
} // namespace
|
||||||
#endif
|
|
||||||
|
|
@ -36,10 +36,11 @@ class DcalcAnalysisPt;
|
||||||
// UnitDelayCalc
|
// UnitDelayCalc
|
||||||
// LumpedCapDelayCalc
|
// LumpedCapDelayCalc
|
||||||
// RCDelayCalc
|
// RCDelayCalc
|
||||||
// SimpleRCDelayCalc
|
// SlewDegradeDelayCalc
|
||||||
// DmpCeffDelayCalc
|
// DmpCeffDelayCalc
|
||||||
// DmpCeffElmoreDelayCalc
|
// DmpCeffElmoreDelayCalc
|
||||||
// DmpCeffTwoPoleDelayCalc
|
// DmpCeffTwoPoleDelayCalc
|
||||||
|
// ArnoldiDelayCalc
|
||||||
|
|
||||||
// Abstract class to interface to a delay calculator primitive.
|
// Abstract class to interface to a delay calculator primitive.
|
||||||
class ArcDelayCalc : public StaState
|
class ArcDelayCalc : public StaState
|
||||||
|
|
|
||||||
|
|
@ -462,6 +462,7 @@ public:
|
||||||
const char *variableString() const;
|
const char *variableString() const;
|
||||||
const Unit *unit(const Units *units);
|
const Unit *unit(const Units *units);
|
||||||
size_t size() const { return values_->size(); }
|
size_t size() const { return values_->size(); }
|
||||||
|
bool inBounds(float value) const;
|
||||||
float axisValue(size_t index) const { return (*values_)[index]; }
|
float axisValue(size_t index) const { return (*values_)[index]; }
|
||||||
// Find the index for value such that axis[index] <= value < axis[index+1].
|
// Find the index for value such that axis[index] <= value < axis[index+1].
|
||||||
size_t findAxisIndex(float value) const;
|
size_t findAxisIndex(float value) const;
|
||||||
|
|
@ -505,16 +506,25 @@ public:
|
||||||
Table1 *ref_times);
|
Table1 *ref_times);
|
||||||
~OutputWaveforms();
|
~OutputWaveforms();
|
||||||
const RiseFall *rf() const { return rf_; }
|
const RiseFall *rf() const { return rf_; }
|
||||||
|
bool inBounds(float in_slew,
|
||||||
|
float load_cap) const;
|
||||||
Table1 voltageWaveform(float in_slew,
|
Table1 voltageWaveform(float in_slew,
|
||||||
float load_cap);
|
float load_cap);
|
||||||
Table1 currentWaveform(float slew,
|
float voltageTime(float in_slew,
|
||||||
float cap);
|
float load_cap,
|
||||||
|
float voltage);
|
||||||
|
const Table1 *currentWaveform(float slew,
|
||||||
|
float cap);
|
||||||
float referenceTime(float slew);
|
float referenceTime(float slew);
|
||||||
|
void setVdd(float vdd);
|
||||||
static bool checkAxes(TableTemplate *tbl_template);
|
static bool checkAxes(TableTemplate *tbl_template);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Table1 *voltageWaveform(size_t wave_index,
|
float voltageTime1(float voltage,
|
||||||
float cap);
|
size_t wave_index,
|
||||||
|
float cap);
|
||||||
|
FloatSeq *voltageTimes(size_t wave_index,
|
||||||
|
float cap);
|
||||||
|
|
||||||
// Row.
|
// Row.
|
||||||
TableAxisPtr slew_axis_;
|
TableAxisPtr slew_axis_;
|
||||||
|
|
@ -522,8 +532,10 @@ private:
|
||||||
TableAxisPtr cap_axis_;
|
TableAxisPtr cap_axis_;
|
||||||
const RiseFall *rf_;
|
const RiseFall *rf_;
|
||||||
Table1Seq current_waveforms_;
|
Table1Seq current_waveforms_;
|
||||||
Table1Seq voltage_waveforms_;
|
FloatTable voltage_times_;
|
||||||
Table1 *ref_times_;
|
Table1 *ref_times_;
|
||||||
|
float vdd_;
|
||||||
|
static constexpr size_t voltage_waveform_step_count_ = 20;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DriverWaveform
|
class DriverWaveform
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,10 @@ public:
|
||||||
void operator=(const Unit &unit);
|
void operator=(const Unit &unit);
|
||||||
float scale() const { return scale_; }
|
float scale() const { return scale_; }
|
||||||
void setScale(float scale);
|
void setScale(float scale);
|
||||||
const char *scaleAbreviation() const;
|
const char *scaleAbbreviation() const;
|
||||||
const char *suffix() const { return suffix_; }
|
const char *suffix() const { return suffix_; }
|
||||||
|
// scale abbreviation + suffix
|
||||||
|
const char *scaledSuffix() const { return scaled_suffix_; }
|
||||||
void setSuffix(const char *suffix);
|
void setSuffix(const char *suffix);
|
||||||
int digits() const { return digits_; }
|
int digits() const { return digits_; }
|
||||||
void setDigits(int digits);
|
void setDigits(int digits);
|
||||||
|
|
@ -46,8 +48,11 @@ public:
|
||||||
int digits) const;
|
int digits) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void setScaledSuffix();
|
||||||
|
|
||||||
float scale_; // multiplier from user units to internal units
|
float scale_; // multiplier from user units to internal units
|
||||||
const char *suffix_; // print suffix
|
const char *suffix_; // print suffix
|
||||||
|
const char *scaled_suffix_;
|
||||||
int digits_; // print digits (after decimal pt)
|
int digits_; // print digits (after decimal pt)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -67,8 +72,6 @@ public:
|
||||||
const Unit *voltageUnit() const { return &voltage_unit_; }
|
const Unit *voltageUnit() const { return &voltage_unit_; }
|
||||||
Unit *resistanceUnit() { return &resistance_unit_; }
|
Unit *resistanceUnit() { return &resistance_unit_; }
|
||||||
const Unit *resistanceUnit() const { 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_; }
|
Unit *currentUnit() { return ¤t_unit_; }
|
||||||
const Unit *currentUnit() const { return ¤t_unit_; }
|
const Unit *currentUnit() const { return ¤t_unit_; }
|
||||||
Unit *powerUnit() { return &power_unit_; }
|
Unit *powerUnit() { return &power_unit_; }
|
||||||
|
|
@ -80,10 +83,9 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Unit time_unit_;
|
Unit time_unit_;
|
||||||
|
Unit resistance_unit_;
|
||||||
Unit capacitance_unit_;
|
Unit capacitance_unit_;
|
||||||
Unit voltage_unit_;
|
Unit voltage_unit_;
|
||||||
Unit resistance_unit_;
|
|
||||||
Unit pulling_resistance_unit_;
|
|
||||||
Unit current_unit_;
|
Unit current_unit_;
|
||||||
Unit power_unit_;
|
Unit power_unit_;
|
||||||
Unit distance_unit_;
|
Unit distance_unit_;
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,16 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
namespace sta {
|
namespace sta {
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::set;
|
||||||
|
|
||||||
|
typedef set<string> StdStringSet;
|
||||||
|
|
||||||
class Path;
|
class Path;
|
||||||
class StaState;
|
class StaState;
|
||||||
|
|
||||||
|
|
@ -33,7 +41,9 @@ writePathSpice(Path *path,
|
||||||
const char *lib_subckt_filename,
|
const char *lib_subckt_filename,
|
||||||
// Device model file included in spice file.
|
// Device model file included in spice file.
|
||||||
const char *model_filename,
|
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,
|
const char *gnd_name,
|
||||||
StaState *sta);
|
StaState *sta);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,6 @@ LibertyReader::readLibertyFile(const char *filename,
|
||||||
mode_value_ = nullptr;
|
mode_value_ = nullptr;
|
||||||
ocv_derate_ = nullptr;
|
ocv_derate_ = nullptr;
|
||||||
pg_port_ = nullptr;
|
pg_port_ = nullptr;
|
||||||
have_resistance_unit_ = false;
|
|
||||||
default_operating_condition_ = nullptr;
|
default_operating_condition_ = nullptr;
|
||||||
receiver_model_ = nullptr;
|
receiver_model_ = nullptr;
|
||||||
|
|
||||||
|
|
@ -648,12 +647,6 @@ LibertyReader::endLibrary(LibertyGroup *group)
|
||||||
void
|
void
|
||||||
LibertyReader::endLibraryAttrs(LibertyGroup *group)
|
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
|
// These attributes reference named groups in the library so
|
||||||
// wait until the end of the library to resolve them.
|
// wait until the end of the library to resolve them.
|
||||||
if (default_wireload_) {
|
if (default_wireload_) {
|
||||||
|
|
@ -727,16 +720,14 @@ LibertyReader::visitPullingResistanceUnit(LibertyAttr *attr)
|
||||||
{
|
{
|
||||||
if (library_)
|
if (library_)
|
||||||
parseUnits(attr, "ohm", res_scale_,
|
parseUnits(attr, "ohm", res_scale_,
|
||||||
library_->units()->pullingResistanceUnit());
|
library_->units()->resistanceUnit());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LibertyReader::visitResistanceUnit(LibertyAttr *attr)
|
LibertyReader::visitResistanceUnit(LibertyAttr *attr)
|
||||||
{
|
{
|
||||||
if (library_) {
|
if (library_)
|
||||||
parseUnits(attr, "ohm", res_scale_, library_->units()->resistanceUnit());
|
parseUnits(attr, "ohm", res_scale_, library_->units()->resistanceUnit());
|
||||||
have_resistance_unit_ = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -2522,7 +2513,7 @@ LibertyReader::endOutputCurrentRiseFall(LibertyGroup *group)
|
||||||
slew_axis->findAxisIndex(waveform->slew(), slew_index, slew_exists);
|
slew_axis->findAxisIndex(waveform->slew(), slew_index, slew_exists);
|
||||||
cap_axis->findAxisIndex(waveform->cap(), cap_index, cap_exists);
|
cap_axis->findAxisIndex(waveform->cap(), cap_index, cap_exists);
|
||||||
if (slew_exists && 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();
|
current_waveforms[index] = waveform->stealCurrents();
|
||||||
(*ref_times)[slew_index] = waveform->referenceTime();
|
(*ref_times)[slew_index] = waveform->referenceTime();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -624,7 +624,6 @@ protected:
|
||||||
float power_scale_;
|
float power_scale_;
|
||||||
float energy_scale_;
|
float energy_scale_;
|
||||||
float distance_scale_;
|
float distance_scale_;
|
||||||
bool have_resistance_unit_;
|
|
||||||
const char *default_operating_condition_;
|
const char *default_operating_condition_;
|
||||||
ReceiverModelPtr receiver_model_;
|
ReceiverModelPtr receiver_model_;
|
||||||
OutputWaveformSeq output_currents_;
|
OutputWaveformSeq output_currents_;
|
||||||
|
|
|
||||||
|
|
@ -123,26 +123,21 @@ LibertyWriter::writeHeader()
|
||||||
fprintf(stream_, " delay_model : table_lookup;\n");
|
fprintf(stream_, " delay_model : table_lookup;\n");
|
||||||
fprintf(stream_, " simulation : false;\n");
|
fprintf(stream_, " simulation : false;\n");
|
||||||
const Unit *cap_unit = library_->units()->capacitanceUnit();
|
const Unit *cap_unit = library_->units()->capacitanceUnit();
|
||||||
fprintf(stream_, " capacitive_load_unit (1,%s%s);\n",
|
fprintf(stream_, " capacitive_load_unit (1,%s);\n",
|
||||||
cap_unit->scaleAbreviation(),
|
cap_unit->scaledSuffix());
|
||||||
cap_unit->suffix());
|
|
||||||
fprintf(stream_, " leakage_power_unit : 1pW;\n");
|
fprintf(stream_, " leakage_power_unit : 1pW;\n");
|
||||||
const Unit *current_unit = library_->units()->currentUnit();
|
const Unit *current_unit = library_->units()->currentUnit();
|
||||||
fprintf(stream_, " current_unit : \"1%s%s\";\n",
|
fprintf(stream_, " current_unit : \"1%s\";\n",
|
||||||
current_unit->scaleAbreviation(),
|
current_unit->scaledSuffix());
|
||||||
current_unit->suffix());
|
|
||||||
const Unit *res_unit = library_->units()->resistanceUnit();
|
const Unit *res_unit = library_->units()->resistanceUnit();
|
||||||
fprintf(stream_, " pulling_resistance_unit : \"1%s%s\";\n",
|
fprintf(stream_, " pulling_resistance_unit : \"1%s\";\n",
|
||||||
res_unit->scaleAbreviation(),
|
res_unit->scaledSuffix());
|
||||||
res_unit->suffix());
|
|
||||||
const Unit *time_unit = library_->units()->timeUnit();
|
const Unit *time_unit = library_->units()->timeUnit();
|
||||||
fprintf(stream_, " time_unit : \"1%s%s\";\n",
|
fprintf(stream_, " time_unit : \"1%s\";\n",
|
||||||
time_unit->scaleAbreviation(),
|
time_unit->scaledSuffix());
|
||||||
time_unit->suffix());
|
|
||||||
const Unit *volt_unit = library_->units()->voltageUnit();
|
const Unit *volt_unit = library_->units()->voltageUnit();
|
||||||
fprintf(stream_, " voltage_unit : \"1%s%s\";\n",
|
fprintf(stream_, " voltage_unit : \"1%s\";\n",
|
||||||
volt_unit->scaleAbreviation(),
|
volt_unit->scaledSuffix());
|
||||||
volt_unit->suffix());
|
|
||||||
fprintf(stream_, " library_features(report_delay_calculation);\n");
|
fprintf(stream_, " library_features(report_delay_calculation);\n");
|
||||||
fprintf(stream_, "\n");
|
fprintf(stream_, "\n");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1437,6 +1437,15 @@ TableAxis::~TableAxis()
|
||||||
delete values_;
|
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.
|
// Bisection search.
|
||||||
size_t
|
size_t
|
||||||
TableAxis::findAxisIndex(float value) const
|
TableAxis::findAxisIndex(float value) const
|
||||||
|
|
@ -1572,9 +1581,6 @@ tableVariableUnit(TableAxisVariable variable,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis,
|
OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis,
|
||||||
|
|
@ -1586,15 +1592,16 @@ OutputWaveforms::OutputWaveforms(TableAxisPtr slew_axis,
|
||||||
cap_axis_(cap_axis),
|
cap_axis_(cap_axis),
|
||||||
rf_(rf),
|
rf_(rf),
|
||||||
current_waveforms_(current_waveforms),
|
current_waveforms_(current_waveforms),
|
||||||
voltage_waveforms_(current_waveforms.size()),
|
voltage_times_(current_waveforms.size()),
|
||||||
ref_times_(ref_times)
|
ref_times_(ref_times),
|
||||||
|
vdd_(0.0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputWaveforms::~OutputWaveforms()
|
OutputWaveforms::~OutputWaveforms()
|
||||||
{
|
{
|
||||||
current_waveforms_.deleteContents();
|
current_waveforms_.deleteContents();
|
||||||
voltage_waveforms_.deleteContents();
|
voltage_times_.deleteContents();
|
||||||
delete ref_times_;
|
delete ref_times_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1615,43 +1622,52 @@ OutputWaveforms::checkAxes(TableTemplate *tbl_template)
|
||||||
&& axis3->variable() == TableAxisVariable::time);
|
&& axis3->variable() == TableAxisVariable::time);
|
||||||
}
|
}
|
||||||
|
|
||||||
Table1
|
bool
|
||||||
OutputWaveforms::voltageWaveform(float slew,
|
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)
|
float cap)
|
||||||
{
|
{
|
||||||
size_t slew_index = slew_axis_->findAxisIndex(slew);
|
size_t slew_index = slew_axis_->findAxisIndex(slew);
|
||||||
size_t cap_index = cap_axis_->findAxisIndex(cap);
|
size_t cap_index = cap_axis_->findAxisIndex(cap);
|
||||||
size_t wave_index00 = slew_index * cap_axis_->size() + cap_index;
|
size_t wave_index = slew_index * cap_axis_->size() + cap_index;
|
||||||
size_t wave_index01 = slew_index * cap_axis_->size() + (cap_index + 1);
|
return current_waveforms_[wave_index];
|
||||||
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);
|
|
||||||
|
float
|
||||||
|
OutputWaveforms::referenceTime(float slew)
|
||||||
|
{
|
||||||
|
return ref_times_->findValue(slew);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OutputWaveforms::setVdd(float vdd)
|
||||||
|
{
|
||||||
|
vdd_ = vdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
float
|
||||||
|
OutputWaveforms::voltageTime(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 cap0 = cap_axis_->axisValue(cap_index);
|
||||||
float cap1 = cap_axis_->axisValue(cap_index + 1);
|
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.
|
// Interpolate waveform samples at voltage steps.
|
||||||
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<TableAxis>(time_axis00->variable(),
|
|
||||||
time_values);
|
|
||||||
|
|
||||||
// Interpolate waveform samples at time steps.
|
|
||||||
size_t index1 = slew_index;
|
size_t index1 = slew_index;
|
||||||
size_t index2 = cap_index;
|
size_t index2 = cap_index;
|
||||||
float x1 = slew;
|
float x1 = slew;
|
||||||
|
|
@ -1662,49 +1678,65 @@ OutputWaveforms::voltageWaveform(float slew,
|
||||||
float x2l = cap_axis_->axisValue(index2);
|
float x2l = cap_axis_->axisValue(index2);
|
||||||
float x2u = cap_axis_->axisValue(index2 + 1);
|
float x2u = cap_axis_->axisValue(index2 + 1);
|
||||||
float dx2 = (x2 - x2l) / (x2u - x2l);
|
float dx2 = (x2 - x2l) / (x2u - x2l);
|
||||||
FloatSeq *values = new FloatSeq;
|
|
||||||
float prev_value = 0.0;
|
float y00 = voltageTime1(volt, wave_index00, cap0);
|
||||||
constexpr float value_tol = .0001;
|
float y01 = voltageTime1(volt, wave_index01, cap1);
|
||||||
for (size_t i = 0; i <= time_step_count; i++) {
|
float y10 = voltageTime1(volt, wave_index10, cap0);
|
||||||
float time = time_min + time_step * i;
|
float y11 = voltageTime1(volt, wave_index11, cap1);
|
||||||
if (time > time_max)
|
float time
|
||||||
break;
|
= (1 - dx1) * (1 - dx2) * y00
|
||||||
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 * (1 - dx2) * y10
|
||||||
+ dx1 * dx2 * y11
|
+ dx1 * dx2 * y11
|
||||||
+ (1 - dx1) * dx2 * y01;
|
+ (1 - dx1) * dx2 * y01;
|
||||||
if (i == 0 || abs(value - prev_value) > value_tol) {
|
return time;
|
||||||
time_values->push_back(time);
|
|
||||||
values->push_back(value);
|
|
||||||
}
|
|
||||||
prev_value = value;
|
|
||||||
}
|
|
||||||
return Table1(values, time_axis);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Table1 *
|
Table1
|
||||||
OutputWaveforms::voltageWaveform(size_t wave_index,
|
OutputWaveforms::voltageWaveform(float slew,
|
||||||
float cap)
|
float cap)
|
||||||
{
|
{
|
||||||
Table1 *voltages = voltage_waveforms_[wave_index];
|
float volt_step = vdd_ / voltage_waveform_step_count_;
|
||||||
if (voltages == nullptr) {
|
FloatSeq *times = new FloatSeq;
|
||||||
FloatSeq *voltages1 = new FloatSeq;
|
FloatSeq *volts = new FloatSeq;
|
||||||
Table1 *currents = current_waveforms_[wave_index];
|
for (size_t v = 0; v <= voltage_waveform_step_count_; v++) {
|
||||||
voltages = new Table1(voltages1, currents->axis1());
|
float volt = v * volt_step;
|
||||||
voltage_waveforms_[wave_index] = voltages;
|
float time = voltageTime(slew, cap, volt);
|
||||||
|
times->push_back(time);
|
||||||
|
volts->push_back(volt);
|
||||||
|
}
|
||||||
|
TableAxisPtr time_axis = make_shared<TableAxis>(TableAxisVariable::time, times);
|
||||||
|
return Table1(volts, time_axis);
|
||||||
|
}
|
||||||
|
|
||||||
// i = C dv/dt
|
float
|
||||||
|
OutputWaveforms::voltageTime1(float voltage,
|
||||||
|
size_t wave_index,
|
||||||
|
float cap)
|
||||||
|
{
|
||||||
|
FloatSeq *voltage_times = voltageTimes(wave_index, cap);
|
||||||
|
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];
|
||||||
|
float time = time0 + (time1 - time0) * (voltage - volt_step * volt_idx);
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// Integrate current waveform to find voltage waveform.
|
||||||
|
// i = C dv/dt
|
||||||
|
FloatSeq volts;
|
||||||
|
Table1 *currents = current_waveforms_[wave_index];
|
||||||
TableAxisPtr time_axis = currents->axis1();
|
TableAxisPtr time_axis = currents->axis1();
|
||||||
float prev_time = time_axis->axisValue(0);
|
float prev_time = time_axis->axisValue(0);
|
||||||
float prev_current = currents->value(0);
|
float prev_current = currents->value(0);
|
||||||
float voltage = 0.0;
|
float voltage = 0.0;
|
||||||
voltages1->push_back(voltage);
|
volts.push_back(voltage);
|
||||||
bool always_rise = true;
|
bool always_rise = true;
|
||||||
bool invert = (always_rise && rf_ == RiseFall::fall());
|
bool invert = (always_rise && rf_ == RiseFall::fall());
|
||||||
for (size_t i = 1; i < time_axis->size(); i++) {
|
for (size_t i = 1; i < time_axis->size(); i++) {
|
||||||
|
|
@ -1712,92 +1744,39 @@ OutputWaveforms::voltageWaveform(size_t wave_index,
|
||||||
float current = currents->value(i);
|
float current = currents->value(i);
|
||||||
float dv = (current + prev_current) / 2.0 * (time - prev_time) / cap;
|
float dv = (current + prev_current) / 2.0 * (time - prev_time) / cap;
|
||||||
voltage += invert ? -dv : dv;
|
voltage += invert ? -dv : dv;
|
||||||
voltages1->push_back(voltage);
|
volts.push_back(voltage);
|
||||||
prev_time = time;
|
prev_time = time;
|
||||||
prev_current = current;
|
prev_current = current;
|
||||||
}
|
}
|
||||||
if (!always_rise && rf_ == RiseFall::fall()) {
|
|
||||||
for (size_t i = 0; i < voltages1->size(); i++)
|
// Sample the voltage waveform at uniform intervals to speed up
|
||||||
(*voltages1)[i] -= voltage;
|
// voltage time lookup.
|
||||||
|
voltage_times = new FloatSeq;
|
||||||
|
float volt_step = vdd_ / 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() - 1) {
|
||||||
|
time0 = time1;
|
||||||
|
volt0 = volt1;
|
||||||
|
i++;
|
||||||
|
time1 = time_axis->axisValue(i);
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
voltage_times_[wave_index] = voltage_times;
|
||||||
}
|
}
|
||||||
return voltages;
|
return voltage_times;
|
||||||
}
|
|
||||||
|
|
||||||
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 * 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);
|
|
||||||
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<TableAxis>(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,10 @@ using std::abs;
|
||||||
Unit::Unit(const char *suffix) :
|
Unit::Unit(const char *suffix) :
|
||||||
scale_(1.0),
|
scale_(1.0),
|
||||||
suffix_(stringCopy(suffix)),
|
suffix_(stringCopy(suffix)),
|
||||||
|
scaled_suffix_(nullptr),
|
||||||
digits_(3)
|
digits_(3)
|
||||||
{
|
{
|
||||||
|
setScaledSuffix();
|
||||||
}
|
}
|
||||||
|
|
||||||
Unit::Unit(float scale,
|
Unit::Unit(float scale,
|
||||||
|
|
@ -40,13 +42,23 @@ Unit::Unit(float scale,
|
||||||
int digits) :
|
int digits) :
|
||||||
scale_(scale),
|
scale_(scale),
|
||||||
suffix_(stringCopy(suffix)),
|
suffix_(stringCopy(suffix)),
|
||||||
|
scaled_suffix_(nullptr),
|
||||||
digits_(digits)
|
digits_(digits)
|
||||||
{
|
{
|
||||||
|
setScaledSuffix();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Unit::setScaledSuffix()
|
||||||
|
{
|
||||||
|
stringDelete(scaled_suffix_);
|
||||||
|
scaled_suffix_ = stringPrint("%s%s", scaleAbbreviation(), suffix_);
|
||||||
}
|
}
|
||||||
|
|
||||||
Unit::~Unit()
|
Unit::~Unit()
|
||||||
{
|
{
|
||||||
stringDelete(suffix_);
|
stringDelete(suffix_);
|
||||||
|
stringDelete(scaled_suffix_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -55,6 +67,8 @@ Unit::operator=(const Unit &unit)
|
||||||
scale_ = unit.scale_;
|
scale_ = unit.scale_;
|
||||||
stringDelete(suffix_);
|
stringDelete(suffix_);
|
||||||
suffix_ = stringCopy(unit.suffix_);
|
suffix_ = stringCopy(unit.suffix_);
|
||||||
|
stringDelete(scaled_suffix_);
|
||||||
|
scaled_suffix_ = stringCopy(unit.scaled_suffix_);
|
||||||
digits_ = unit.digits_;
|
digits_ = unit.digits_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,10 +88,11 @@ void
|
||||||
Unit::setScale(float scale)
|
Unit::setScale(float scale)
|
||||||
{
|
{
|
||||||
scale_ = scale;
|
scale_ = scale;
|
||||||
|
setScaledSuffix();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
Unit::scaleAbreviation() const
|
Unit::scaleAbbreviation() const
|
||||||
{
|
{
|
||||||
if (fuzzyEqual(scale_, 1E+6))
|
if (fuzzyEqual(scale_, 1E+6))
|
||||||
return "M";
|
return "M";
|
||||||
|
|
@ -104,6 +119,7 @@ Unit::setSuffix(const char *suffix)
|
||||||
{
|
{
|
||||||
stringDelete(suffix_);
|
stringDelete(suffix_);
|
||||||
suffix_ = stringCopy(suffix);
|
suffix_ = stringCopy(suffix);
|
||||||
|
setScaledSuffix();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -150,10 +166,9 @@ Unit::asString(float value,
|
||||||
|
|
||||||
Units::Units() :
|
Units::Units() :
|
||||||
time_unit_("s"),
|
time_unit_("s"),
|
||||||
|
resistance_unit_("ohm"),
|
||||||
capacitance_unit_("F"),
|
capacitance_unit_("F"),
|
||||||
voltage_unit_("v"),
|
voltage_unit_("v"),
|
||||||
resistance_unit_("ohm"),
|
|
||||||
pulling_resistance_unit_("ohm"),
|
|
||||||
current_unit_("A"),
|
current_unit_("A"),
|
||||||
power_unit_("W"),
|
power_unit_("W"),
|
||||||
distance_unit_("m"),
|
distance_unit_("m"),
|
||||||
|
|
@ -166,10 +181,10 @@ Units::find(const char *unit_name)
|
||||||
{
|
{
|
||||||
if (stringEq(unit_name, "time"))
|
if (stringEq(unit_name, "time"))
|
||||||
return &time_unit_;
|
return &time_unit_;
|
||||||
else if (stringEq(unit_name, "capacitance"))
|
|
||||||
return &capacitance_unit_;
|
|
||||||
else if (stringEq(unit_name, "resistance"))
|
else if (stringEq(unit_name, "resistance"))
|
||||||
return &resistance_unit_;
|
return &resistance_unit_;
|
||||||
|
else if (stringEq(unit_name, "capacitance"))
|
||||||
|
return &capacitance_unit_;
|
||||||
else if (stringEq(unit_name, "voltage"))
|
else if (stringEq(unit_name, "voltage"))
|
||||||
return &voltage_unit_;
|
return &voltage_unit_;
|
||||||
else if (stringEq(unit_name, "current"))
|
else if (stringEq(unit_name, "current"))
|
||||||
|
|
@ -186,8 +201,8 @@ void
|
||||||
Units::operator=(const Units &units)
|
Units::operator=(const Units &units)
|
||||||
{
|
{
|
||||||
time_unit_ = *units.timeUnit();
|
time_unit_ = *units.timeUnit();
|
||||||
capacitance_unit_ = *units.capacitanceUnit();
|
|
||||||
resistance_unit_ = *units.resistanceUnit();
|
resistance_unit_ = *units.resistanceUnit();
|
||||||
|
capacitance_unit_ = *units.capacitanceUnit();
|
||||||
voltage_unit_ = *units.voltageUnit();
|
voltage_unit_ = *units.voltageUnit();
|
||||||
current_unit_ = *units.currentUnit();
|
current_unit_ = *units.currentUnit();
|
||||||
power_unit_ = *units.powerUnit();
|
power_unit_ = *units.powerUnit();
|
||||||
|
|
|
||||||
19
power/Vcd.hh
19
power/Vcd.hh
|
|
@ -38,7 +38,24 @@ typedef int64_t VcdTime;
|
||||||
typedef vector<string> VcdScope;
|
typedef vector<string> VcdScope;
|
||||||
typedef map<string, VcdVar*> VcdNameMap;
|
typedef map<string, VcdVar*> 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
|
class Vcd : public StaState
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include "Report.hh"
|
#include "Report.hh"
|
||||||
#include "Error.hh"
|
#include "Error.hh"
|
||||||
#include "StringUtil.hh"
|
#include "StringUtil.hh"
|
||||||
|
#include "EnumNameMap.hh"
|
||||||
|
|
||||||
namespace sta {
|
namespace sta {
|
||||||
|
|
||||||
|
|
@ -160,6 +161,24 @@ VcdReader::setTimeUnit(const string &time_unit)
|
||||||
vcd_->setTimeUnit(time_unit, time_unit_scale);;
|
vcd_->setTimeUnit(time_unit, time_unit_scale);;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static EnumNameMap<VcdVarType> 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
|
void
|
||||||
VcdReader::parseVar()
|
VcdReader::parseVar()
|
||||||
{
|
{
|
||||||
|
|
@ -167,34 +186,27 @@ VcdReader::parseVar()
|
||||||
if (tokens.size() == 4
|
if (tokens.size() == 4
|
||||||
|| tokens.size() == 5) {
|
|| tokens.size() == 5) {
|
||||||
string type_name = tokens[0];
|
string type_name = tokens[0];
|
||||||
VcdVarType type = VcdVarType::wire;
|
VcdVarType type = vcd_var_type_map.find(type_name, VcdVarType::unknown);
|
||||||
if (type_name == "wire")
|
if (type == VcdVarType::unknown)
|
||||||
type = VcdVarType::wire;
|
report_->fileWarn(803, filename_, stmt_line_,
|
||||||
else if (type_name == "reg")
|
"Unknown variable type %s.",
|
||||||
type = VcdVarType::reg;
|
type_name.c_str());
|
||||||
else if (type_name == "parameter")
|
else {
|
||||||
type = VcdVarType::parameter;
|
int width = stoi(tokens[1]);
|
||||||
else if (type_name == "real")
|
string id = tokens[2];
|
||||||
type = VcdVarType::real;
|
string name;
|
||||||
else
|
|
||||||
report_->fileError(803, filename_, stmt_line_,
|
|
||||||
"Unknown variable type %s.",
|
|
||||||
type_name.c_str());
|
|
||||||
|
|
||||||
int width = stoi(tokens[1]);
|
for (string &context : scope_) {
|
||||||
string id = tokens[2];
|
name += context;
|
||||||
string name;
|
name += '/';
|
||||||
|
}
|
||||||
|
name += tokens[3];
|
||||||
|
// iverilog separates bus base name from bit range.
|
||||||
|
if (tokens.size() == 5)
|
||||||
|
name += tokens[4];
|
||||||
|
|
||||||
for (string &context : scope_) {
|
vcd_->makeVar(name, type, width, id);
|
||||||
name += context;
|
|
||||||
name += '/';
|
|
||||||
}
|
}
|
||||||
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
|
else
|
||||||
report_->fileError(804, filename_, stmt_line_, "Variable syntax error.");
|
report_->fileError(804, filename_, stmt_line_, "Variable syntax error.");
|
||||||
|
|
|
||||||
|
|
@ -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
|
void
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,6 @@ MakeTimingModel::makeLibrary()
|
||||||
*library_->units()->capacitanceUnit() = *default_lib->units()->capacitanceUnit();
|
*library_->units()->capacitanceUnit() = *default_lib->units()->capacitanceUnit();
|
||||||
*library_->units()->voltageUnit() = *default_lib->units()->voltageUnit();
|
*library_->units()->voltageUnit() = *default_lib->units()->voltageUnit();
|
||||||
*library_->units()->resistanceUnit() = *default_lib->units()->resistanceUnit();
|
*library_->units()->resistanceUnit() = *default_lib->units()->resistanceUnit();
|
||||||
*library_->units()->pullingResistanceUnit() = *default_lib->units()->pullingResistanceUnit();
|
|
||||||
*library_->units()->powerUnit() = *default_lib->units()->powerUnit();
|
*library_->units()->powerUnit() = *default_lib->units()->powerUnit();
|
||||||
*library_->units()->distanceUnit() = *default_lib->units()->distanceUnit();
|
*library_->units()->distanceUnit() = *default_lib->units()->distanceUnit();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,9 @@
|
||||||
|
|
||||||
namespace sta {
|
namespace sta {
|
||||||
|
|
||||||
using std::string;
|
|
||||||
using std::ofstream;
|
using std::ofstream;
|
||||||
using std::ifstream;
|
using std::ifstream;
|
||||||
using std::max;
|
using std::max;
|
||||||
using std::set;
|
|
||||||
|
|
||||||
typedef Map<string, StringVector*> CellSpicePortNames;
|
typedef Map<string, StringVector*> CellSpicePortNames;
|
||||||
typedef int Stage;
|
typedef int Stage;
|
||||||
|
|
@ -71,6 +69,7 @@ public:
|
||||||
const char *subckt_filename,
|
const char *subckt_filename,
|
||||||
const char *lib_subckt_filename,
|
const char *lib_subckt_filename,
|
||||||
const char *model_filename,
|
const char *model_filename,
|
||||||
|
StdStringSet *off_path_pin_names,
|
||||||
const char *power_name,
|
const char *power_name,
|
||||||
const char *gnd_name,
|
const char *gnd_name,
|
||||||
const StaState *sta);
|
const StaState *sta);
|
||||||
|
|
@ -99,12 +98,19 @@ private:
|
||||||
const Clock *clk,
|
const Clock *clk,
|
||||||
DcalcAPIndex dcalc_ap_index);
|
DcalcAPIndex dcalc_ap_index);
|
||||||
void writeStageParasitics(Stage stage);
|
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();
|
void writeSubckts();
|
||||||
set<string> findPathCellnames();
|
StdStringSet findPathCellnames();
|
||||||
void findPathCellSubckts(set<string> &path_cell_names);
|
void findPathCellSubckts(StdStringSet &path_cell_names);
|
||||||
void recordSpicePortNames(const char *cell_name,
|
void recordSpicePortNames(const char *cell_name,
|
||||||
StringVector &tokens);
|
StringVector &tokens);
|
||||||
float maxTime();
|
float maxTime();
|
||||||
|
float pathMaxTime();
|
||||||
const char *nodeName(ParasiticNode *node);
|
const char *nodeName(ParasiticNode *node);
|
||||||
void initNodeMap(const char *net_name);
|
void initNodeMap(const char *net_name);
|
||||||
const char *spiceTrans(const RiseFall *rf);
|
const char *spiceTrans(const RiseFall *rf);
|
||||||
|
|
@ -209,12 +215,14 @@ private:
|
||||||
const char *stageLoadPinName(Stage stage);
|
const char *stageLoadPinName(Stage stage);
|
||||||
LibertyCell *stageLibertyCell(Stage stage);
|
LibertyCell *stageLibertyCell(Stage stage);
|
||||||
Instance *stageInstance(Stage stage);
|
Instance *stageInstance(Stage stage);
|
||||||
|
StdStringSet stageOffPathPinNames(Stage stage);
|
||||||
|
|
||||||
Path *path_;
|
Path *path_;
|
||||||
const char *spice_filename_;
|
const char *spice_filename_;
|
||||||
const char *subckt_filename_;
|
const char *subckt_filename_;
|
||||||
const char *lib_subckt_filename_;
|
const char *lib_subckt_filename_;
|
||||||
const char *model_filename_;
|
const char *model_filename_;
|
||||||
|
StdStringSet *off_path_pin_names_;
|
||||||
const char *power_name_;
|
const char *power_name_;
|
||||||
const char *gnd_name_;
|
const char *gnd_name_;
|
||||||
|
|
||||||
|
|
@ -270,13 +278,14 @@ writePathSpice(Path *path,
|
||||||
const char *subckt_filename,
|
const char *subckt_filename,
|
||||||
const char *lib_subckt_filename,
|
const char *lib_subckt_filename,
|
||||||
const char *model_filename,
|
const char *model_filename,
|
||||||
const char *power_name,
|
StdStringSet *off_path_pin_names,
|
||||||
|
const char *power_name,
|
||||||
const char *gnd_name,
|
const char *gnd_name,
|
||||||
StaState *sta)
|
StaState *sta)
|
||||||
{
|
{
|
||||||
WritePathSpice writer(path, spice_filename, subckt_filename,
|
WritePathSpice writer(path, spice_filename, subckt_filename,
|
||||||
lib_subckt_filename, model_filename,
|
lib_subckt_filename, model_filename,
|
||||||
power_name, gnd_name, sta);
|
off_path_pin_names, power_name, gnd_name, sta);
|
||||||
writer.writeSpice();
|
writer.writeSpice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -285,6 +294,7 @@ WritePathSpice::WritePathSpice(Path *path,
|
||||||
const char *subckt_filename,
|
const char *subckt_filename,
|
||||||
const char *lib_subckt_filename,
|
const char *lib_subckt_filename,
|
||||||
const char *model_filename,
|
const char *model_filename,
|
||||||
|
StdStringSet *off_path_pin_names,
|
||||||
const char *power_name,
|
const char *power_name,
|
||||||
const char *gnd_name,
|
const char *gnd_name,
|
||||||
const StaState *sta) :
|
const StaState *sta) :
|
||||||
|
|
@ -294,6 +304,7 @@ WritePathSpice::WritePathSpice(Path *path,
|
||||||
subckt_filename_(subckt_filename),
|
subckt_filename_(subckt_filename),
|
||||||
lib_subckt_filename_(lib_subckt_filename),
|
lib_subckt_filename_(lib_subckt_filename),
|
||||||
model_filename_(model_filename),
|
model_filename_(model_filename),
|
||||||
|
off_path_pin_names_(off_path_pin_names),
|
||||||
power_name_(power_name),
|
power_name_(power_name),
|
||||||
gnd_name_(gnd_name),
|
gnd_name_(gnd_name),
|
||||||
path_expanded_(sta),
|
path_expanded_(sta),
|
||||||
|
|
@ -375,7 +386,7 @@ WritePathSpice::writeHeader()
|
||||||
streamPrint(spice_stream_, ".include \"%s\"\n", subckt_filename_stem.c_str());
|
streamPrint(spice_stream_, ".include \"%s\"\n", subckt_filename_stem.c_str());
|
||||||
|
|
||||||
float max_time = maxTime();
|
float max_time = maxTime();
|
||||||
float time_step = max_time / 1e+3;
|
float time_step = 1e-13;
|
||||||
streamPrint(spice_stream_, ".tran %.3g %.3g\n\n",
|
streamPrint(spice_stream_, ".tran %.3g %.3g\n\n",
|
||||||
time_step, max_time);
|
time_step, max_time);
|
||||||
streamPrint(spice_stream_, ".options nomod\n");
|
streamPrint(spice_stream_, ".options nomod\n");
|
||||||
|
|
@ -388,6 +399,9 @@ WritePathSpice::writePrintStmt()
|
||||||
for (Stage stage = stageFirst(); stage <= stageLast(); stage++) {
|
for (Stage stage = stageFirst(); stage <= stageLast(); stage++) {
|
||||||
streamPrint(spice_stream_, " v(%s)", stageDrvrPinName(stage));
|
streamPrint(spice_stream_, " v(%s)", stageDrvrPinName(stage));
|
||||||
streamPrint(spice_stream_, " v(%s)", stageLoadPinName(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");
|
streamPrint(spice_stream_, "\n\n");
|
||||||
}
|
}
|
||||||
|
|
@ -397,9 +411,6 @@ WritePathSpice::maxTime()
|
||||||
{
|
{
|
||||||
Stage input_stage = stageFirst();
|
Stage input_stage = stageFirst();
|
||||||
PathRef *input_path = stageDrvrPath(input_stage);
|
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)) {
|
if (input_path->isClock(this)) {
|
||||||
const Clock *clk = input_path->clock(this);
|
const Clock *clk = input_path->clock(this);
|
||||||
float period = clk->period();
|
float period = clk->period();
|
||||||
|
|
@ -407,13 +418,36 @@ WritePathSpice::maxTime()
|
||||||
float max_time = period * clk_cycle_count_ + first_edge_offset;
|
float max_time = period * clk_cycle_count_ + first_edge_offset;
|
||||||
return max_time;
|
return max_time;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
float end_slew = findSlew(path_);
|
return pathMaxTime();
|
||||||
float arrival = delayAsFloat(path_->arrival(this));
|
}
|
||||||
float max_time = railToRailSlew(input_slew, rf) + arrival
|
|
||||||
+ railToRailSlew(end_slew, rf);
|
// Make sure run time is long enough to see side load transitions along the path.
|
||||||
return max_time;
|
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);
|
||||||
|
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_);
|
||||||
|
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 = delayAsFloat(path->arrival(this)) + path_max_slew * 2.0;
|
||||||
|
if (path_max_time > max_time)
|
||||||
|
max_time = path_max_time;
|
||||||
}
|
}
|
||||||
|
return max_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
float
|
float
|
||||||
|
|
@ -441,13 +475,17 @@ WritePathSpice::writeStageInstances()
|
||||||
stageDrvrPinName(stage),
|
stageDrvrPinName(stage),
|
||||||
stageLoadPinName(stage),
|
stageLoadPinName(stage),
|
||||||
stage_cname);
|
stage_cname);
|
||||||
else
|
else {
|
||||||
streamPrint(spice_stream_, "x%s %s %s %s %s\n",
|
streamPrint(spice_stream_, "x%s %s %s %s",
|
||||||
stage_cname,
|
stage_cname,
|
||||||
stageGateInputPinName(stage),
|
stageGateInputPinName(stage),
|
||||||
stageDrvrPinName(stage),
|
stageDrvrPinName(stage),
|
||||||
stageLoadPinName(stage),
|
stageLoadPinName(stage));
|
||||||
stage_cname);
|
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");
|
streamPrint(spice_stream_, "\n");
|
||||||
}
|
}
|
||||||
|
|
@ -855,11 +893,16 @@ WritePathSpice::writeGateStage(Stage stage)
|
||||||
const char *drvr_pin_name = stageDrvrPinName(stage);
|
const char *drvr_pin_name = stageDrvrPinName(stage);
|
||||||
const Pin *load_pin = stageLoadPin(stage);
|
const Pin *load_pin = stageLoadPin(stage);
|
||||||
const char *load_pin_name = stageLoadPinName(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,
|
stage,
|
||||||
input_pin_name,
|
input_pin_name,
|
||||||
drvr_pin_name,
|
drvr_pin_name,
|
||||||
load_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.
|
// Driver subckt call.
|
||||||
Instance *inst = stageInstance(stage);
|
Instance *inst = stageInstance(stage);
|
||||||
LibertyPort *input_port = stageGateInputPort(stage);
|
LibertyPort *input_port = stageGateInputPort(stage);
|
||||||
|
|
@ -1300,60 +1343,77 @@ WritePathSpice::writeStageParasitics(Stage stage)
|
||||||
{
|
{
|
||||||
PathRef *drvr_path = stageDrvrPath(stage);
|
PathRef *drvr_path = stageDrvrPath(stage);
|
||||||
Pin *drvr_pin = stageDrvrPin(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);
|
DcalcAnalysisPt *dcalc_ap = drvr_path->dcalcAnalysisPt(this);
|
||||||
ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt();
|
ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt();
|
||||||
Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap);
|
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<const Pin*> reachable_pins;
|
Set<const Pin*> reachable_pins;
|
||||||
int res_index = 1;
|
int res_index = 1;
|
||||||
int cap_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.
|
// Sort devices for consistent regression results.
|
||||||
Vector<ParasiticDevice*> devices;
|
Vector<ParasiticDevice*> devices;
|
||||||
ParasiticDeviceIterator *device_iter1 = parasitics_->deviceIterator(parasitic);
|
ParasiticDeviceIterator *device_iter1 = parasitics_->deviceIterator(parasitic);
|
||||||
while (device_iter1->hasNext()) {
|
while (device_iter1->hasNext()) {
|
||||||
ParasiticDevice *device = device_iter1->next();
|
ParasiticDevice *device = device_iter1->next();
|
||||||
devices.push_back(device);
|
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;
|
else if (parasitics_->isCouplingCap(device)) {
|
||||||
|
// Ground coupling caps for now.
|
||||||
sort(devices, ParasiticDeviceLess(parasitics_));
|
ParasiticNode *node1 = parasitics_->node1(device);
|
||||||
|
float cap = parasitics_->value(device, parasitic_ap);
|
||||||
for (ParasiticDevice *device : devices) {
|
streamPrint(spice_stream_, "C%d %s 0 %.3e\n",
|
||||||
float resistance = parasitics_->value(device, parasitic_ap);
|
cap_index,
|
||||||
if (parasitics_->isResistor(device)) {
|
nodeName(node1),
|
||||||
ParasiticNode *node1 = parasitics_->node1(device);
|
cap);
|
||||||
ParasiticNode *node2 = parasitics_->node2(device);
|
cap_index++;
|
||||||
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
|
|
||||||
streamPrint(spice_stream_, "* No parasitics found for this net.\n");
|
|
||||||
|
|
||||||
// Add resistors from drvr to load for missing parasitic connections.
|
// Add resistors from drvr to load for missing parasitic connections.
|
||||||
auto pin_iter = network_->connectedPinIterator(drvr_pin);
|
auto pin_iter = network_->connectedPinIterator(drvr_pin);
|
||||||
|
|
@ -1373,30 +1433,107 @@ WritePathSpice::writeStageParasitics(Stage stage)
|
||||||
}
|
}
|
||||||
delete pin_iter;
|
delete pin_iter;
|
||||||
|
|
||||||
if (parasitic) {
|
// Sort node capacitors for consistent regression results.
|
||||||
// Sort node capacitors for consistent regression results.
|
Vector<ParasiticNode*> nodes;
|
||||||
Vector<ParasiticNode*> nodes;
|
ParasiticNodeIterator *node_iter = parasitics_->nodeIterator(parasitic);
|
||||||
ParasiticNodeIterator *node_iter = parasitics_->nodeIterator(parasitic);
|
while (node_iter->hasNext()) {
|
||||||
while (node_iter->hasNext()) {
|
ParasiticNode *node = node_iter->next();
|
||||||
ParasiticNode *node = node_iter->next();
|
nodes.push_back(node);
|
||||||
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(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
|
void
|
||||||
|
|
@ -1433,7 +1570,7 @@ WritePathSpice::nodeName(ParasiticNode *node)
|
||||||
void
|
void
|
||||||
WritePathSpice::writeSubckts()
|
WritePathSpice::writeSubckts()
|
||||||
{
|
{
|
||||||
set<string> path_cell_names = findPathCellnames();
|
StdStringSet path_cell_names = findPathCellnames();
|
||||||
findPathCellSubckts(path_cell_names);
|
findPathCellSubckts(path_cell_names);
|
||||||
|
|
||||||
ifstream lib_subckts_stream(lib_subckt_filename_);
|
ifstream lib_subckts_stream(lib_subckt_filename_);
|
||||||
|
|
@ -1489,10 +1626,10 @@ WritePathSpice::writeSubckts()
|
||||||
throw FileNotReadable(lib_subckt_filename_);
|
throw FileNotReadable(lib_subckt_filename_);
|
||||||
}
|
}
|
||||||
|
|
||||||
set<string>
|
StdStringSet
|
||||||
WritePathSpice::findPathCellnames()
|
WritePathSpice::findPathCellnames()
|
||||||
{
|
{
|
||||||
set<string> path_cell_names;
|
StdStringSet path_cell_names;
|
||||||
for (Stage stage = stageFirst(); stage <= stageLast(); stage++) {
|
for (Stage stage = stageFirst(); stage <= stageLast(); stage++) {
|
||||||
TimingArc *arc = stageGateArc(stage);
|
TimingArc *arc = stageGateArc(stage);
|
||||||
if (arc) {
|
if (arc) {
|
||||||
|
|
@ -1520,7 +1657,7 @@ WritePathSpice::findPathCellnames()
|
||||||
|
|
||||||
// Subckts can call subckts (asap7).
|
// Subckts can call subckts (asap7).
|
||||||
void
|
void
|
||||||
WritePathSpice::findPathCellSubckts(set<string> &path_cell_names)
|
WritePathSpice::findPathCellSubckts(StdStringSet &path_cell_names)
|
||||||
{
|
{
|
||||||
ifstream lib_subckts_stream(lib_subckt_filename_);
|
ifstream lib_subckts_stream(lib_subckt_filename_);
|
||||||
if (lib_subckts_stream.is_open()) {
|
if (lib_subckts_stream.is_open()) {
|
||||||
|
|
@ -1731,6 +1868,26 @@ WritePathSpice::stageLoadPinName(Stage stage)
|
||||||
return network_->pathName(pin);
|
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 *
|
Instance *
|
||||||
WritePathSpice::stageInstance(Stage stage)
|
WritePathSpice::stageInstance(Stage stage)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <regex>
|
|
||||||
#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<string> StringVector;
|
|
||||||
typedef Map<string, StringVector*> CellSpicePortNames;
|
|
||||||
typedef int Stage;
|
|
||||||
typedef Map<ParasiticNode*, int> 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<LibertyPort*, LogicValue> 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<ParasiticDevice*> ParasiticDeviceSet;
|
|
||||||
typedef Set<ParasiticNode*> 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 <cell_name> [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
|
|
||||||
|
|
@ -114,7 +114,7 @@ define_cmd_args "report_units" {}
|
||||||
proc report_units { args } {
|
proc report_units { args } {
|
||||||
check_argc_eq0 "report_units" $args
|
check_argc_eq0 "report_units" $args
|
||||||
foreach unit {"time" "capacitance" "resistance" "voltage" "current" "power" "distance"} {
|
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]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
79
tcl/StaTcl.i
79
tcl/StaTcl.i
|
|
@ -254,6 +254,26 @@ tclListSeqConstChar(Tcl_Obj *const source,
|
||||||
return nullptr;
|
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.
|
// Sequence out to tcl list.
|
||||||
|
|
@ -425,6 +445,10 @@ using namespace sta;
|
||||||
$1 = tclListSeqConstChar($input, interp);
|
$1 = tclListSeqConstChar($input, interp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%typemap(in) StdStringSet* {
|
||||||
|
$1 = tclListSetStdString($input, interp);
|
||||||
|
}
|
||||||
|
|
||||||
%typemap(out) StringSeq* {
|
%typemap(out) StringSeq* {
|
||||||
StringSeq *strs = $1;
|
StringSeq *strs = $1;
|
||||||
Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
|
Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
|
||||||
|
|
@ -885,24 +909,26 @@ using namespace sta;
|
||||||
|
|
||||||
%typemap(out) Table1 {
|
%typemap(out) Table1 {
|
||||||
Table1 &table = $1;
|
Table1 &table = $1;
|
||||||
Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr);
|
if (table.axis1()) {
|
||||||
Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr);
|
Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr);
|
||||||
for (float f : *table.axis1()->values()) {
|
Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr);
|
||||||
Tcl_Obj *obj = Tcl_NewDoubleObj(f);
|
for (float f : *table.axis1()->values()) {
|
||||||
Tcl_ListObjAppendElement(interp, list1, obj);
|
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* {
|
%typemap(out) const Table1* {
|
||||||
Table1 *table = $1;
|
const Table1 *table = $1;
|
||||||
Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr);
|
Tcl_Obj *list3 = Tcl_NewListObj(0, nullptr);
|
||||||
if (table) {
|
if (table) {
|
||||||
Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr);
|
Tcl_Obj *list1 = Tcl_NewListObj(0, nullptr);
|
||||||
|
|
@ -3963,11 +3989,11 @@ set_cmd_unit_suffix(const char *unit_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
unit_scale_abreviation(const char *unit_name)
|
unit_scale_abbreviation (const char *unit_name)
|
||||||
{
|
{
|
||||||
Unit *unit = Sta::sta()->units()->find(unit_name);
|
Unit *unit = Sta::sta()->units()->find(unit_name);
|
||||||
if (unit)
|
if (unit)
|
||||||
return unit->scaleAbreviation();
|
return unit->scaleAbbreviation();
|
||||||
else
|
else
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
@ -3982,6 +4008,16 @@ unit_suffix(const char *unit_name)
|
||||||
return "";
|
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 *
|
VertexIterator *
|
||||||
|
|
@ -4865,12 +4901,13 @@ write_path_spice_cmd(PathRef *path,
|
||||||
const char *subckt_filename,
|
const char *subckt_filename,
|
||||||
const char *lib_subckt_filename,
|
const char *lib_subckt_filename,
|
||||||
const char *model_filename,
|
const char *model_filename,
|
||||||
|
StdStringSet *off_path_pins,
|
||||||
const char *power_name,
|
const char *power_name,
|
||||||
const char *gnd_name)
|
const char *gnd_name)
|
||||||
{
|
{
|
||||||
Sta *sta = Sta::sta();
|
Sta *sta = Sta::sta();
|
||||||
writePathSpice(path, spice_filename, subckt_filename,
|
writePathSpice(path, spice_filename, subckt_filename,
|
||||||
lib_subckt_filename, model_filename,
|
lib_subckt_filename, model_filename, off_path_pins,
|
||||||
power_name, gnd_name, sta);
|
power_name, gnd_name, sta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5673,7 +5710,7 @@ voltage_waveform(float in_slew,
|
||||||
return Table1();
|
return Table1();
|
||||||
}
|
}
|
||||||
|
|
||||||
Table1
|
const Table1 *
|
||||||
current_waveform(float in_slew,
|
current_waveform(float in_slew,
|
||||||
float load_cap)
|
float load_cap)
|
||||||
{
|
{
|
||||||
|
|
@ -5681,11 +5718,11 @@ current_waveform(float in_slew,
|
||||||
if (gate_model) {
|
if (gate_model) {
|
||||||
OutputWaveforms *waveforms = gate_model->outputWaveforms();
|
OutputWaveforms *waveforms = gate_model->outputWaveforms();
|
||||||
if (waveforms) {
|
if (waveforms) {
|
||||||
Table1 waveform = waveforms->currentWaveform(in_slew, load_cap);
|
const Table1 *waveform = waveforms->currentWaveform(in_slew, load_cap);
|
||||||
return waveform;
|
return waveform;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Table1();
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // TimingArc methods
|
} // TimingArc methods
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ proc write_path_spice { args } {
|
||||||
set spice_file [file join $spice_dir "$path_name.sp"]
|
set spice_file [file join $spice_dir "$path_name.sp"]
|
||||||
set subckt_file [file join $spice_dir "$path_name.subckt"]
|
set subckt_file [file join $spice_dir "$path_name.subckt"]
|
||||||
write_path_spice_cmd $path $spice_file $subckt_file \
|
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
|
incr path_index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,7 @@ VerilogWriter::writeWireDcls(Instance *inst)
|
||||||
{
|
{
|
||||||
Cell *cell = network_->cell(inst);
|
Cell *cell = network_->cell(inst);
|
||||||
char escape = network_->pathEscape();
|
char escape = network_->pathEscape();
|
||||||
Map<const char*, BusIndexRange, CharPtrLess> bus_ranges;
|
Map<string, BusIndexRange, std::less<string>> bus_ranges;
|
||||||
NetIterator *net_iter = network_->netIterator(inst);
|
NetIterator *net_iter = network_->netIterator(inst);
|
||||||
while (net_iter->hasNext()) {
|
while (net_iter->hasNext()) {
|
||||||
Net *net = net_iter->next();
|
Net *net = net_iter->next();
|
||||||
|
|
@ -243,7 +243,7 @@ VerilogWriter::writeWireDcls(Instance *inst)
|
||||||
string bus_name;
|
string bus_name;
|
||||||
int index;
|
int index;
|
||||||
parseBusName(net_name, '[', ']', escape, is_bus, bus_name, 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.first = max(range.first, index);
|
||||||
range.second = min(range.second, index);
|
range.second = min(range.second, index);
|
||||||
}
|
}
|
||||||
|
|
@ -256,7 +256,7 @@ VerilogWriter::writeWireDcls(Instance *inst)
|
||||||
delete net_iter;
|
delete net_iter;
|
||||||
|
|
||||||
for (auto name_range : bus_ranges) {
|
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;
|
const BusIndexRange &range = name_range.second;
|
||||||
string net_vname = netVerilogName(bus_name, network_->pathEscape());
|
string net_vname = netVerilogName(bus_name, network_->pathEscape());
|
||||||
fprintf(stream_, " wire [%d:%d] %s;\n",
|
fprintf(stream_, " wire [%d:%d] %s;\n",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue