write_timing_model
Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
parent
7e0ba747e7
commit
f2c6b49d07
|
|
@ -164,6 +164,7 @@ set(STA_SOURCE
|
|||
search/Genclks.cc
|
||||
search/Latches.cc
|
||||
search/Levelize.cc
|
||||
search/MakeTimingModel.cc
|
||||
search/Path.cc
|
||||
search/PathAnalysisPt.cc
|
||||
search/PathEnd.cc
|
||||
|
|
@ -189,7 +190,7 @@ set(STA_SOURCE
|
|||
search/VisitPathGroupVertices.cc
|
||||
search/WorstSlack.cc
|
||||
search/WritePathSpice.cc
|
||||
|
||||
|
||||
util/Debug.cc
|
||||
util/DispatchQueue.cc
|
||||
util/Error.cc
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
namespace sta {
|
||||
|
||||
class WriteTimingModel;
|
||||
class LibertyCellIterator;
|
||||
class LibertyCellPortIterator;
|
||||
class LibertyCellPortBitIterator;
|
||||
|
|
@ -644,6 +645,7 @@ public:
|
|||
LibertyLibrary *libertyLibrary() const { return liberty_cell_->libertyLibrary(); }
|
||||
LibertyPort *findLibertyMember(int index) const;
|
||||
LibertyPort *findLibertyBusBit(int index) const;
|
||||
void setDirection(PortDirection *dir);
|
||||
void fanoutLoad(// Return values.
|
||||
float &fanout_load,
|
||||
bool &exists) const;
|
||||
|
|
@ -769,7 +771,6 @@ protected:
|
|||
bool is_bundle,
|
||||
ConcretePortSeq *members);
|
||||
virtual ~LibertyPort();
|
||||
void setDirection(PortDirection *dir);
|
||||
void setMinPort(LibertyPort *min);
|
||||
void addScaledPort(OperatingConditions *op_cond,
|
||||
LibertyPort *scaled_port);
|
||||
|
|
|
|||
|
|
@ -28,5 +28,3 @@ writeLiberty(LibertyLibrary *lib,
|
|||
StaState *sta);
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1267,6 +1267,10 @@ public:
|
|||
// Return values.
|
||||
PowerResult &result);
|
||||
|
||||
void writeTimingModel(const char *cell_name,
|
||||
const char *filename,
|
||||
const Corner *corner);
|
||||
|
||||
// Find equivalent cells in equiv_libs.
|
||||
// Optionally add mappings for cells in map_libs.
|
||||
void makeEquivCells(LibertyLibrarySeq *equiv_libs,
|
||||
|
|
|
|||
|
|
@ -250,7 +250,9 @@ void
|
|||
LibertyWriter::writeCell(const LibertyCell *cell)
|
||||
{
|
||||
fprintf(stream_, " cell (\"%s\") {\n", cell->name());
|
||||
fprintf(stream_, " area : %.3f \n", cell->area());
|
||||
float area = cell->area();
|
||||
if (area > 0.0)
|
||||
fprintf(stream_, " area : %.3f \n", area);
|
||||
if (cell->isMacro())
|
||||
fprintf(stream_, " is_macro : true;\n");
|
||||
|
||||
|
|
|
|||
|
|
@ -476,6 +476,6 @@
|
|||
0622 PathVertex.cc:279 missing requireds.
|
||||
0623 PathVertexRep.cc:153 missing arrivals.
|
||||
0624 PathVertexRep.cc:150 missing arrivals
|
||||
0701 LibertyWriter.cc:354 %s/%s/%s timing model not supported.
|
||||
0702 LibertyWriter.cc:373 3 axis table models not supported.
|
||||
0703 LibertyWriter.cc:484 %s/%s/%s timing arc type %s not supported.
|
||||
0701 LibertyWriter.cc:360 %s/%s/%s timing model not supported.
|
||||
0702 LibertyWriter.cc:379 3 axis table models not supported.
|
||||
0703 LibertyWriter.cc:504 %s/%s/%s timing arc type %s not supported.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,276 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2022, 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 "MakeTimingModel.hh"
|
||||
|
||||
#include "Debug.hh"
|
||||
#include "Units.hh"
|
||||
#include "Transition.hh"
|
||||
#include "Liberty.hh"
|
||||
#include "liberty/LibertyBuilder.hh"
|
||||
#include "LibertyWriter.hh"
|
||||
#include "Network.hh"
|
||||
#include "PortDirection.hh"
|
||||
#include "Corner.hh"
|
||||
#include "DcalcAnalysisPt.hh"
|
||||
#include "dcalc/GraphDelayCalc1.hh"
|
||||
#include "Sdc.hh"
|
||||
#include "StaState.hh"
|
||||
#include "Sta.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
void
|
||||
writeTimingModel(const char *cell_name,
|
||||
const char *filename,
|
||||
const Corner *corner,
|
||||
Sta *sta)
|
||||
{
|
||||
MakeTimingModel writer(corner, sta);
|
||||
writer.writeTimingModel(cell_name, filename);
|
||||
}
|
||||
|
||||
MakeTimingModel::MakeTimingModel(const Corner *corner,
|
||||
Sta *sta) :
|
||||
StaState(sta),
|
||||
sta_(sta),
|
||||
corner_(corner),
|
||||
min_max_(MinMax::max()),
|
||||
lib_builder_(new LibertyBuilder)
|
||||
{
|
||||
}
|
||||
|
||||
MakeTimingModel::~MakeTimingModel()
|
||||
{
|
||||
delete lib_builder_;
|
||||
}
|
||||
|
||||
void
|
||||
MakeTimingModel::writeTimingModel(const char *cell_name,
|
||||
const char *filename)
|
||||
{
|
||||
makeTimingModel(cell_name, filename);
|
||||
writeLibertyFile(filename);
|
||||
}
|
||||
|
||||
void
|
||||
MakeTimingModel::writeLibertyFile(const char *filename)
|
||||
{
|
||||
writeLiberty(library_, filename, this);
|
||||
}
|
||||
|
||||
void
|
||||
MakeTimingModel::makeTimingModel(const char *cell_name,
|
||||
const char *filename)
|
||||
{
|
||||
makeLibrary(cell_name, filename);
|
||||
makeCell(cell_name, filename);
|
||||
makePorts();
|
||||
|
||||
for (Clock *clk : *sdc_->clocks())
|
||||
sta_->setPropagatedClock(clk);
|
||||
|
||||
#if 0
|
||||
findInputToOutputPaths();
|
||||
findInputSetupHolds();
|
||||
findClkedOutputPaths();
|
||||
#endif
|
||||
findInputSetupHolds();
|
||||
}
|
||||
|
||||
void
|
||||
MakeTimingModel::makeLibrary(const char *cell_name,
|
||||
const char *filename)
|
||||
{
|
||||
library_ = network_->makeLibertyLibrary(cell_name, filename);
|
||||
LibertyLibrary *default_lib = network_->defaultLibertyLibrary();
|
||||
*library_->units()->timeUnit() = *default_lib->units()->timeUnit();
|
||||
*library_->units()->capacitanceUnit() = *default_lib->units()->capacitanceUnit();
|
||||
*library_->units()->voltageUnit() = *default_lib->units()->voltageUnit();
|
||||
*library_->units()->resistanceUnit() = *default_lib->units()->resistanceUnit();
|
||||
*library_->units()->pullingResistanceUnit() = *default_lib->units()->pullingResistanceUnit();
|
||||
*library_->units()->powerUnit() = *default_lib->units()->powerUnit();
|
||||
*library_->units()->distanceUnit() = *default_lib->units()->distanceUnit();
|
||||
|
||||
for (RiseFall *rf : RiseFall::range()) {
|
||||
library_->setInputThreshold(rf, default_lib->inputThreshold(rf));
|
||||
library_->setOutputThreshold(rf, default_lib->outputThreshold(rf));
|
||||
library_->setSlewLowerThreshold(rf, default_lib->slewLowerThreshold(rf));
|
||||
library_->setSlewUpperThreshold(rf, default_lib->slewUpperThreshold(rf));
|
||||
}
|
||||
|
||||
library_->setDelayModelType(default_lib->delayModelType());
|
||||
library_->setNominalProcess(default_lib->nominalProcess());
|
||||
library_->setNominalVoltage(default_lib->nominalVoltage());
|
||||
library_->setNominalTemperature(default_lib->nominalTemperature());
|
||||
}
|
||||
|
||||
void
|
||||
MakeTimingModel::makeCell(const char *cell_name,
|
||||
const char *filename)
|
||||
{
|
||||
cell_ = lib_builder_->makeCell(library_, cell_name, filename);
|
||||
}
|
||||
|
||||
void
|
||||
MakeTimingModel::makePorts()
|
||||
{
|
||||
const DcalcAnalysisPt *dcalc_ap = corner_->findDcalcAnalysisPt(min_max_);
|
||||
InstancePinIterator *pin_iter = network_->pinIterator(network_->topInstance());
|
||||
while (pin_iter->hasNext()) {
|
||||
Pin *pin = pin_iter->next();
|
||||
Port *port = network_->port(pin);
|
||||
LibertyPort *lib_port = lib_builder_->makePort(cell_, network_->name(port));
|
||||
lib_port->setDirection(network_->direction(port));
|
||||
float load_cap = graph_delay_calc_->loadCap(pin, dcalc_ap);
|
||||
lib_port->setCapacitance(load_cap);
|
||||
}
|
||||
delete pin_iter;
|
||||
}
|
||||
|
||||
// input -> output combinational paths
|
||||
void
|
||||
MakeTimingModel::findInputToOutputPaths()
|
||||
{
|
||||
InstancePinIterator *input_iter = network_->pinIterator(network_->topInstance());
|
||||
while (input_iter->hasNext()) {
|
||||
Pin *input_pin = input_iter->next();
|
||||
if (network_->direction(input_pin)->isInput()
|
||||
&& !sta_->isClockSrc(input_pin)) {
|
||||
InstancePinIterator *output_iter = network_->pinIterator(network_->topInstance());
|
||||
while (output_iter->hasNext()) {
|
||||
Pin *output_pin = output_iter->next();
|
||||
if (network_->direction(output_pin)->isOutput()) {
|
||||
PinSet *from_pins = new PinSet;
|
||||
from_pins->insert(input_pin);
|
||||
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
|
||||
RiseFallBoth::riseFall());
|
||||
PinSet *to_pins = new PinSet;
|
||||
to_pins->insert(output_pin);
|
||||
ExceptionTo *to = sta_->makeExceptionTo(to_pins, nullptr, nullptr,
|
||||
RiseFallBoth::riseFall(),
|
||||
RiseFallBoth::riseFall());
|
||||
PathEndSeq *ends = sta_->findPathEnds(from, nullptr, to, true, corner_,
|
||||
min_max_->asMinMaxAll(),
|
||||
1, 1, false,
|
||||
-INF, INF, false, nullptr,
|
||||
false, false, false, false, false, false);
|
||||
if (!ends->empty()) {
|
||||
debugPrint(debug_, "timing_model", 1, "input %s -> output %s",
|
||||
network_->pathName(input_pin),
|
||||
network_->pathName(output_pin));
|
||||
PathEnd *end = (*ends)[0];
|
||||
sta_->reportPathEnd(end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// input -> register setup/hold
|
||||
void
|
||||
MakeTimingModel::findInputSetupHolds()
|
||||
{
|
||||
InstancePinIterator *input_iter = network_->pinIterator(network_->topInstance());
|
||||
while (input_iter->hasNext()) {
|
||||
Pin *input_pin = input_iter->next();
|
||||
if (network_->direction(input_pin)->isInput()
|
||||
&& !sta_->isClockSrc(input_pin)) {
|
||||
for (Clock *clk : *sdc_->clocks()) {
|
||||
for (RiseFall *clk_rf : RiseFall::range()) {
|
||||
for (MinMax *min_max : MinMax::range()) {
|
||||
MinMaxAll *min_max2 = min_max->asMinMaxAll();
|
||||
bool setup = min_max == MinMax::max();
|
||||
bool hold = !setup;
|
||||
for (RiseFall *input_rf : RiseFall::range()) {
|
||||
sdc_->setInputDelay(input_pin, RiseFallBoth::riseFall(), clk, clk_rf,
|
||||
nullptr, false, false, min_max2, false, 0.0);
|
||||
|
||||
PinSet *from_pins = new PinSet;
|
||||
from_pins->insert(input_pin);
|
||||
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
|
||||
input_rf->asRiseFallBoth());
|
||||
|
||||
ClockSet *to_clks = new ClockSet;
|
||||
to_clks->insert(clk);
|
||||
ExceptionTo *to = sta_->makeExceptionTo(nullptr, to_clks, nullptr,
|
||||
clk_rf->asRiseFallBoth(),
|
||||
RiseFallBoth::riseFall());
|
||||
PathEndSeq *ends = sta_->findPathEnds(from, nullptr, to, false, corner_,
|
||||
min_max2,
|
||||
1, 1, false,
|
||||
-INF, INF, false, nullptr,
|
||||
setup, hold, setup, hold, setup, hold);
|
||||
if (!ends->empty()) {
|
||||
debugPrint(debug_, "timing_model", 1, "%s %s %s -> clock %s %s",
|
||||
setup ? "setup" : "hold",
|
||||
network_->pathName(input_pin),
|
||||
input_rf->asString(),
|
||||
clk->name(),
|
||||
clk_rf->asString());
|
||||
PathEnd *end = (*ends)[0];
|
||||
sta_->reportPathEnd(end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MakeTimingModel::findClkedOutputPaths()
|
||||
{
|
||||
InstancePinIterator *output_iter = network_->pinIterator(network_->topInstance());
|
||||
while (output_iter->hasNext()) {
|
||||
Pin *output_pin = output_iter->next();
|
||||
if (network_->direction(output_pin)->isOutput()) {
|
||||
for (Clock *clk : *sdc_->clocks()) {
|
||||
for (RiseFall *clk_rf : RiseFall::range()) {
|
||||
sdc_->setOutputDelay(output_pin, RiseFallBoth::riseFall(), clk, clk_rf,
|
||||
nullptr, false, false, MinMaxAll::max(), false, 0.0);
|
||||
|
||||
ClockSet *from_clks = new ClockSet;
|
||||
from_clks->insert(clk);
|
||||
ExceptionFrom *from = sta_->makeExceptionFrom(nullptr, from_clks, nullptr,
|
||||
clk_rf->asRiseFallBoth());
|
||||
PinSet *to_pins = new PinSet;
|
||||
to_pins->insert(output_pin);
|
||||
ExceptionTo *to = sta_->makeExceptionTo(to_pins, nullptr, nullptr,
|
||||
RiseFallBoth::riseFall(),
|
||||
RiseFallBoth::riseFall());
|
||||
|
||||
PathEndSeq *ends = sta_->findPathEnds(from, nullptr, to, false, corner_,
|
||||
MinMaxAll::max(),
|
||||
1, 1, false,
|
||||
-INF, INF, false, nullptr,
|
||||
true, false, false, false, false, false);
|
||||
if (!ends->empty()) {
|
||||
debugPrint(debug_, "timing_model", 1, "clock %s -> output %s",
|
||||
clk->name(),
|
||||
network_->pathName(output_pin));
|
||||
PathEnd *end = (*ends)[0];
|
||||
sta_->reportPathEnd(end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2022, 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 "LibertyClass.hh"
|
||||
#include "SearchClass.hh"
|
||||
#include "StaState.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
class Sta;
|
||||
class LibertyBuilder;
|
||||
|
||||
class MakeTimingModel : public StaState
|
||||
{
|
||||
public:
|
||||
MakeTimingModel(const Corner *corner,
|
||||
Sta *sta);
|
||||
~MakeTimingModel();
|
||||
void writeTimingModel(const char *cell_name,
|
||||
const char *filename);
|
||||
void makeTimingModel(const char *cell_name,
|
||||
const char *filename);
|
||||
void writeLibertyFile(const char *filename);
|
||||
|
||||
private:
|
||||
void makeLibrary(const char *cell_name,
|
||||
const char *filename);
|
||||
void makeCell(const char *cell_name,
|
||||
const char *filename);
|
||||
void makePorts();
|
||||
void findInputToOutputPaths();
|
||||
void findInputSetupHolds();
|
||||
void findClkedOutputPaths();
|
||||
|
||||
Sta *sta_;
|
||||
LibertyLibrary *library_;
|
||||
LibertyCell *cell_;
|
||||
const Corner *corner_;
|
||||
MinMax *min_max_;
|
||||
LibertyBuilder *lib_builder_;
|
||||
};
|
||||
|
||||
void
|
||||
writeTimingModel(const char *cell_name,
|
||||
const char *filename,
|
||||
const Corner *corner,
|
||||
Sta *sta);
|
||||
|
||||
} // namespace
|
||||
|
|
@ -69,6 +69,7 @@
|
|||
#include "Power.hh"
|
||||
#include "VisitPathEnds.hh"
|
||||
#include "PathExpanded.hh"
|
||||
#include "MakeTimingModel.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
|
|
@ -5565,6 +5566,17 @@ Sta::equivCells(LibertyCell *cell)
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
Sta::writeTimingModel(const char *cell_name,
|
||||
const char *filename,
|
||||
const Corner *corner)
|
||||
{
|
||||
sta::writeTimingModel(cell_name, filename, corner, this);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
Sta::powerPreamble()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1030,6 +1030,20 @@ proc worst_clock_skew { args } {
|
|||
return [time_sta_ui [worst_clk_skew_cmd $setup_hold]]
|
||||
}
|
||||
|
||||
################################################################
|
||||
|
||||
define_cmd_args "write_timing_model" {[-corner corner] cell_name filename}
|
||||
|
||||
proc write_timing_model { args } {
|
||||
parse_key_args "write_timing_model" args keys {corner} flags {}
|
||||
check_argc_eq2 "write_timing_model" $args
|
||||
|
||||
set cell_name [lindex $args 0]
|
||||
set filename [lindex $args 1]
|
||||
set corner [parse_corner keys]
|
||||
write_timing_model_cmd $cell_name [file nativename $filename] $corner
|
||||
}
|
||||
|
||||
################################################################
|
||||
#
|
||||
# Helper functions
|
||||
|
|
|
|||
10
tcl/StaTcl.i
10
tcl/StaTcl.i
|
|
@ -5043,6 +5043,16 @@ write_path_spice_cmd(PathRef *path,
|
|||
power_name, gnd_name, sta);
|
||||
}
|
||||
|
||||
void
|
||||
write_timing_model_cmd(const char *cell_name,
|
||||
const char *filename,
|
||||
const Corner *corner)
|
||||
{
|
||||
Sta::sta()->writeTimingModel(cell_name, filename, corner);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
bool
|
||||
liberty_supply_exists(const char *supply_name)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue