2019-01-04 01:14:15 +01:00
|
|
|
// OpenSTA, Static Timing Analyzer
|
2020-03-07 03:50:37 +01:00
|
|
|
// Copyright (c) 2020, Parallax Software, Inc.
|
2019-01-04 01:14:15 +01:00
|
|
|
//
|
|
|
|
|
// 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/>.
|
|
|
|
|
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "WritePathSpice.hh"
|
2020-04-05 20:35:51 +02:00
|
|
|
|
2019-01-04 01:14:15 +01:00
|
|
|
#include <string>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <fstream>
|
2020-04-05 20:35:51 +02:00
|
|
|
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "Debug.hh"
|
|
|
|
|
#include "Error.hh"
|
|
|
|
|
#include "Report.hh"
|
|
|
|
|
#include "StringUtil.hh"
|
|
|
|
|
#include "FuncExpr.hh"
|
|
|
|
|
#include "Units.hh"
|
|
|
|
|
#include "Sequential.hh"
|
|
|
|
|
#include "TableModel.hh"
|
|
|
|
|
#include "Liberty.hh"
|
|
|
|
|
#include "TimingArc.hh"
|
|
|
|
|
#include "PortDirection.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 "Sim.hh"
|
2019-01-04 01:14:15 +01:00
|
|
|
|
|
|
|
|
namespace sta {
|
|
|
|
|
|
|
|
|
|
using std::string;
|
|
|
|
|
using std::ofstream;
|
|
|
|
|
using std::ifstream;
|
|
|
|
|
|
|
|
|
|
typedef Map<string, StringVector*> CellSpicePortNames;
|
|
|
|
|
typedef int Stage;
|
|
|
|
|
typedef Map<ParasiticNode*, int> ParasiticNodeMap;
|
2019-01-20 18:44:24 +01:00
|
|
|
typedef Map<LibertyPort*, LogicValue> LibertyPortLogicValues;
|
2019-01-04 01:14:15 +01:00
|
|
|
|
|
|
|
|
void
|
|
|
|
|
streamPrint(ofstream &stream,
|
|
|
|
|
const char *fmt,
|
|
|
|
|
...) __attribute__((format (printf, 2, 3)));
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
class WritePathSpice : public StaState
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
WritePathSpice(Path *path,
|
|
|
|
|
const char *spice_filename,
|
|
|
|
|
const char *subckt_filename,
|
|
|
|
|
const char *lib_subckt_filename,
|
|
|
|
|
const char *model_filename,
|
|
|
|
|
const char *power_name,
|
|
|
|
|
const char *gnd_name,
|
|
|
|
|
const StaState *sta);
|
|
|
|
|
~WritePathSpice();
|
2019-08-08 23:13:02 +02:00
|
|
|
void writeSpice();
|
2019-01-04 01:14:15 +01:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void writeHeader();
|
|
|
|
|
void writeStageInstances();
|
|
|
|
|
void writeInputSource();
|
2019-01-23 05:41:32 +01:00
|
|
|
void writeStepVoltSource(const Pin *pin,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *rf,
|
2019-01-23 05:41:32 +01:00
|
|
|
float slew,
|
|
|
|
|
float time,
|
|
|
|
|
int &volt_index);
|
2019-01-04 01:14:15 +01:00
|
|
|
void writeStageSubckts();
|
|
|
|
|
void writeInputStage(Stage stage);
|
|
|
|
|
void writeMeasureStmts();
|
|
|
|
|
void writeMeasureStmt(const Pin *pin);
|
|
|
|
|
void writeGateStage(Stage stage);
|
2019-02-21 17:00:06 +01:00
|
|
|
void writeSubcktInst(const Pin *input_pin);
|
|
|
|
|
void writeSubcktInstVoltSrcs(Stage stage,
|
|
|
|
|
const Pin *input_pin,
|
|
|
|
|
int &volt_index,
|
|
|
|
|
LibertyPortLogicValues &port_values,
|
|
|
|
|
const Clock *clk,
|
|
|
|
|
DcalcAPIndex dcalc_ap_index);
|
2019-01-04 01:14:15 +01:00
|
|
|
void writeStageParasitics(Stage stage);
|
|
|
|
|
void writeSubckts();
|
|
|
|
|
void findPathCellnames(// Return values.
|
|
|
|
|
StringSet &path_cell_names);
|
|
|
|
|
void recordSpicePortNames(const char *cell_name,
|
|
|
|
|
StringVector &tokens);
|
|
|
|
|
float maxTime();
|
|
|
|
|
const char *nodeName(ParasiticNode *node);
|
|
|
|
|
void initNodeMap(const char *net_name);
|
2019-11-11 23:30:19 +01:00
|
|
|
const char *spiceTrans(const RiseFall *rf);
|
2019-01-04 01:14:15 +01:00
|
|
|
void writeMeasureDelayStmt(Stage stage,
|
2019-01-20 18:44:24 +01:00
|
|
|
Path *from_path,
|
|
|
|
|
Path *to_path);
|
2019-01-04 01:14:15 +01:00
|
|
|
void writeMeasureSlewStmt(Stage stage,
|
|
|
|
|
Path *path);
|
2019-02-21 17:00:06 +01:00
|
|
|
void gatePortValues(Stage stage,
|
|
|
|
|
// Return values.
|
|
|
|
|
LibertyPortLogicValues &port_values,
|
|
|
|
|
const Clock *&clk,
|
|
|
|
|
DcalcAPIndex &dcalc_ap_index);
|
|
|
|
|
void regPortValues(Stage stage,
|
|
|
|
|
// Return values.
|
|
|
|
|
LibertyPortLogicValues &port_values,
|
|
|
|
|
const Clock *&clk,
|
|
|
|
|
DcalcAPIndex &dcalc_ap_index);
|
|
|
|
|
void gatePortValues(const Instance *inst,
|
|
|
|
|
FuncExpr *expr,
|
|
|
|
|
LibertyPort *input_port,
|
|
|
|
|
// Return values.
|
|
|
|
|
LibertyPortLogicValues &port_values);
|
|
|
|
|
void seqPortValues(Sequential *seq,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *rf,
|
2019-02-21 17:00:06 +01:00
|
|
|
// Return values.
|
|
|
|
|
LibertyPortLogicValues &port_values);
|
2019-01-23 05:41:32 +01:00
|
|
|
void writeInputWaveform();
|
|
|
|
|
void writeClkWaveform();
|
2019-11-11 23:30:19 +01:00
|
|
|
void writeWaveformEdge(const RiseFall *rf,
|
2019-01-23 05:41:32 +01:00
|
|
|
float time,
|
|
|
|
|
float slew);
|
|
|
|
|
void writeClkedStepSource(const Pin *pin,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *rf,
|
2019-01-23 05:41:32 +01:00
|
|
|
const Clock *clk,
|
|
|
|
|
DcalcAPIndex dcalc_ap_index,
|
|
|
|
|
int &volt_index);
|
|
|
|
|
float clkWaveformTImeOffset(const Clock *clk);
|
|
|
|
|
float findSlew(Path *path);
|
|
|
|
|
float findSlew(Path *path,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *rf,
|
2019-01-27 08:03:01 +01:00
|
|
|
TimingArc *next_arc);
|
2019-01-23 05:41:32 +01:00
|
|
|
float findSlew(Vertex *vertex,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *rf,
|
2019-01-27 08:03:01 +01:00
|
|
|
TimingArc *next_arc,
|
2019-01-23 05:41:32 +01:00
|
|
|
DcalcAPIndex dcalc_ap_index);
|
|
|
|
|
LibertyPort *onePort(FuncExpr *expr);
|
2019-04-14 00:01:14 +02:00
|
|
|
void writeVoltageSource(const char *inst_name,
|
|
|
|
|
const char *port_name,
|
|
|
|
|
float voltage,
|
|
|
|
|
int &volt_index);
|
2019-01-23 05:41:32 +01:00
|
|
|
void writeVoltageSource(LibertyCell *cell,
|
|
|
|
|
const char *inst_name,
|
|
|
|
|
const char *subckt_port_name,
|
|
|
|
|
const char *pg_port_name,
|
2019-04-14 00:01:14 +02:00
|
|
|
float voltage,
|
2019-01-23 05:41:32 +01:00
|
|
|
int &volt_index);
|
2019-01-27 08:03:01 +01:00
|
|
|
float slewAxisMinValue(TimingArc *arc);
|
2019-04-14 00:01:14 +02:00
|
|
|
float pgPortVoltage(LibertyPgPort *pg_port);
|
2019-01-04 01:14:15 +01:00
|
|
|
|
|
|
|
|
// Stage "accessors".
|
2019-01-20 18:44:24 +01:00
|
|
|
//
|
|
|
|
|
// stage
|
|
|
|
|
// |---------------|
|
2019-03-04 02:50:56 +01:00
|
|
|
// |\ |\ .
|
2019-01-20 18:44:24 +01:00
|
|
|
// -------| >---/\/\/----| >---
|
|
|
|
|
// gate |/ drvr load|/
|
|
|
|
|
// input
|
|
|
|
|
//
|
2019-02-21 17:00:06 +01:00
|
|
|
// A path from an input port has no GateInputPath (the input port is the drvr).
|
2019-01-04 01:14:15 +01:00
|
|
|
// Internally a stage index from stageFirst() to stageLast()
|
|
|
|
|
// is turned into an index into path_expanded_.
|
2019-01-20 18:44:24 +01:00
|
|
|
//
|
2019-01-04 01:14:15 +01:00
|
|
|
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);
|
2019-01-23 05:41:32 +01:00
|
|
|
Pin *stageGateInputPin(Stage stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
Pin *stageDrvrPin(Stage stage);
|
2019-02-21 17:00:06 +01:00
|
|
|
LibertyPort *stageGateInputPort(Stage stage);
|
|
|
|
|
LibertyPort *stageDrvrPort(Stage stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
Pin *stageLoadPin(Stage stage);
|
|
|
|
|
const char *stageGateInputPinName(Stage stage);
|
|
|
|
|
const char *stageDrvrPinName(Stage stage);
|
|
|
|
|
const char *stageLoadPinName(Stage stage);
|
2019-02-21 17:00:06 +01:00
|
|
|
LibertyCell *stageLibertyCell(Stage stage);
|
|
|
|
|
Instance *stageInstance(Stage stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
|
|
|
|
|
Path *path_;
|
|
|
|
|
const char *spice_filename_;
|
|
|
|
|
const char *subckt_filename_;
|
|
|
|
|
const char *lib_subckt_filename_;
|
|
|
|
|
const char *model_filename_;
|
|
|
|
|
const char *power_name_;
|
|
|
|
|
const char *gnd_name_;
|
|
|
|
|
|
|
|
|
|
ofstream spice_stream_;
|
|
|
|
|
PathExpanded path_expanded_;
|
|
|
|
|
CellSpicePortNames cell_spice_port_names_;
|
|
|
|
|
ParasiticNodeMap node_map_;
|
|
|
|
|
int next_node_index_;
|
|
|
|
|
const char *net_name_;
|
|
|
|
|
float power_voltage_;
|
|
|
|
|
float gnd_voltage_;
|
2019-01-23 05:41:32 +01:00
|
|
|
LibertyLibrary *default_library_;
|
2019-01-04 01:14:15 +01:00
|
|
|
// Resistance to use to simulate a short circuit between spice nodes.
|
2019-01-20 18:44:24 +01:00
|
|
|
float short_ckt_resistance_;
|
2019-01-23 05:41:32 +01:00
|
|
|
// Input clock waveform cycles.
|
|
|
|
|
int clk_cycle_count_;
|
2019-01-04 01:14:15 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2020-02-16 02:10:24 +01:00
|
|
|
class SubcktEndsMissing : public Exception
|
2019-01-04 01:14:15 +01:00
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SubcktEndsMissing(const char *cell_name,
|
2019-08-08 23:13:02 +02:00
|
|
|
const char *subckt_filename);
|
2020-02-16 02:10:24 +01:00
|
|
|
const char *what() const noexcept;
|
2019-01-04 01:14:15 +01:00
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
string what_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SubcktEndsMissing::SubcktEndsMissing(const char *cell_name,
|
2020-02-16 02:10:24 +01:00
|
|
|
const char *subckt_filename) :
|
|
|
|
|
Exception()
|
2019-01-04 01:14:15 +01:00
|
|
|
{
|
2020-03-07 22:44:52 +01:00
|
|
|
what_ = "spice subckt for cell ";
|
2019-01-04 01:14:15 +01:00
|
|
|
what_ += cell_name;
|
|
|
|
|
what_ += " missing .ends in ";
|
|
|
|
|
what_ += subckt_filename;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
2020-02-16 02:10:24 +01:00
|
|
|
SubcktEndsMissing::what() const noexcept
|
2019-01-04 01:14:15 +01:00
|
|
|
{
|
|
|
|
|
return what_.c_str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
writePathSpice(Path *path,
|
|
|
|
|
const char *spice_filename,
|
|
|
|
|
const char *subckt_filename,
|
|
|
|
|
const char *lib_subckt_filename,
|
|
|
|
|
const char *model_filename,
|
|
|
|
|
const char *power_name,
|
|
|
|
|
const char *gnd_name,
|
|
|
|
|
StaState *sta)
|
|
|
|
|
{
|
|
|
|
|
WritePathSpice writer(path, spice_filename, subckt_filename,
|
|
|
|
|
lib_subckt_filename, model_filename,
|
|
|
|
|
power_name, gnd_name, sta);
|
|
|
|
|
writer.writeSpice();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WritePathSpice::WritePathSpice(Path *path,
|
|
|
|
|
const char *spice_filename,
|
|
|
|
|
const char *subckt_filename,
|
|
|
|
|
const char *lib_subckt_filename,
|
|
|
|
|
const char *model_filename,
|
|
|
|
|
const char *power_name,
|
|
|
|
|
const char *gnd_name,
|
|
|
|
|
const StaState *sta) :
|
|
|
|
|
StaState(sta),
|
|
|
|
|
path_(path),
|
|
|
|
|
spice_filename_(spice_filename),
|
|
|
|
|
subckt_filename_(subckt_filename),
|
|
|
|
|
lib_subckt_filename_(lib_subckt_filename),
|
|
|
|
|
model_filename_(model_filename),
|
|
|
|
|
power_name_(power_name),
|
|
|
|
|
gnd_name_(gnd_name),
|
|
|
|
|
path_expanded_(sta),
|
2019-03-13 01:25:53 +01:00
|
|
|
net_name_(nullptr),
|
2019-01-23 05:41:32 +01:00
|
|
|
default_library_(network_->defaultLibertyLibrary()),
|
|
|
|
|
short_ckt_resistance_(.0001),
|
|
|
|
|
clk_cycle_count_(3)
|
2019-01-04 01:14:15 +01:00
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
bool exists;
|
|
|
|
|
default_library_->supplyVoltage(power_name_, power_voltage_, exists);
|
2019-04-14 00:01:14 +02:00
|
|
|
if (!exists) {
|
2019-11-11 23:53:29 +01:00
|
|
|
DcalcAnalysisPt *dcalc_ap = path_->dcalcAnalysisPt(this);
|
|
|
|
|
const OperatingConditions *op_cond = dcalc_ap->operatingConditions();
|
2019-04-14 00:01:14 +02:00
|
|
|
if (op_cond == nullptr)
|
|
|
|
|
op_cond = network_->defaultLibertyLibrary()->defaultOperatingConditions();
|
|
|
|
|
power_voltage_ = op_cond->voltage();
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
default_library_->supplyVoltage(gnd_name_, gnd_voltage_, exists);
|
|
|
|
|
if (!exists)
|
2019-04-14 00:01:14 +02:00
|
|
|
gnd_voltage_ = 0.0;
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WritePathSpice::~WritePathSpice()
|
|
|
|
|
{
|
2019-01-17 00:37:31 +01:00
|
|
|
stringDelete(net_name_);
|
2019-01-04 01:14:15 +01:00
|
|
|
cell_spice_port_names_.deleteContents();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::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();
|
|
|
|
|
writeMeasureStmts();
|
|
|
|
|
writeInputSource();
|
|
|
|
|
writeStageSubckts();
|
|
|
|
|
streamPrint(spice_stream_, ".end\n");
|
|
|
|
|
spice_stream_.close();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
throw FileNotWritable(spice_filename_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::writeHeader()
|
|
|
|
|
{
|
2019-11-11 23:53:29 +01:00
|
|
|
const MinMax *min_max = path_->minMax(this);
|
|
|
|
|
Pvt *pvt = sdc_->operatingConditions(min_max);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (pvt == nullptr)
|
2019-01-23 05:41:32 +01:00
|
|
|
pvt = default_library_->defaultOperatingConditions();
|
2019-02-19 17:42:28 +01:00
|
|
|
Path *start_path = path_expanded_.startPath();
|
|
|
|
|
streamPrint(spice_stream_, "* Path from %s %s to %s %s\n",
|
|
|
|
|
network_->pathName(start_path->pin(this)),
|
|
|
|
|
start_path->transition(this)->asString(),
|
2019-02-19 07:32:59 +01:00
|
|
|
network_->pathName(path_->pin(this)),
|
|
|
|
|
path_->transition(this)->asString());
|
2019-11-11 23:53:29 +01:00
|
|
|
float temp = pvt->temperature();
|
2019-01-04 01:14:15 +01:00
|
|
|
streamPrint(spice_stream_, ".temp %.1f\n", temp);
|
|
|
|
|
streamPrint(spice_stream_, ".include \"%s\"\n", model_filename_);
|
|
|
|
|
streamPrint(spice_stream_, ".include \"%s\"\n", subckt_filename_);
|
|
|
|
|
|
2019-11-11 23:53:29 +01:00
|
|
|
float max_time = maxTime();
|
|
|
|
|
float time_step = max_time / 1e+3;
|
2019-01-04 01:14:15 +01:00
|
|
|
streamPrint(spice_stream_, ".tran %.3g %.3g\n\n",
|
|
|
|
|
time_step, max_time);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
WritePathSpice::maxTime()
|
|
|
|
|
{
|
2019-11-11 23:53:29 +01:00
|
|
|
Stage input_stage = stageFirst();
|
|
|
|
|
PathRef *input_path = stageDrvrPath(input_stage);
|
|
|
|
|
const RiseFall *rf = input_path->transition(this);
|
|
|
|
|
TimingArc *next_arc = stageGateArc(input_stage + 1);
|
2019-12-09 20:06:49 +01:00
|
|
|
float input_slew = findSlew(input_path, rf, next_arc);
|
2019-01-23 05:41:32 +01:00
|
|
|
if (input_path->isClock(this)) {
|
2019-11-11 23:53:29 +01:00
|
|
|
Clock *clk = input_path->clock(this);
|
|
|
|
|
float period = clk->period();
|
2019-01-23 05:41:32 +01:00
|
|
|
float first_edge_offset = period / 10;
|
2019-11-11 23:53:29 +01:00
|
|
|
float max_time = period * clk_cycle_count_ + first_edge_offset;
|
2019-01-23 05:41:32 +01:00
|
|
|
return max_time;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2019-12-09 20:06:49 +01:00
|
|
|
float end_slew = findSlew(path_);
|
2019-11-11 23:53:29 +01:00
|
|
|
float max_time = delayAsFloat(input_slew
|
|
|
|
|
+ path_->arrival(this)
|
|
|
|
|
+ end_slew * 2) * 1.5;
|
2019-01-23 05:41:32 +01:00
|
|
|
return max_time;
|
|
|
|
|
}
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::writeStageInstances()
|
|
|
|
|
{
|
|
|
|
|
streamPrint(spice_stream_, "*****************\n");
|
|
|
|
|
streamPrint(spice_stream_, "* Stage instances\n");
|
|
|
|
|
streamPrint(spice_stream_, "*****************\n\n");
|
|
|
|
|
|
|
|
|
|
for (Stage stage = stageFirst(); stage <= stageLast(); stage++) {
|
2019-11-11 23:53:29 +01:00
|
|
|
const char *stage_name = stageName(stage).c_str();
|
2019-01-04 01:14:15 +01:00
|
|
|
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,
|
|
|
|
|
stageGateInputPinName(stage),
|
|
|
|
|
stageDrvrPinName(stage),
|
|
|
|
|
stageLoadPinName(stage),
|
|
|
|
|
stage_name);
|
|
|
|
|
}
|
|
|
|
|
streamPrint(spice_stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
WritePathSpice::pgPortVoltage(LibertyPgPort *pg_port)
|
|
|
|
|
{
|
2019-04-14 00:01:14 +02:00
|
|
|
LibertyLibrary *liberty = pg_port->cell()->libertyLibrary();
|
|
|
|
|
float voltage = 0.0;
|
2019-03-13 01:25:53 +01:00
|
|
|
bool exists;
|
2019-11-11 23:53:29 +01:00
|
|
|
const char *voltage_name = pg_port->voltageName();
|
2019-04-14 00:01:14 +02:00
|
|
|
if (voltage_name) {
|
|
|
|
|
liberty->supplyVoltage(voltage_name, voltage, exists);
|
|
|
|
|
if (!exists) {
|
|
|
|
|
if (stringEqual(voltage_name, power_name_))
|
|
|
|
|
voltage = power_voltage_;
|
|
|
|
|
else if (stringEqual(voltage_name, gnd_name_))
|
|
|
|
|
voltage = gnd_voltage_;
|
|
|
|
|
else
|
|
|
|
|
report_->error("pg_pin %s/%s voltage %s not found,\n",
|
|
|
|
|
pg_port->cell()->name(),
|
|
|
|
|
pg_port->name(),
|
|
|
|
|
voltage_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
report_->error("Liberty pg_port %s/%s missing voltage_name attribute,\n",
|
|
|
|
|
pg_port->cell()->name(),
|
|
|
|
|
pg_port->name());
|
2019-01-04 01:14:15 +01:00
|
|
|
return voltage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::writeInputSource()
|
|
|
|
|
{
|
|
|
|
|
streamPrint(spice_stream_, "**************\n");
|
|
|
|
|
streamPrint(spice_stream_, "* Input source\n");
|
|
|
|
|
streamPrint(spice_stream_, "**************\n\n");
|
|
|
|
|
|
2019-11-11 23:53:29 +01:00
|
|
|
Stage input_stage = stageFirst();
|
|
|
|
|
PathRef *input_path = stageDrvrPath(input_stage);
|
2019-01-23 05:41:32 +01:00
|
|
|
if (input_path->isClock(this))
|
|
|
|
|
writeClkWaveform();
|
|
|
|
|
else
|
|
|
|
|
writeInputWaveform();
|
|
|
|
|
streamPrint(spice_stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::writeInputWaveform()
|
|
|
|
|
{
|
2019-11-11 23:53:29 +01:00
|
|
|
Stage input_stage = stageFirst();
|
|
|
|
|
PathRef *input_path = stageDrvrPath(input_stage);
|
|
|
|
|
const RiseFall *rf = input_path->transition(this);
|
|
|
|
|
TimingArc *next_arc = stageGateArc(input_stage + 1);
|
2019-12-09 20:06:49 +01:00
|
|
|
float slew0 = findSlew(input_path, rf, next_arc);
|
2019-01-23 05:41:32 +01:00
|
|
|
// Arbitrary offset.
|
2019-11-11 23:53:29 +01:00
|
|
|
float time0 = slew0;
|
2019-01-23 05:41:32 +01:00
|
|
|
int volt_index = 1;
|
2019-11-11 23:53:29 +01:00
|
|
|
const Pin *drvr_pin = stageDrvrPin(input_stage);
|
|
|
|
|
writeStepVoltSource(drvr_pin, rf, slew0, time0, volt_index);
|
2019-01-23 05:41:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::writeStepVoltSource(const Pin *pin,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *rf,
|
2019-01-23 05:41:32 +01:00
|
|
|
float slew,
|
|
|
|
|
float time,
|
|
|
|
|
int &volt_index)
|
|
|
|
|
{
|
2019-01-04 01:14:15 +01:00
|
|
|
float volt0, volt1;
|
2019-11-11 23:30:19 +01:00
|
|
|
if (rf == RiseFall::rise()) {
|
2019-01-04 01:14:15 +01:00
|
|
|
volt0 = gnd_voltage_;
|
|
|
|
|
volt1 = power_voltage_;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
volt0 = power_voltage_;
|
|
|
|
|
volt1 = gnd_voltage_;
|
|
|
|
|
}
|
2019-01-23 05:41:32 +01:00
|
|
|
streamPrint(spice_stream_, "v%d %s 0 pwl(\n",
|
|
|
|
|
volt_index,
|
|
|
|
|
network_->pathName(pin));
|
|
|
|
|
streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0);
|
2019-11-11 23:30:19 +01:00
|
|
|
writeWaveformEdge(rf, time, slew);
|
2019-01-23 05:41:32 +01:00
|
|
|
streamPrint(spice_stream_, "+%.3e %.3e\n", maxTime(), volt1);
|
|
|
|
|
streamPrint(spice_stream_, "+)\n");
|
|
|
|
|
volt_index++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::writeClkWaveform()
|
|
|
|
|
{
|
2019-11-11 23:53:29 +01:00
|
|
|
Stage input_stage = stageFirst();
|
|
|
|
|
PathRef *input_path = stageDrvrPath(input_stage);
|
|
|
|
|
TimingArc *next_arc = stageGateArc(input_stage + 1);
|
|
|
|
|
ClockEdge *clk_edge = input_path->clkEdge(this);
|
|
|
|
|
Clock *clk = clk_edge->clock();
|
|
|
|
|
float period = clk->period();
|
2019-01-23 05:41:32 +01:00
|
|
|
float time_offset = clkWaveformTImeOffset(clk);
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFall *rf0, *rf1;
|
2019-01-23 05:41:32 +01:00
|
|
|
float volt0;
|
|
|
|
|
if (clk_edge->time() < period) {
|
2019-11-11 23:30:19 +01:00
|
|
|
rf0 = RiseFall::rise();
|
|
|
|
|
rf1 = RiseFall::fall();
|
2019-01-23 05:41:32 +01:00
|
|
|
volt0 = gnd_voltage_;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2019-11-11 23:30:19 +01:00
|
|
|
rf0 = RiseFall::fall();
|
|
|
|
|
rf1 = RiseFall::rise();
|
2019-01-23 05:41:32 +01:00
|
|
|
volt0 = power_voltage_;
|
|
|
|
|
}
|
2019-12-09 20:06:49 +01:00
|
|
|
float slew0 = findSlew(input_path, rf0, next_arc);
|
|
|
|
|
float slew1 = findSlew(input_path, rf1, next_arc);
|
2019-01-23 05:41:32 +01:00
|
|
|
streamPrint(spice_stream_, "v1 %s 0 pwl(\n",
|
|
|
|
|
stageDrvrPinName(input_stage));
|
2019-01-04 01:14:15 +01:00
|
|
|
streamPrint(spice_stream_, "+%.3e %.3e\n", 0.0, volt0);
|
2019-01-23 05:41:32 +01:00
|
|
|
for (int cycle = 0; cycle < clk_cycle_count_; cycle++) {
|
2019-11-11 23:53:29 +01:00
|
|
|
float time0 = time_offset + cycle * period;
|
|
|
|
|
float time1 = time0 + period / 2.0;
|
2019-11-11 23:30:19 +01:00
|
|
|
writeWaveformEdge(rf0, time0, slew0);
|
|
|
|
|
writeWaveformEdge(rf1, time1, slew1);
|
2019-01-23 05:41:32 +01:00
|
|
|
}
|
|
|
|
|
streamPrint(spice_stream_, "+%.3e %.3e\n", maxTime(), volt0);
|
|
|
|
|
streamPrint(spice_stream_, "+)\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
WritePathSpice::clkWaveformTImeOffset(const Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
return clk->period() / 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
WritePathSpice::findSlew(Path *path)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
Vertex *vertex = path->vertex(this);
|
|
|
|
|
DcalcAPIndex dcalc_ap_index = path->dcalcAnalysisPt(this)->index();
|
|
|
|
|
const RiseFall *rf = path->transition(this);
|
|
|
|
|
return findSlew(vertex, rf, nullptr, dcalc_ap_index);
|
2019-01-23 05:41:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
WritePathSpice::findSlew(Path *path,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *rf,
|
2019-01-27 08:03:01 +01:00
|
|
|
TimingArc *next_arc)
|
2019-01-23 05:41:32 +01:00
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
Vertex *vertex = path->vertex(this);
|
|
|
|
|
DcalcAPIndex dcalc_ap_index = path->dcalcAnalysisPt(this)->index();
|
|
|
|
|
return findSlew(vertex, rf, next_arc, dcalc_ap_index);
|
2019-01-23 05:41:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
WritePathSpice::findSlew(Vertex *vertex,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *rf,
|
2019-01-27 08:03:01 +01:00
|
|
|
TimingArc *next_arc,
|
2019-01-23 05:41:32 +01:00
|
|
|
DcalcAPIndex dcalc_ap_index)
|
|
|
|
|
{
|
2019-12-09 20:06:49 +01:00
|
|
|
float slew = delayAsFloat(graph_->slew(vertex, rf, dcalc_ap_index));
|
2019-01-27 08:03:01 +01:00
|
|
|
if (slew == 0.0 && next_arc)
|
|
|
|
|
slew = slewAxisMinValue(next_arc);
|
2019-01-23 05:41:32 +01:00
|
|
|
if (slew == 0.0)
|
|
|
|
|
slew = units_->timeUnit()->scale();
|
|
|
|
|
return slew;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-27 08:03:01 +01:00
|
|
|
// Look up the smallest slew axis value in the timing arc delay table.
|
|
|
|
|
float
|
|
|
|
|
WritePathSpice::slewAxisMinValue(TimingArc *arc)
|
|
|
|
|
{
|
|
|
|
|
GateTableModel *gate_model = dynamic_cast<GateTableModel*>(arc->model());
|
|
|
|
|
if (gate_model) {
|
|
|
|
|
const TableModel *model = gate_model->delayModel();
|
|
|
|
|
TableAxis *axis;
|
|
|
|
|
TableAxisVariable var;
|
|
|
|
|
axis = model->axis1();
|
|
|
|
|
var = axis->variable();
|
2019-03-13 01:25:53 +01:00
|
|
|
if (var == TableAxisVariable::input_transition_time
|
|
|
|
|
|| var == TableAxisVariable::input_net_transition)
|
2019-01-27 08:03:01 +01:00
|
|
|
return axis->axisValue(0);
|
|
|
|
|
|
|
|
|
|
axis = model->axis2();
|
|
|
|
|
var = axis->variable();
|
2019-03-13 01:25:53 +01:00
|
|
|
if (var == TableAxisVariable::input_transition_time
|
|
|
|
|
|| var == TableAxisVariable::input_net_transition)
|
2019-01-27 08:03:01 +01:00
|
|
|
return axis->axisValue(0);
|
|
|
|
|
|
|
|
|
|
axis = model->axis3();
|
|
|
|
|
var = axis->variable();
|
2019-03-13 01:25:53 +01:00
|
|
|
if (var == TableAxisVariable::input_transition_time
|
|
|
|
|
|| var == TableAxisVariable::input_net_transition)
|
2019-01-27 08:03:01 +01:00
|
|
|
return axis->axisValue(0);
|
|
|
|
|
}
|
|
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-23 05:41:32 +01:00
|
|
|
// Write PWL rise/fall edge that crosses threshold at time.
|
|
|
|
|
void
|
2019-11-11 23:30:19 +01:00
|
|
|
WritePathSpice::writeWaveformEdge(const RiseFall *rf,
|
2019-01-23 05:41:32 +01:00
|
|
|
float time,
|
|
|
|
|
float slew)
|
|
|
|
|
{
|
|
|
|
|
float volt0, volt1;
|
2019-11-11 23:30:19 +01:00
|
|
|
if (rf == RiseFall::rise()) {
|
2019-01-23 05:41:32 +01:00
|
|
|
volt0 = gnd_voltage_;
|
|
|
|
|
volt1 = power_voltage_;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
volt0 = power_voltage_;
|
|
|
|
|
volt1 = gnd_voltage_;
|
|
|
|
|
}
|
2019-11-11 23:53:29 +01:00
|
|
|
float threshold = default_library_->inputThreshold(rf);
|
|
|
|
|
float lower = default_library_->slewLowerThreshold(rf);
|
|
|
|
|
float upper = default_library_->slewUpperThreshold(rf);
|
|
|
|
|
float dt = slew / (upper - lower);
|
|
|
|
|
float time0 = time - dt * threshold;
|
|
|
|
|
float time1 = time0 + dt;
|
2019-01-04 01:14:15 +01:00
|
|
|
streamPrint(spice_stream_, "+%.3e %.3e\n", time0, volt0);
|
|
|
|
|
streamPrint(spice_stream_, "+%.3e %.3e\n", time1, volt1);
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-23 05:41:32 +01:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2019-01-04 01:14:15 +01:00
|
|
|
void
|
|
|
|
|
WritePathSpice::writeMeasureStmts()
|
|
|
|
|
{
|
|
|
|
|
streamPrint(spice_stream_, "********************\n");
|
|
|
|
|
streamPrint(spice_stream_, "* Measure statements\n");
|
|
|
|
|
streamPrint(spice_stream_, "********************\n\n");
|
|
|
|
|
|
2019-11-11 23:30:19 +01:00
|
|
|
for (Stage stage = stageFirst(); stage <= stageLast(); stage++) {
|
2019-11-11 23:53:29 +01:00
|
|
|
PathRef *gate_input_path = stageGateInputPath(stage);
|
|
|
|
|
PathRef *drvr_path = stageDrvrPath(stage);
|
|
|
|
|
PathRef *load_path = stageLoadPath(stage);
|
2019-01-20 18:44:24 +01:00
|
|
|
if (gate_input_path) {
|
|
|
|
|
// gate input -> gate output
|
|
|
|
|
writeMeasureSlewStmt(stage, gate_input_path);
|
|
|
|
|
writeMeasureDelayStmt(stage, gate_input_path, drvr_path);
|
|
|
|
|
}
|
|
|
|
|
writeMeasureSlewStmt(stage, drvr_path);
|
|
|
|
|
// gate output | input port -> load
|
|
|
|
|
writeMeasureDelayStmt(stage, drvr_path, load_path);
|
2019-01-04 01:14:15 +01:00
|
|
|
if (stage == stageLast())
|
2019-01-20 18:44:24 +01:00
|
|
|
writeMeasureSlewStmt(stage, load_path);
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
streamPrint(spice_stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::writeMeasureDelayStmt(Stage stage,
|
2019-01-20 18:44:24 +01:00
|
|
|
Path *from_path,
|
|
|
|
|
Path *to_path)
|
2019-01-04 01:14:15 +01:00
|
|
|
{
|
2019-11-11 23:53:29 +01:00
|
|
|
const char *from_pin_name = network_->pathName(from_path->pin(this));
|
|
|
|
|
const RiseFall *from_rf = from_path->transition(this);
|
|
|
|
|
float from_threshold = power_voltage_ * default_library_->inputThreshold(from_rf);
|
2019-01-04 01:14:15 +01:00
|
|
|
|
2019-11-11 23:53:29 +01:00
|
|
|
const char *to_pin_name = network_->pathName(to_path->pin(this));
|
|
|
|
|
const RiseFall *to_rf = to_path->transition(this);
|
|
|
|
|
float to_threshold = power_voltage_ * default_library_->inputThreshold(to_rf);
|
2019-01-04 01:14:15 +01:00
|
|
|
streamPrint(spice_stream_,
|
2019-01-20 18:44:24 +01:00
|
|
|
".measure tran %s_%s_delay_%s\n",
|
2019-01-04 01:14:15 +01:00
|
|
|
stageName(stage).c_str(),
|
2019-01-20 18:44:24 +01:00
|
|
|
from_pin_name,
|
|
|
|
|
to_pin_name);
|
|
|
|
|
streamPrint(spice_stream_,
|
|
|
|
|
"+trig v(%s) val=%.3f %s=last\n",
|
|
|
|
|
from_pin_name,
|
|
|
|
|
from_threshold,
|
2019-11-11 23:30:19 +01:00
|
|
|
spiceTrans(from_rf));
|
2019-01-20 18:44:24 +01:00
|
|
|
streamPrint(spice_stream_,
|
|
|
|
|
"+targ v(%s) val=%.3f %s=last\n",
|
|
|
|
|
to_pin_name,
|
|
|
|
|
to_threshold,
|
2019-11-11 23:30:19 +01:00
|
|
|
spiceTrans(to_rf));
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::writeMeasureSlewStmt(Stage stage,
|
|
|
|
|
Path *path)
|
|
|
|
|
{
|
2019-11-11 23:53:29 +01:00
|
|
|
const char *pin_name = network_->pathName(path->pin(this));
|
|
|
|
|
const RiseFall *rf = path->transition(this);
|
|
|
|
|
const char *spice_rf = spiceTrans(rf);
|
|
|
|
|
float lower = power_voltage_ * default_library_->slewLowerThreshold(rf);
|
|
|
|
|
float upper = power_voltage_ * default_library_->slewUpperThreshold(rf);
|
2019-01-04 01:14:15 +01:00
|
|
|
float threshold1, threshold2;
|
2019-11-11 23:53:29 +01:00
|
|
|
if (rf == RiseFall::rise()) {
|
2019-01-04 01:14:15 +01:00
|
|
|
threshold1 = lower;
|
|
|
|
|
threshold2 = upper;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
threshold1 = upper;
|
|
|
|
|
threshold2 = lower;
|
|
|
|
|
}
|
|
|
|
|
streamPrint(spice_stream_,
|
2019-01-20 18:44:24 +01:00
|
|
|
".measure tran %s_%s_slew\n",
|
2019-01-04 01:14:15 +01:00
|
|
|
stageName(stage).c_str(),
|
2019-01-20 18:44:24 +01:00
|
|
|
pin_name);
|
|
|
|
|
streamPrint(spice_stream_,
|
|
|
|
|
"+trig v(%s) val=%.3f %s=last\n",
|
2019-01-04 01:14:15 +01:00
|
|
|
pin_name,
|
|
|
|
|
threshold1,
|
2019-11-11 23:30:19 +01:00
|
|
|
spice_rf);
|
2019-01-20 18:44:24 +01:00
|
|
|
streamPrint(spice_stream_,
|
|
|
|
|
"+targ v(%s) val=%.3f %s=last\n",
|
2019-01-04 01:14:15 +01:00
|
|
|
pin_name,
|
|
|
|
|
threshold2,
|
2019-11-11 23:30:19 +01:00
|
|
|
spice_rf);
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
2019-11-11 23:30:19 +01:00
|
|
|
WritePathSpice::spiceTrans(const RiseFall *rf)
|
2019-01-04 01:14:15 +01:00
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
if (rf == RiseFall::rise())
|
2019-01-04 01:14:15 +01:00
|
|
|
return "RISE";
|
|
|
|
|
else
|
|
|
|
|
return "FALL";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::writeStageSubckts()
|
|
|
|
|
{
|
|
|
|
|
streamPrint(spice_stream_, "***************\n");
|
|
|
|
|
streamPrint(spice_stream_, "* Stage subckts\n");
|
|
|
|
|
streamPrint(spice_stream_, "***************\n\n");
|
|
|
|
|
|
2019-11-11 23:30:19 +01:00
|
|
|
for (Stage stage = stageFirst(); stage <= stageLast(); stage++) {
|
2019-01-04 01:14:15 +01:00
|
|
|
if (stage == stageFirst())
|
|
|
|
|
writeInputStage(stage);
|
|
|
|
|
else
|
|
|
|
|
writeGateStage(stage);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Input port to first gate input.
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::writeInputStage(Stage stage)
|
|
|
|
|
{
|
|
|
|
|
// Input arc.
|
|
|
|
|
// External driver not handled.
|
2019-11-11 23:53:29 +01:00
|
|
|
const char *drvr_pin_name = stageDrvrPinName(stage);
|
|
|
|
|
const char *load_pin_name = stageLoadPinName(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
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
|
|
|
|
|
WritePathSpice::writeGateStage(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:53:29 +01:00
|
|
|
const Pin *input_pin = stageGateInputPin(stage);
|
|
|
|
|
const char *input_pin_name = stageGateInputPinName(stage);
|
|
|
|
|
const Pin *drvr_pin = stageDrvrPin(stage);
|
|
|
|
|
const char *drvr_pin_name = stageDrvrPinName(stage);
|
|
|
|
|
const Pin *load_pin = stageLoadPin(stage);
|
|
|
|
|
const char *load_pin_name = stageLoadPinName(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
streamPrint(spice_stream_, ".subckt stage%d %s %s %s\n",
|
|
|
|
|
stage,
|
|
|
|
|
input_pin_name,
|
|
|
|
|
drvr_pin_name,
|
|
|
|
|
load_pin_name);
|
2019-02-21 17:00:06 +01:00
|
|
|
// Driver subckt call.
|
2019-11-11 23:53:29 +01:00
|
|
|
Instance *inst = stageInstance(stage);
|
|
|
|
|
LibertyPort *input_port = stageGateInputPort(stage);
|
|
|
|
|
LibertyPort *drvr_port = stageDrvrPort(stage);
|
2019-02-21 17:00:06 +01:00
|
|
|
streamPrint(spice_stream_, "* Gate %s %s -> %s\n",
|
|
|
|
|
network_->pathName(inst),
|
|
|
|
|
input_port->name(),
|
|
|
|
|
drvr_port->name());
|
|
|
|
|
writeSubcktInst(input_pin);
|
|
|
|
|
LibertyPortLogicValues port_values;
|
|
|
|
|
DcalcAPIndex dcalc_ap_index;
|
|
|
|
|
const Clock *clk;
|
|
|
|
|
int volt_index = 1;
|
|
|
|
|
gatePortValues(stage, port_values, clk, dcalc_ap_index);
|
|
|
|
|
writeSubcktInstVoltSrcs(stage, input_pin, volt_index,
|
|
|
|
|
port_values, clk, dcalc_ap_index);
|
|
|
|
|
streamPrint(spice_stream_, "\n");
|
|
|
|
|
|
|
|
|
|
port_values.clear();
|
|
|
|
|
auto pin_iter = network_->connectedPinIterator(drvr_pin);
|
|
|
|
|
while (pin_iter->hasNext()) {
|
2019-11-11 23:53:29 +01:00
|
|
|
Pin *pin = pin_iter->next();
|
2019-02-21 17:00:06 +01:00
|
|
|
if (pin != drvr_pin
|
|
|
|
|
&& pin != load_pin
|
|
|
|
|
&& network_->direction(pin)->isAnyInput()
|
|
|
|
|
&& !network_->isHierarchical(pin)
|
|
|
|
|
&& !network_->isTopLevelPort(pin)) {
|
|
|
|
|
streamPrint(spice_stream_, "* Side load %s\n", network_->pathName(pin));
|
|
|
|
|
writeSubcktInst(pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
writeSubcktInstVoltSrcs(stage, pin, volt_index, port_values, nullptr, 0);
|
2019-02-21 17:00:06 +01:00
|
|
|
streamPrint(spice_stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete pin_iter;
|
|
|
|
|
|
|
|
|
|
writeStageParasitics(stage);
|
|
|
|
|
streamPrint(spice_stream_, ".ends\n\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::writeSubcktInst(const Pin *input_pin)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
const Instance *inst = network_->instance(input_pin);
|
|
|
|
|
const char *inst_name = network_->pathName(inst);
|
|
|
|
|
LibertyCell *cell = network_->libertyCell(inst);
|
|
|
|
|
const char *cell_name = cell->name();
|
|
|
|
|
StringVector *spice_port_names = cell_spice_port_names_[cell_name];
|
2019-01-04 01:14:15 +01:00
|
|
|
streamPrint(spice_stream_, "x%s", inst_name);
|
2019-11-11 23:30:19 +01:00
|
|
|
for (string subckt_port_name : *spice_port_names) {
|
|
|
|
|
const char *subckt_port_cname = subckt_port_name.c_str();
|
|
|
|
|
Pin *pin = network_->findPin(inst, subckt_port_cname);
|
|
|
|
|
LibertyPgPort *pg_port = cell->findPgPort(subckt_port_cname);
|
2019-01-04 01:14:15 +01:00
|
|
|
const char *pin_name;
|
|
|
|
|
if (pin) {
|
|
|
|
|
pin_name = network_->pathName(pin);
|
|
|
|
|
streamPrint(spice_stream_, " %s", pin_name);
|
|
|
|
|
}
|
|
|
|
|
else if (pg_port)
|
2019-03-13 01:25:53 +01:00
|
|
|
streamPrint(spice_stream_, " %s/%s", inst_name, subckt_port_cname);
|
2019-04-14 00:01:14 +02:00
|
|
|
else if (stringEq(subckt_port_cname, power_name_)
|
|
|
|
|
|| stringEq(subckt_port_cname, gnd_name_))
|
|
|
|
|
streamPrint(spice_stream_, " %s/%s", inst_name, subckt_port_cname);
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
streamPrint(spice_stream_, " %s\n", cell_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Power/ground and input voltage sources.
|
|
|
|
|
void
|
2019-02-21 17:00:06 +01:00
|
|
|
WritePathSpice::writeSubcktInstVoltSrcs(Stage stage,
|
|
|
|
|
const Pin *input_pin,
|
|
|
|
|
int &volt_index,
|
|
|
|
|
LibertyPortLogicValues &port_values,
|
|
|
|
|
const Clock *clk,
|
|
|
|
|
DcalcAPIndex dcalc_ap_index)
|
|
|
|
|
|
2019-01-04 01:14:15 +01:00
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
const Instance *inst = network_->instance(input_pin);
|
|
|
|
|
LibertyCell *cell = network_->libertyCell(inst);
|
|
|
|
|
const char *cell_name = cell->name();
|
|
|
|
|
StringVector *spice_port_names = cell_spice_port_names_[cell_name];
|
2019-02-21 17:00:06 +01:00
|
|
|
|
2019-11-11 23:30:19 +01:00
|
|
|
const Pin *drvr_pin = stageDrvrPin(stage);
|
|
|
|
|
const LibertyPort *input_port = network_->libertyPort(input_pin);
|
|
|
|
|
const LibertyPort *drvr_port = network_->libertyPort(drvr_pin);
|
|
|
|
|
const char *input_port_name = input_port->name();
|
|
|
|
|
const char *drvr_port_name = drvr_port->name();
|
|
|
|
|
const char *inst_name = network_->pathName(inst);
|
2019-01-23 05:41:32 +01:00
|
|
|
|
2019-01-04 01:14:15 +01:00
|
|
|
debugPrint1(debug_, "write_spice", 2, "subckt %s\n", cell->name());
|
2019-11-11 23:30:19 +01:00
|
|
|
for (string subckt_port_sname : *spice_port_names) {
|
|
|
|
|
const char *subckt_port_name = subckt_port_sname.c_str();
|
|
|
|
|
LibertyPgPort *pg_port = cell->findPgPort(subckt_port_name);
|
2019-01-04 01:14:15 +01:00
|
|
|
debugPrint2(debug_, "write_spice", 2, " port %s%s\n",
|
|
|
|
|
subckt_port_name,
|
|
|
|
|
pg_port ? " pwr/gnd" : "");
|
2019-04-14 00:01:14 +02:00
|
|
|
if (pg_port)
|
|
|
|
|
writeVoltageSource(inst_name, subckt_port_name,
|
|
|
|
|
pgPortVoltage(pg_port), volt_index);
|
|
|
|
|
else if (stringEq(subckt_port_name, power_name_))
|
|
|
|
|
writeVoltageSource(inst_name, subckt_port_name,
|
|
|
|
|
power_voltage_, volt_index);
|
|
|
|
|
else if (stringEq(subckt_port_name, gnd_name_))
|
|
|
|
|
writeVoltageSource(inst_name, subckt_port_name,
|
|
|
|
|
gnd_voltage_, volt_index);
|
|
|
|
|
else if (!(stringEq(subckt_port_name, input_port_name)
|
2019-01-04 01:14:15 +01:00
|
|
|
|| stringEq(subckt_port_name, drvr_port_name))) {
|
|
|
|
|
// Input voltage to sensitize path from gate input to output.
|
2019-11-11 23:30:19 +01:00
|
|
|
LibertyPort *port = cell->findLibertyPort(subckt_port_name);
|
2019-02-21 17:00:06 +01:00
|
|
|
if (port
|
|
|
|
|
&& port->direction()->isAnyInput()) {
|
2019-01-20 18:44:24 +01:00
|
|
|
const Pin *pin = network_->findPin(inst, port);
|
|
|
|
|
// Look for tie high/low or propagated constant values.
|
|
|
|
|
LogicValue port_value = sim_->logicValue(pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (port_value == LogicValue::unknown) {
|
2019-01-20 18:44:24 +01:00
|
|
|
bool has_value;
|
|
|
|
|
LogicValue value;
|
|
|
|
|
port_values.findKey(port, value, has_value);
|
|
|
|
|
if (has_value)
|
|
|
|
|
port_value = value;
|
|
|
|
|
}
|
2019-01-23 05:41:32 +01:00
|
|
|
switch (port_value) {
|
2019-03-13 01:25:53 +01:00
|
|
|
case LogicValue::zero:
|
|
|
|
|
case LogicValue::unknown:
|
2019-01-23 05:41:32 +01:00
|
|
|
writeVoltageSource(cell, inst_name, subckt_port_name,
|
|
|
|
|
port->relatedGroundPin(),
|
2019-04-14 00:01:14 +02:00
|
|
|
gnd_voltage_,
|
2019-01-23 05:41:32 +01:00
|
|
|
volt_index);
|
|
|
|
|
break;
|
2019-03-13 01:25:53 +01:00
|
|
|
case LogicValue::one:
|
2019-01-23 05:41:32 +01:00
|
|
|
writeVoltageSource(cell, inst_name, subckt_port_name,
|
|
|
|
|
port->relatedPowerPin(),
|
2019-04-14 00:01:14 +02:00
|
|
|
power_voltage_,
|
2019-01-23 05:41:32 +01:00
|
|
|
volt_index);
|
|
|
|
|
break;
|
2019-03-13 01:25:53 +01:00
|
|
|
case LogicValue::rise:
|
2019-11-11 23:30:19 +01:00
|
|
|
writeClkedStepSource(pin, RiseFall::rise(), clk,
|
2019-01-23 05:41:32 +01:00
|
|
|
dcalc_ap_index, volt_index);
|
|
|
|
|
break;
|
2019-03-13 01:25:53 +01:00
|
|
|
case LogicValue::fall:
|
2019-11-11 23:30:19 +01:00
|
|
|
writeClkedStepSource(pin, RiseFall::fall(), clk,
|
2019-01-23 05:41:32 +01:00
|
|
|
dcalc_ap_index, volt_index);
|
|
|
|
|
break;
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-27 08:03:01 +01:00
|
|
|
// PWL voltage source that rises half way into the first clock cycle.
|
2019-01-23 05:41:32 +01:00
|
|
|
void
|
|
|
|
|
WritePathSpice::writeClkedStepSource(const Pin *pin,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *rf,
|
2019-01-23 05:41:32 +01:00
|
|
|
const Clock *clk,
|
|
|
|
|
DcalcAPIndex dcalc_ap_index,
|
|
|
|
|
int &volt_index)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
Vertex *vertex = graph_->pinLoadVertex(pin);
|
2019-12-09 20:06:49 +01:00
|
|
|
float slew = findSlew(vertex, rf, nullptr, dcalc_ap_index);
|
|
|
|
|
float time = clkWaveformTImeOffset(clk) + clk->period() / 2.0;
|
2019-11-11 23:30:19 +01:00
|
|
|
writeStepVoltSource(pin, rf, slew, time, volt_index);
|
2019-01-23 05:41:32 +01:00
|
|
|
}
|
|
|
|
|
|
2019-04-14 00:01:14 +02:00
|
|
|
void
|
|
|
|
|
WritePathSpice::writeVoltageSource(const char *inst_name,
|
|
|
|
|
const char *port_name,
|
|
|
|
|
float voltage,
|
|
|
|
|
int &volt_index)
|
|
|
|
|
{
|
|
|
|
|
streamPrint(spice_stream_, "v%d %s/%s 0 %.3f\n",
|
|
|
|
|
volt_index,
|
|
|
|
|
inst_name, port_name,
|
|
|
|
|
voltage);
|
|
|
|
|
volt_index++;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-23 05:41:32 +01:00
|
|
|
void
|
|
|
|
|
WritePathSpice::writeVoltageSource(LibertyCell *cell,
|
|
|
|
|
const char *inst_name,
|
|
|
|
|
const char *subckt_port_name,
|
|
|
|
|
const char *pg_port_name,
|
2019-04-14 00:01:14 +02:00
|
|
|
float voltage,
|
2019-01-23 05:41:32 +01:00
|
|
|
int &volt_index)
|
|
|
|
|
{
|
2019-04-14 00:01:14 +02:00
|
|
|
if (pg_port_name) {
|
2019-11-11 23:30:19 +01:00
|
|
|
LibertyPgPort *pg_port = cell->findPgPort(pg_port_name);
|
2019-04-14 00:01:14 +02:00
|
|
|
if (pg_port)
|
|
|
|
|
voltage = pgPortVoltage(pg_port);
|
2019-01-23 05:41:32 +01:00
|
|
|
else
|
2019-04-14 00:01:14 +02:00
|
|
|
report_->error("%s pg_port %s not found,\n",
|
|
|
|
|
cell->name(),
|
|
|
|
|
pg_port_name);
|
|
|
|
|
|
2019-01-23 05:41:32 +01:00
|
|
|
}
|
2019-04-14 00:01:14 +02:00
|
|
|
writeVoltageSource(inst_name, subckt_port_name, voltage, volt_index);
|
2019-01-23 05:41:32 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-21 17:00:06 +01:00
|
|
|
void
|
|
|
|
|
WritePathSpice::gatePortValues(Stage stage,
|
|
|
|
|
// Return values.
|
|
|
|
|
LibertyPortLogicValues &port_values,
|
|
|
|
|
const Clock *&clk,
|
|
|
|
|
DcalcAPIndex &dcalc_ap_index)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
clk = nullptr;
|
2019-02-21 17:00:06 +01:00
|
|
|
dcalc_ap_index = 0;
|
|
|
|
|
|
2019-11-11 23:30:19 +01:00
|
|
|
Edge *gate_edge = stageGateEdge(stage);
|
|
|
|
|
LibertyPort *drvr_port = stageDrvrPort(stage);
|
2019-02-21 17:00:06 +01:00
|
|
|
if (gate_edge->role()->genericRole() == TimingRole::regClkToQ())
|
|
|
|
|
regPortValues(stage, port_values, clk, dcalc_ap_index);
|
|
|
|
|
else if (drvr_port->function()) {
|
2019-11-11 23:30:19 +01:00
|
|
|
Pin *input_pin = stageGateInputPin(stage);
|
|
|
|
|
LibertyPort *input_port = network_->libertyPort(input_pin);
|
|
|
|
|
Instance *inst = network_->instance(input_pin);
|
2019-02-21 17:00:06 +01:00
|
|
|
gatePortValues(inst, drvr_port->function(), input_port, port_values);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::regPortValues(Stage stage,
|
|
|
|
|
// Return values.
|
|
|
|
|
LibertyPortLogicValues &port_values,
|
|
|
|
|
const Clock *&clk,
|
|
|
|
|
DcalcAPIndex &dcalc_ap_index)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
LibertyPort *drvr_port = stageDrvrPort(stage);
|
|
|
|
|
FuncExpr *drvr_expr = drvr_port->function();
|
2019-02-21 17:00:06 +01:00
|
|
|
if (drvr_expr) {
|
2019-11-11 23:30:19 +01:00
|
|
|
LibertyPort *q_port = drvr_expr->port();
|
2019-02-21 17:00:06 +01:00
|
|
|
if (q_port) {
|
|
|
|
|
// Drvr (register/latch output) function should be a reference
|
|
|
|
|
// to an internal port like IQ or IQN.
|
2019-11-11 23:30:19 +01:00
|
|
|
LibertyCell *cell = stageLibertyCell(stage);
|
|
|
|
|
Sequential *seq = cell->outputPortSequential(q_port);
|
2019-02-21 17:00:06 +01:00
|
|
|
if (seq) {
|
2019-11-11 23:30:19 +01:00
|
|
|
PathRef *drvr_path = stageDrvrPath(stage);
|
|
|
|
|
const RiseFall *drvr_rf = drvr_path->transition(this);
|
|
|
|
|
seqPortValues(seq, drvr_rf, port_values);
|
2019-02-21 17:00:06 +01:00
|
|
|
clk = drvr_path->clock(this);
|
|
|
|
|
dcalc_ap_index = drvr_path->dcalcAnalysisPt(this)->index();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
report_->error("no register/latch found for path from %s to %s,\n",
|
|
|
|
|
stageGateInputPort(stage)->name(),
|
|
|
|
|
stageDrvrPort(stage)->name());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-23 05:41:32 +01:00
|
|
|
// Find the logic values for expression inputs to enable paths input_port.
|
2019-01-20 18:44:24 +01:00
|
|
|
void
|
2019-02-21 17:00:06 +01:00
|
|
|
WritePathSpice::gatePortValues(const Instance *inst,
|
|
|
|
|
FuncExpr *expr,
|
|
|
|
|
LibertyPort *input_port,
|
|
|
|
|
// Return values.
|
|
|
|
|
LibertyPortLogicValues &port_values)
|
2019-01-20 18:44:24 +01:00
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
FuncExpr *left = expr->left();
|
|
|
|
|
FuncExpr *right = expr->right();
|
2019-01-20 18:44:24 +01:00
|
|
|
switch (expr->op()) {
|
|
|
|
|
case FuncExpr::op_port:
|
|
|
|
|
break;
|
|
|
|
|
case FuncExpr::op_not:
|
2019-02-21 17:00:06 +01:00
|
|
|
gatePortValues(inst, left, input_port, port_values);
|
2019-01-20 18:44:24 +01:00
|
|
|
break;
|
|
|
|
|
case FuncExpr::op_or:
|
2019-01-23 05:41:32 +01:00
|
|
|
if (left->hasPort(input_port)
|
2019-01-20 18:44:24 +01:00
|
|
|
&& right->op() == FuncExpr::op_port)
|
2019-03-13 01:25:53 +01:00
|
|
|
port_values[right->port()] = LogicValue::zero;
|
2019-01-23 05:41:32 +01:00
|
|
|
else if (left->hasPort(input_port)
|
2019-01-20 18:44:24 +01:00
|
|
|
&& right->op() == FuncExpr::op_not
|
|
|
|
|
&& right->left()->op() == FuncExpr::op_port)
|
2019-01-23 05:41:32 +01:00
|
|
|
// input_port + !right_port
|
2019-03-13 01:25:53 +01:00
|
|
|
port_values[right->left()->port()] = LogicValue::one;
|
2019-01-23 05:41:32 +01:00
|
|
|
else if (right->hasPort(input_port)
|
2019-01-20 18:44:24 +01:00
|
|
|
&& left->op() == FuncExpr::op_port)
|
2019-03-13 01:25:53 +01:00
|
|
|
port_values[left->port()] = LogicValue::zero;
|
2019-01-23 05:41:32 +01:00
|
|
|
else if (right->hasPort(input_port)
|
2019-01-20 18:44:24 +01:00
|
|
|
&& left->op() == FuncExpr::op_not
|
|
|
|
|
&& left->left()->op() == FuncExpr::op_port)
|
2019-01-23 05:41:32 +01:00
|
|
|
// input_port + !left_port
|
2019-03-13 01:25:53 +01:00
|
|
|
port_values[left->left()->port()] = LogicValue::one;
|
2019-01-20 18:44:24 +01:00
|
|
|
else {
|
2019-02-21 17:00:06 +01:00
|
|
|
gatePortValues(inst, left, input_port, port_values);
|
|
|
|
|
gatePortValues(inst, right, input_port, port_values);
|
2019-01-20 18:44:24 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case FuncExpr::op_and:
|
2019-01-23 05:41:32 +01:00
|
|
|
if (left->hasPort(input_port)
|
2019-01-20 18:44:24 +01:00
|
|
|
&& right->op() == FuncExpr::op_port)
|
2019-03-13 01:25:53 +01:00
|
|
|
port_values[right->port()] = LogicValue::one;
|
2019-01-23 05:41:32 +01:00
|
|
|
else if (left->hasPort(input_port)
|
2019-01-20 18:44:24 +01:00
|
|
|
&& right->op() == FuncExpr::op_not
|
|
|
|
|
&& right->left()->op() == FuncExpr::op_port)
|
2019-01-23 05:41:32 +01:00
|
|
|
// input_port * !right_port
|
2019-03-13 01:25:53 +01:00
|
|
|
port_values[right->left()->port()] = LogicValue::zero;
|
2019-01-23 05:41:32 +01:00
|
|
|
else if (right->hasPort(input_port)
|
2019-01-20 18:44:24 +01:00
|
|
|
&& left->op() == FuncExpr::op_port)
|
2019-03-13 01:25:53 +01:00
|
|
|
port_values[left->port()] = LogicValue::one;
|
2019-01-23 05:41:32 +01:00
|
|
|
else if (right->hasPort(input_port)
|
2019-01-20 18:44:24 +01:00
|
|
|
&& left->op() == FuncExpr::op_not
|
|
|
|
|
&& left->left()->op() == FuncExpr::op_port)
|
2019-01-23 05:41:32 +01:00
|
|
|
// input_port * !left_port
|
2019-03-13 01:25:53 +01:00
|
|
|
port_values[left->left()->port()] = LogicValue::zero;
|
2019-01-20 18:44:24 +01:00
|
|
|
else {
|
2019-02-21 17:00:06 +01:00
|
|
|
gatePortValues(inst, left, input_port, port_values);
|
|
|
|
|
gatePortValues(inst, right, input_port, port_values);
|
2019-01-20 18:44:24 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case FuncExpr::op_xor:
|
|
|
|
|
// Need to know timing arc sense to get this right.
|
2019-01-23 05:41:32 +01:00
|
|
|
if (left->port() == input_port
|
2019-01-20 18:44:24 +01:00
|
|
|
&& right->op() == FuncExpr::op_port)
|
2019-03-13 01:25:53 +01:00
|
|
|
port_values[right->port()] = LogicValue::zero;
|
2019-01-23 05:41:32 +01:00
|
|
|
else if (right->port() == input_port
|
2019-01-20 18:44:24 +01:00
|
|
|
&& left->op() == FuncExpr::op_port)
|
2019-03-13 01:25:53 +01:00
|
|
|
port_values[left->port()] = LogicValue::zero;
|
2019-01-20 18:44:24 +01:00
|
|
|
else {
|
2019-02-21 17:00:06 +01:00
|
|
|
gatePortValues(inst, left, input_port, port_values);
|
|
|
|
|
gatePortValues(inst, right, input_port, port_values);
|
2019-01-20 18:44:24 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case FuncExpr::op_one:
|
|
|
|
|
case FuncExpr::op_zero:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-23 05:41:32 +01:00
|
|
|
void
|
2019-02-21 17:00:06 +01:00
|
|
|
WritePathSpice::seqPortValues(Sequential *seq,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *rf,
|
2019-02-21 17:00:06 +01:00
|
|
|
// Return values.
|
|
|
|
|
LibertyPortLogicValues &port_values)
|
2019-01-23 05:41:32 +01:00
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
FuncExpr *data = seq->data();
|
|
|
|
|
LibertyPort *port = onePort(data);
|
2019-01-23 05:41:32 +01:00
|
|
|
if (port) {
|
2019-11-11 23:30:19 +01:00
|
|
|
TimingSense sense = data->portTimingSense(port);
|
2019-01-23 05:41:32 +01:00
|
|
|
switch (sense) {
|
2019-03-13 01:25:53 +01:00
|
|
|
case TimingSense::positive_unate:
|
2019-11-11 23:30:19 +01:00
|
|
|
if (rf == RiseFall::rise())
|
2019-03-13 01:25:53 +01:00
|
|
|
port_values[port] = LogicValue::rise;
|
2019-01-23 05:41:32 +01:00
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
port_values[port] = LogicValue::fall;
|
2019-01-23 05:41:32 +01:00
|
|
|
break;
|
2019-03-13 01:25:53 +01:00
|
|
|
case TimingSense::negative_unate:
|
2019-11-11 23:30:19 +01:00
|
|
|
if (rf == RiseFall::rise())
|
2019-03-13 01:25:53 +01:00
|
|
|
port_values[port] = LogicValue::fall;
|
2019-01-23 05:41:32 +01:00
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
port_values[port] = LogicValue::rise;
|
2019-01-23 05:41:32 +01:00
|
|
|
break;
|
2019-03-13 01:25:53 +01:00
|
|
|
case TimingSense::non_unate:
|
|
|
|
|
case TimingSense::none:
|
|
|
|
|
case TimingSense::unknown:
|
2019-01-23 05:41:32 +01:00
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pick a port, any port...
|
|
|
|
|
LibertyPort *
|
|
|
|
|
WritePathSpice::onePort(FuncExpr *expr)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
FuncExpr *left = expr->left();
|
|
|
|
|
FuncExpr *right = expr->right();
|
2019-01-23 05:41:32 +01:00
|
|
|
LibertyPort *port;
|
|
|
|
|
switch (expr->op()) {
|
|
|
|
|
case FuncExpr::op_port:
|
|
|
|
|
return expr->port();
|
|
|
|
|
case FuncExpr::op_not:
|
|
|
|
|
return onePort(left);
|
|
|
|
|
case FuncExpr::op_or:
|
|
|
|
|
case FuncExpr::op_and:
|
|
|
|
|
case FuncExpr::op_xor:
|
|
|
|
|
port = onePort(left);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (port == nullptr)
|
2019-01-23 05:41:32 +01:00
|
|
|
port = onePort(right);
|
|
|
|
|
return port;
|
|
|
|
|
case FuncExpr::op_one:
|
|
|
|
|
case FuncExpr::op_zero:
|
2019-03-04 02:50:56 +01:00
|
|
|
default:
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2019-01-23 05:41:32 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-13 01:25:53 +01:00
|
|
|
// Sort predicate for ParasiticDevices.
|
|
|
|
|
class ParasiticDeviceLess
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ParasiticDeviceLess(Parasitics *parasitics) :
|
|
|
|
|
parasitics_(parasitics)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
bool operator()(const ParasiticDevice *device1,
|
|
|
|
|
const ParasiticDevice *device2) const
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
ParasiticNode *node1 = parasitics_->node1(device1);
|
|
|
|
|
ParasiticNode *node2 = parasitics_->node1(device2);
|
|
|
|
|
const char *name1 = parasitics_->name(node1);
|
|
|
|
|
const char *name2 = parasitics_->name(node2);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (stringEq(name1, name2)) {
|
2019-11-11 23:30:19 +01:00
|
|
|
ParasiticNode *node12 = parasitics_->node2(device1);
|
|
|
|
|
ParasiticNode *node22 = parasitics_->node2(device2);
|
|
|
|
|
const char *name12 = parasitics_->name(node12);
|
|
|
|
|
const char *name22 = parasitics_->name(node22);
|
2019-03-13 01:25:53 +01:00
|
|
|
return stringLess(name12, name22);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return stringLess(name1, name2);
|
|
|
|
|
}
|
|
|
|
|
private:
|
|
|
|
|
Parasitics *parasitics_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Sort predicate for ParasiticDevices.
|
|
|
|
|
class ParasiticNodeLess
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ParasiticNodeLess(Parasitics *parasitics) :
|
|
|
|
|
parasitics_(parasitics)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
bool operator()(const ParasiticNode *node1,
|
|
|
|
|
const ParasiticNode *node2) const
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
const char *name1 = parasitics_->name(node1);
|
|
|
|
|
const char *name2 = parasitics_->name(node2);
|
2019-03-13 01:25:53 +01:00
|
|
|
return stringLess(name1, name2);
|
|
|
|
|
}
|
|
|
|
|
private:
|
|
|
|
|
Parasitics *parasitics_;
|
|
|
|
|
};
|
|
|
|
|
|
2019-01-04 01:14:15 +01:00
|
|
|
void
|
|
|
|
|
WritePathSpice::writeStageParasitics(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
PathRef *drvr_path = stageDrvrPath(stage);
|
|
|
|
|
Pin *drvr_pin = stageDrvrPin(stage);
|
|
|
|
|
DcalcAnalysisPt *dcalc_ap = drvr_path->dcalcAnalysisPt(this);
|
|
|
|
|
ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt();
|
|
|
|
|
Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap);
|
2019-02-21 17:00:06 +01:00
|
|
|
Set<const Pin*> reachable_pins;
|
|
|
|
|
int res_index = 1;
|
2019-01-04 01:14:15 +01:00
|
|
|
int cap_index = 1;
|
|
|
|
|
if (parasitic) {
|
2019-11-11 23:30:19 +01:00
|
|
|
Net *net = network_->net(drvr_pin);
|
|
|
|
|
const char *net_name = net ? network_->pathName(net) : network_->pathName(drvr_pin);
|
2019-01-04 01:14:15 +01:00
|
|
|
initNodeMap(net_name);
|
|
|
|
|
streamPrint(spice_stream_, "* Net %s\n", net_name);
|
2019-03-13 01:25:53 +01:00
|
|
|
|
|
|
|
|
// Sort devices for consistent regression results.
|
|
|
|
|
Vector<ParasiticDevice*> devices;
|
|
|
|
|
ParasiticDeviceIterator *device_iter1 = parasitics_->deviceIterator(parasitic);
|
|
|
|
|
while (device_iter1->hasNext()) {
|
2019-11-11 23:30:19 +01:00
|
|
|
ParasiticDevice *device = device_iter1->next();
|
2019-03-13 01:25:53 +01:00
|
|
|
devices.push_back(device);
|
|
|
|
|
}
|
|
|
|
|
delete device_iter1;
|
|
|
|
|
|
|
|
|
|
sort(devices, ParasiticDeviceLess(parasitics_));
|
|
|
|
|
|
2019-11-11 23:53:29 +01:00
|
|
|
for (ParasiticDevice *device : devices) {
|
2019-11-11 23:30:19 +01:00
|
|
|
float resistance = parasitics_->value(device, parasitic_ap);
|
2019-01-04 01:14:15 +01:00
|
|
|
if (parasitics_->isResistor(device)) {
|
2019-11-11 23:30:19 +01:00
|
|
|
ParasiticNode *node1 = parasitics_->node1(device);
|
|
|
|
|
ParasiticNode *node2 = parasitics_->node2(device);
|
2019-01-04 01:14:15 +01:00
|
|
|
streamPrint(spice_stream_, "R%d %s %s %.3e\n",
|
2019-02-21 17:00:06 +01:00
|
|
|
res_index,
|
2019-01-04 01:14:15 +01:00
|
|
|
nodeName(node1),
|
|
|
|
|
nodeName(node2),
|
|
|
|
|
resistance);
|
2019-02-21 17:00:06 +01:00
|
|
|
res_index++;
|
|
|
|
|
|
2019-11-11 23:30:19 +01:00
|
|
|
const Pin *pin1 = parasitics_->connectionPin(node1);
|
2019-02-21 17:00:06 +01:00
|
|
|
reachable_pins.insert(pin1);
|
2019-11-11 23:30:19 +01:00
|
|
|
const Pin *pin2 = parasitics_->connectionPin(node2);
|
2019-02-21 17:00:06 +01:00
|
|
|
reachable_pins.insert(pin2);
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
else if (parasitics_->isCouplingCap(device)) {
|
2019-01-06 01:09:27 +01:00
|
|
|
// Ground coupling caps for now.
|
|
|
|
|
ParasiticNode *node1 = parasitics_->node1(device);
|
2019-11-11 23:30:19 +01:00
|
|
|
float cap = parasitics_->value(device, parasitic_ap);
|
2019-01-06 01:09:27 +01:00
|
|
|
streamPrint(spice_stream_, "C%d %s 0 %.3e\n",
|
|
|
|
|
cap_index,
|
|
|
|
|
nodeName(node1),
|
|
|
|
|
cap);
|
|
|
|
|
cap_index++;
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
2019-02-21 17:00:06 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
streamPrint(spice_stream_, "* No parasitics found for this net.\n");
|
|
|
|
|
|
|
|
|
|
// Add resistors from drvr to load for missing parasitic connections.
|
|
|
|
|
auto pin_iter = network_->connectedPinIterator(drvr_pin);
|
|
|
|
|
while (pin_iter->hasNext()) {
|
2019-11-11 23:53:29 +01:00
|
|
|
const Pin *pin = pin_iter->next();
|
2019-02-21 17:00:06 +01:00
|
|
|
if (pin != drvr_pin
|
|
|
|
|
&& network_->isLoad(pin)
|
|
|
|
|
&& !network_->isHierarchical(pin)
|
|
|
|
|
&& !reachable_pins.hasKey(pin)) {
|
|
|
|
|
streamPrint(spice_stream_, "R%d %s %s %.3e\n",
|
|
|
|
|
res_index,
|
|
|
|
|
network_->pathName(drvr_pin),
|
|
|
|
|
network_->pathName(pin),
|
|
|
|
|
short_ckt_resistance_);
|
|
|
|
|
res_index++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete pin_iter;
|
|
|
|
|
|
|
|
|
|
if (parasitic) {
|
2019-03-13 01:25:53 +01:00
|
|
|
// Sort node capacitors for consistent regression results.
|
|
|
|
|
Vector<ParasiticNode*> nodes;
|
2019-01-26 03:06:24 +01:00
|
|
|
ParasiticNodeIterator *node_iter = parasitics_->nodeIterator(parasitic);
|
|
|
|
|
while (node_iter->hasNext()) {
|
2019-11-11 23:30:19 +01:00
|
|
|
ParasiticNode *node = node_iter->next();
|
2019-03-13 01:25:53 +01:00
|
|
|
nodes.push_back(node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sort(nodes, ParasiticNodeLess(parasitics_));
|
|
|
|
|
|
2019-11-11 23:30:19 +01:00
|
|
|
for (ParasiticNode *node : nodes) {
|
|
|
|
|
float cap = parasitics_->nodeGndCap(node, parasitic_ap);
|
2019-01-26 03:06:24 +01:00
|
|
|
// Spice has a cow over zero value caps.
|
2019-01-20 18:44:24 +01:00
|
|
|
if (cap > 0.0) {
|
|
|
|
|
streamPrint(spice_stream_, "C%d %s 0 %.3e\n",
|
|
|
|
|
cap_index,
|
|
|
|
|
nodeName(node),
|
|
|
|
|
cap);
|
|
|
|
|
cap_index++;
|
|
|
|
|
}
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
2019-01-26 03:06:24 +01:00
|
|
|
delete node_iter;
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::initNodeMap(const char *net_name)
|
|
|
|
|
{
|
|
|
|
|
stringDelete(net_name_);
|
|
|
|
|
node_map_.clear();
|
|
|
|
|
next_node_index_ = 1;
|
|
|
|
|
net_name_ = stringCopy(net_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
WritePathSpice::nodeName(ParasiticNode *node)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
const Pin *pin = parasitics_->connectionPin(node);
|
2019-01-04 01:14:15 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2019-01-17 00:37:31 +01:00
|
|
|
return stringPrintTmp("%s/%d", net_name_, node_index);
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
// Copy the subckt definition from lib_subckt_filename for
|
|
|
|
|
// each cell in path to path_subckt_filename.
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::writeSubckts()
|
|
|
|
|
{
|
|
|
|
|
StringSet path_cell_names;
|
|
|
|
|
findPathCellnames(path_cell_names);
|
|
|
|
|
|
|
|
|
|
ifstream lib_subckts_stream(lib_subckt_filename_);
|
|
|
|
|
if (lib_subckts_stream.is_open()) {
|
|
|
|
|
ofstream subckts_stream(subckt_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")) {
|
2019-11-11 23:30:19 +01:00
|
|
|
const char *cell_name = tokens[1].c_str();
|
2019-01-04 01:14:15 +01:00
|
|
|
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";
|
2019-04-14 00:01:14 +02:00
|
|
|
if (stringBeginEqual(line.c_str(), ".ends")) {
|
2019-01-04 01:14:15 +01:00
|
|
|
subckts_stream << "\n";
|
|
|
|
|
found_ends = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!found_ends)
|
|
|
|
|
throw SubcktEndsMissing(cell_name, lib_subckt_filename_);
|
2019-04-11 05:36:48 +02:00
|
|
|
path_cell_names.erase(cell_name);
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
recordSpicePortNames(cell_name, tokens);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
subckts_stream.close();
|
|
|
|
|
lib_subckts_stream.close();
|
|
|
|
|
|
|
|
|
|
if (!path_cell_names.empty()) {
|
|
|
|
|
report_->error("The following subkcts are missing from %s\n",
|
|
|
|
|
lib_subckt_filename_);
|
2019-11-11 23:30:19 +01:00
|
|
|
for (const char *cell_name : path_cell_names)
|
2019-01-04 01:14:15 +01:00
|
|
|
report_->printError(" %s\n", cell_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
lib_subckts_stream.close();
|
|
|
|
|
throw FileNotWritable(subckt_filename_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
throw FileNotReadable(lib_subckt_filename_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::findPathCellnames(// Return values.
|
|
|
|
|
StringSet &path_cell_names)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
for (Stage stage = stageFirst(); stage <= stageLast(); stage++) {
|
|
|
|
|
TimingArc *arc = stageGateArc(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
if (arc) {
|
2019-11-11 23:30:19 +01:00
|
|
|
LibertyCell *cell = arc->set()->libertyCell();
|
2019-01-04 01:14:15 +01:00
|
|
|
if (cell) {
|
|
|
|
|
debugPrint1(debug_, "write_spice", 2, "cell %s\n", cell->name());
|
|
|
|
|
path_cell_names.insert(cell->name());
|
|
|
|
|
}
|
2019-02-21 17:00:06 +01:00
|
|
|
// Include side receivers.
|
2019-11-11 23:30:19 +01:00
|
|
|
Pin *drvr_pin = stageDrvrPin(stage);
|
2019-02-21 17:00:06 +01:00
|
|
|
auto pin_iter = network_->connectedPinIterator(drvr_pin);
|
|
|
|
|
while (pin_iter->hasNext()) {
|
2019-11-11 23:53:29 +01:00
|
|
|
const Pin *pin = pin_iter->next();
|
2019-11-11 23:30:19 +01:00
|
|
|
LibertyPort *port = network_->libertyPort(pin);
|
2019-02-21 17:00:06 +01:00
|
|
|
if (port) {
|
2019-11-11 23:30:19 +01:00
|
|
|
LibertyCell *cell = port->libertyCell();
|
2019-02-21 17:00:06 +01:00
|
|
|
path_cell_names.insert(cell->name());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete pin_iter;
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WritePathSpice::recordSpicePortNames(const char *cell_name,
|
|
|
|
|
StringVector &tokens)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
LibertyCell *cell = network_->findLibertyCell(cell_name);
|
2019-01-08 07:15:17 +01:00
|
|
|
if (cell) {
|
2019-11-11 23:30:19 +01:00
|
|
|
StringVector *spice_port_names = new StringVector;
|
2019-03-04 02:50:56 +01:00
|
|
|
for (size_t i = 2; i < tokens.size(); i++) {
|
2019-11-11 23:30:19 +01:00
|
|
|
const char *port_name = tokens[i].c_str();
|
|
|
|
|
LibertyPort *port = cell->findLibertyPort(port_name);
|
|
|
|
|
LibertyPgPort *pg_port = cell->findPgPort(port_name);
|
2019-04-14 00:01:14 +02:00
|
|
|
if (port == nullptr
|
|
|
|
|
&& pg_port == nullptr
|
|
|
|
|
&& !stringEqual(port_name, power_name_)
|
|
|
|
|
&& !stringEqual(port_name, gnd_name_))
|
|
|
|
|
report_->error("subckt %s port %s has no corresponding liberty port, pg_port and is not power or ground.\n",
|
2019-01-08 07:15:17 +01:00
|
|
|
cell_name, port_name);
|
|
|
|
|
spice_port_names->push_back(port_name);
|
|
|
|
|
}
|
|
|
|
|
cell_spice_port_names_[cell_name] = spice_port_names;
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
Stage
|
|
|
|
|
WritePathSpice::stageFirst()
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Stage
|
|
|
|
|
WritePathSpice::stageLast()
|
|
|
|
|
{
|
|
|
|
|
return (path_expanded_.size() + 1) / 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string
|
|
|
|
|
WritePathSpice::stageName(Stage stage)
|
|
|
|
|
{
|
|
|
|
|
string name;
|
|
|
|
|
stringPrint(name, "stage%d", stage);
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
WritePathSpice::stageGateInputPathIndex(Stage stage)
|
|
|
|
|
{
|
|
|
|
|
return stage * 2 - 3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
WritePathSpice::stageDrvrPathIndex(Stage stage)
|
|
|
|
|
{
|
|
|
|
|
return stage * 2 - 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
WritePathSpice::stageLoadPathIndex(Stage stage)
|
|
|
|
|
{
|
|
|
|
|
return stage * 2 - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathRef *
|
|
|
|
|
WritePathSpice::stageGateInputPath(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
int path_index = stageGateInputPathIndex(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
return path_expanded_.path(path_index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathRef *
|
|
|
|
|
WritePathSpice::stageDrvrPath(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
int path_index = stageDrvrPathIndex(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
return path_expanded_.path(path_index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathRef *
|
|
|
|
|
WritePathSpice::stageLoadPath(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
int path_index = stageLoadPathIndex(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
return path_expanded_.path(path_index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TimingArc *
|
|
|
|
|
WritePathSpice::stageGateArc(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
int path_index = stageDrvrPathIndex(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
if (path_index >= 0)
|
|
|
|
|
return path_expanded_.prevArc(path_index);
|
|
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2019-01-04 01:14:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TimingArc *
|
|
|
|
|
WritePathSpice::stageWireArc(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
int path_index = stageLoadPathIndex(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
return path_expanded_.prevArc(path_index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Edge *
|
|
|
|
|
WritePathSpice::stageGateEdge(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
PathRef *path = stageDrvrPath(stage);
|
|
|
|
|
TimingArc *arc = stageGateArc(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
return path->prevEdge(arc, this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Edge *
|
|
|
|
|
WritePathSpice::stageWireEdge(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
PathRef *path = stageLoadPath(stage);
|
|
|
|
|
TimingArc *arc = stageWireArc(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
return path->prevEdge(arc, this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Pin *
|
2019-01-23 05:41:32 +01:00
|
|
|
WritePathSpice::stageGateInputPin(Stage stage)
|
2019-01-04 01:14:15 +01:00
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
PathRef *path = stageGateInputPath(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
return path->pin(this);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-21 17:00:06 +01:00
|
|
|
LibertyPort *
|
|
|
|
|
WritePathSpice::stageGateInputPort(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
Pin *pin = stageGateInputPin(stage);
|
2019-02-21 17:00:06 +01:00
|
|
|
return network_->libertyPort(pin);
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-04 01:14:15 +01:00
|
|
|
Pin *
|
|
|
|
|
WritePathSpice::stageDrvrPin(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
PathRef *path = stageDrvrPath(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
return path->pin(this);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-21 17:00:06 +01:00
|
|
|
LibertyPort *
|
|
|
|
|
WritePathSpice::stageDrvrPort(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
Pin *pin = stageDrvrPin(stage);
|
2019-02-21 17:00:06 +01:00
|
|
|
return network_->libertyPort(pin);
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-04 01:14:15 +01:00
|
|
|
Pin *
|
|
|
|
|
WritePathSpice::stageLoadPin(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
PathRef *path = stageLoadPath(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
return path->pin(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
WritePathSpice::stageGateInputPinName(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
Pin *pin = stageGateInputPin(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
return network_->pathName(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
WritePathSpice::stageDrvrPinName(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
Pin *pin = stageDrvrPin(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
return network_->pathName(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
WritePathSpice::stageLoadPinName(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
Pin *pin = stageLoadPin(stage);
|
2019-01-04 01:14:15 +01:00
|
|
|
return network_->pathName(pin);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-21 17:00:06 +01:00
|
|
|
Instance *
|
|
|
|
|
WritePathSpice::stageInstance(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
Pin *pin = stageDrvrPin(stage);
|
2019-02-21 17:00:06 +01:00
|
|
|
return network_->instance(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LibertyCell *
|
|
|
|
|
WritePathSpice::stageLibertyCell(Stage stage)
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
Pin *pin = stageDrvrPin(stage);
|
2019-02-21 17:00:06 +01:00
|
|
|
return network_->libertyPort(pin)->libertyCell();
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-04 01:14:15 +01:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
// 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;
|
2019-03-04 02:50:56 +01:00
|
|
|
if (vasprintf(&result, fmt, args) == -1)
|
|
|
|
|
internalError("out of memory");
|
2019-01-04 01:14:15 +01:00
|
|
|
stream << result;
|
|
|
|
|
free(result);
|
|
|
|
|
va_end(args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|