564 lines
12 KiB
OpenEdge ABL
564 lines
12 KiB
OpenEdge ABL
// OpenSTA, Static Timing Analyzer
|
|
// Copyright (c) 2025, 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/>.
|
|
//
|
|
// The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software.
|
|
//
|
|
// Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
//
|
|
// This notice may not be removed or altered from any source distribution.
|
|
|
|
%module liberty
|
|
|
|
%{
|
|
#include "PortDirection.hh"
|
|
#include "Liberty.hh"
|
|
#include "EquivCells.hh"
|
|
#include "LibertyWriter.hh"
|
|
#include "Sta.hh"
|
|
|
|
using namespace sta;
|
|
|
|
%}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Empty class definitions to make swig happy.
|
|
// Private constructor/destructor so swig doesn't emit them.
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
class LibertyLibrary
|
|
{
|
|
private:
|
|
LibertyLibrary();
|
|
~LibertyLibrary();
|
|
};
|
|
|
|
class LibertyLibraryIterator
|
|
{
|
|
private:
|
|
LibertyLibraryIterator();
|
|
~LibertyLibraryIterator();
|
|
};
|
|
|
|
class LibertyCell
|
|
{
|
|
private:
|
|
LibertyCell();
|
|
~LibertyCell();
|
|
};
|
|
|
|
class LibertyPort
|
|
{
|
|
private:
|
|
LibertyPort();
|
|
~LibertyPort();
|
|
};
|
|
|
|
class LibertyCellPortIterator
|
|
{
|
|
private:
|
|
LibertyCellPortIterator();
|
|
~LibertyCellPortIterator();
|
|
};
|
|
|
|
class LibertyPortMemberIterator
|
|
{
|
|
private:
|
|
LibertyPortMemberIterator();
|
|
~LibertyPortMemberIterator();
|
|
};
|
|
|
|
class TimingArcSet
|
|
{
|
|
private:
|
|
TimingArcSet();
|
|
~TimingArcSet();
|
|
};
|
|
|
|
class TimingArc
|
|
{
|
|
private:
|
|
TimingArc();
|
|
~TimingArc();
|
|
};
|
|
|
|
class Wireload
|
|
{
|
|
private:
|
|
Wireload();
|
|
~Wireload();
|
|
};
|
|
|
|
class WireloadSelection
|
|
{
|
|
private:
|
|
WireloadSelection();
|
|
~WireloadSelection();
|
|
};
|
|
|
|
%inline %{
|
|
|
|
bool
|
|
read_liberty_cmd(char *filename,
|
|
Corner *corner,
|
|
const MinMaxAll *min_max,
|
|
bool infer_latches)
|
|
{
|
|
Sta *sta = Sta::sta();
|
|
LibertyLibrary *lib = sta->readLiberty(filename, corner, min_max, infer_latches);
|
|
return (lib != nullptr);
|
|
}
|
|
|
|
void
|
|
write_liberty_cmd(LibertyLibrary *library,
|
|
char *filename)
|
|
{
|
|
writeLiberty(library, filename, Sta::sta());
|
|
}
|
|
|
|
void
|
|
make_equiv_cells(LibertyLibrary *lib)
|
|
{
|
|
LibertyLibrarySeq libs;
|
|
libs.push_back(lib);
|
|
Sta::sta()->makeEquivCells(&libs, nullptr);
|
|
}
|
|
|
|
LibertyCellSeq *
|
|
find_equiv_cells(LibertyCell *cell)
|
|
{
|
|
return Sta::sta()->equivCells(cell);
|
|
}
|
|
|
|
bool
|
|
equiv_cells(LibertyCell *cell1,
|
|
LibertyCell *cell2)
|
|
{
|
|
return sta::equivCells(cell1, cell2);
|
|
}
|
|
|
|
bool
|
|
equiv_cell_ports(LibertyCell *cell1,
|
|
LibertyCell *cell2)
|
|
{
|
|
return equivCellPorts(cell1, cell2);
|
|
}
|
|
|
|
bool
|
|
equiv_cell_timing_arcs(LibertyCell *cell1,
|
|
LibertyCell *cell2)
|
|
{
|
|
return equivCellTimingArcSets(cell1, cell2);
|
|
}
|
|
|
|
LibertyCellSeq *
|
|
find_library_buffers(LibertyLibrary *library)
|
|
{
|
|
return library->buffers();
|
|
}
|
|
|
|
const char *
|
|
liberty_port_direction(const LibertyPort *port)
|
|
{
|
|
return port->direction()->name();
|
|
}
|
|
|
|
bool
|
|
liberty_supply_exists(const char *supply_name)
|
|
{
|
|
auto network = Sta::sta()->network();
|
|
auto lib = network->defaultLibertyLibrary();
|
|
return lib && lib->supplyExists(supply_name);
|
|
}
|
|
|
|
LibertyLibraryIterator *
|
|
liberty_library_iterator()
|
|
{
|
|
return Sta::sta()->network()->libertyLibraryIterator();
|
|
}
|
|
|
|
LibertyLibrary *
|
|
find_liberty(const char *name)
|
|
{
|
|
return Sta::sta()->network()->findLiberty(name);
|
|
}
|
|
|
|
LibertyCell *
|
|
find_liberty_cell(const char *name)
|
|
{
|
|
return Sta::sta()->network()->findLibertyCell(name);
|
|
}
|
|
|
|
bool
|
|
timing_role_is_check(const TimingRole *role)
|
|
{
|
|
return role->isTimingCheck();
|
|
}
|
|
|
|
%} // inline
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Object Methods
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
%extend LibertyLibrary {
|
|
const char *name() { return self->name(); }
|
|
|
|
LibertyCell *
|
|
find_liberty_cell(const char *name)
|
|
{
|
|
return self->findLibertyCell(name);
|
|
}
|
|
|
|
LibertyCellSeq
|
|
find_liberty_cells_matching(const char *pattern,
|
|
bool regexp,
|
|
bool nocase)
|
|
{
|
|
PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp());
|
|
return self->findLibertyCellsMatching(&matcher);
|
|
}
|
|
|
|
Wireload *
|
|
find_wireload(const char *model_name)
|
|
{
|
|
return self->findWireload(model_name);
|
|
}
|
|
|
|
WireloadSelection *
|
|
find_wireload_selection(const char *selection_name)
|
|
{
|
|
return self->findWireloadSelection(selection_name);
|
|
}
|
|
|
|
OperatingConditions *
|
|
find_operating_conditions(const char *op_cond_name)
|
|
{
|
|
return self->findOperatingConditions(op_cond_name);
|
|
}
|
|
|
|
OperatingConditions *
|
|
default_operating_conditions()
|
|
{
|
|
return self->defaultOperatingConditions();
|
|
}
|
|
|
|
} // LibertyLibrary methods
|
|
|
|
%extend LibertyCell {
|
|
const char *name() { return self->name(); }
|
|
bool is_leaf() { return self->isLeaf(); }
|
|
bool is_buffer() { return self->isBuffer(); }
|
|
bool is_inverter() { return self->isInverter(); }
|
|
LibertyLibrary *liberty_library() { return self->libertyLibrary(); }
|
|
Cell *cell() { return reinterpret_cast<Cell*>(self); }
|
|
LibertyPort *
|
|
find_liberty_port(const char *name)
|
|
{
|
|
return self->findLibertyPort(name);
|
|
}
|
|
|
|
LibertyPortSeq
|
|
find_liberty_ports_matching(const char *pattern,
|
|
bool regexp,
|
|
bool nocase)
|
|
{
|
|
PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp());
|
|
return self->findLibertyPortsMatching(&matcher);
|
|
}
|
|
|
|
LibertyCellPortIterator *
|
|
liberty_port_iterator() { return new LibertyCellPortIterator(self); }
|
|
|
|
const TimingArcSetSeq &
|
|
timing_arc_sets()
|
|
{
|
|
return self->timingArcSets();
|
|
}
|
|
|
|
void
|
|
ensure_voltage_waveforms()
|
|
{
|
|
Corners *corners = Sta::sta()->corners();
|
|
const DcalcAnalysisPtSeq &dcalc_aps = corners->dcalcAnalysisPts();
|
|
self->ensureVoltageWaveforms(dcalc_aps);
|
|
}
|
|
|
|
LibertyCell *test_cell() { return self->testCell(); }
|
|
|
|
} // LibertyCell methods
|
|
|
|
%extend LibertyPort {
|
|
const char *bus_name() { return self->busName(); }
|
|
Cell *cell() { return self->cell(); }
|
|
bool is_bus() { return self->isBus(); }
|
|
bool is_bus_bit() { return self->isBusBit(); }
|
|
bool is_bundle() { return self->isBundle(); }
|
|
bool is_bundle_member() { return self->isBundleMember(); }
|
|
bool has_members() { return self->hasMembers(); }
|
|
LibertyPortMemberIterator *
|
|
member_iterator() { return new LibertyPortMemberIterator(self); }
|
|
LibertyPort *bundle_port() { return self->bundlePort(); }
|
|
|
|
string
|
|
function()
|
|
{
|
|
FuncExpr *func = self->function();
|
|
if (func)
|
|
return func->to_string();
|
|
else
|
|
return "";
|
|
}
|
|
|
|
string
|
|
tristate_enable()
|
|
{
|
|
FuncExpr *enable = self->tristateEnable();
|
|
if (enable)
|
|
return enable->to_string();
|
|
else
|
|
return "";
|
|
}
|
|
|
|
float
|
|
capacitance(Corner *corner,
|
|
const MinMax *min_max)
|
|
{
|
|
Sta *sta = Sta::sta();
|
|
return sta->capacitance(self, corner, min_max);
|
|
}
|
|
|
|
void
|
|
set_direction(const char *dir)
|
|
{
|
|
self->setDirection(PortDirection::find(dir));
|
|
}
|
|
|
|
const char *
|
|
scan_signal_type()
|
|
{
|
|
return scanSignalTypeName(self->scanSignalType());
|
|
}
|
|
|
|
} // LibertyPort methods
|
|
|
|
%extend TimingArcSet {
|
|
LibertyPort *from() { return self->from(); }
|
|
LibertyPort *to() { return self->to(); }
|
|
const TimingRole *role() { return self->role(); }
|
|
const char *sdf_cond() { return self->sdfCond(); }
|
|
|
|
const char *
|
|
full_name()
|
|
{
|
|
const char *from = self->from()->name();
|
|
const char *to = self->to()->name();
|
|
const char *cell_name = self->libertyCell()->name();
|
|
return stringPrintTmp("%s %s -> %s",
|
|
cell_name,
|
|
from,
|
|
to);
|
|
}
|
|
|
|
TimingArcSeq &
|
|
timing_arcs() { return self->arcs(); }
|
|
|
|
} // TimingArcSet methods
|
|
|
|
%extend TimingArc {
|
|
LibertyPort *from() { return self->from(); }
|
|
LibertyPort *to() { return self->to(); }
|
|
const Transition *from_edge() { return self->fromEdge(); }
|
|
const char *from_edge_name() { return self->fromEdge()->asRiseFall()->name(); }
|
|
const Transition *to_edge() { return self->toEdge(); }
|
|
const char *to_edge_name() { return self->toEdge()->asRiseFall()->name(); }
|
|
const TimingRole *role() { return self->role(); }
|
|
|
|
float
|
|
time_voltage(float in_slew,
|
|
float load_cap,
|
|
float time)
|
|
{
|
|
GateTableModel *gate_model = self->gateTableModel();
|
|
if (gate_model) {
|
|
OutputWaveforms *waveforms = gate_model->outputWaveforms();
|
|
if (waveforms)
|
|
return waveforms->timeVoltage(in_slew, load_cap, time);
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
float
|
|
time_current(float in_slew,
|
|
float load_cap,
|
|
float time)
|
|
{
|
|
GateTableModel *gate_model = self->gateTableModel();
|
|
if (gate_model) {
|
|
OutputWaveforms *waveforms = gate_model->outputWaveforms();
|
|
if (waveforms)
|
|
return waveforms->timeCurrent(in_slew, load_cap, time);
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
float
|
|
voltage_current(float in_slew,
|
|
float load_cap,
|
|
float voltage)
|
|
{
|
|
GateTableModel *gate_model = self->gateTableModel();
|
|
if (gate_model) {
|
|
OutputWaveforms *waveforms = gate_model->outputWaveforms();
|
|
if (waveforms)
|
|
return waveforms->voltageCurrent(in_slew, load_cap, voltage);
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
float
|
|
voltage_time(float in_slew,
|
|
float load_cap,
|
|
float voltage)
|
|
{
|
|
GateTableModel *gate_model = self->gateTableModel();
|
|
if (gate_model) {
|
|
OutputWaveforms *waveforms = gate_model->outputWaveforms();
|
|
if (waveforms)
|
|
return waveforms->voltageTime(in_slew, load_cap, voltage);
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
Table1
|
|
voltage_waveform(float in_slew,
|
|
float load_cap)
|
|
{
|
|
GateTableModel *gate_model = self->gateTableModel();
|
|
if (gate_model) {
|
|
OutputWaveforms *waveforms = gate_model->outputWaveforms();
|
|
if (waveforms) {
|
|
Table1 waveform = waveforms->voltageWaveform(in_slew, load_cap);
|
|
return waveform;
|
|
}
|
|
}
|
|
return Table1();
|
|
}
|
|
|
|
const Table1 *
|
|
voltage_waveform_raw(float in_slew,
|
|
float load_cap)
|
|
{
|
|
GateTableModel *gate_model = self->gateTableModel();
|
|
if (gate_model) {
|
|
OutputWaveforms *waveforms = gate_model->outputWaveforms();
|
|
if (waveforms) {
|
|
const Table1 *waveform = waveforms->voltageWaveformRaw(in_slew, load_cap);
|
|
return waveform;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Table1
|
|
current_waveform(float in_slew,
|
|
float load_cap)
|
|
{
|
|
GateTableModel *gate_model = self->gateTableModel();
|
|
if (gate_model) {
|
|
OutputWaveforms *waveforms = gate_model->outputWaveforms();
|
|
if (waveforms) {
|
|
Table1 waveform = waveforms->currentWaveform(in_slew, load_cap);
|
|
return waveform;
|
|
}
|
|
}
|
|
return Table1();
|
|
}
|
|
|
|
const Table1 *
|
|
current_waveform_raw(float in_slew,
|
|
float load_cap)
|
|
{
|
|
GateTableModel *gate_model = self->gateTableModel();
|
|
if (gate_model) {
|
|
OutputWaveforms *waveforms = gate_model->outputWaveforms();
|
|
if (waveforms) {
|
|
const Table1 *waveform = waveforms->currentWaveformRaw(in_slew, load_cap);
|
|
return waveform;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Table1
|
|
voltage_current_waveform(float in_slew,
|
|
float load_cap)
|
|
{
|
|
GateTableModel *gate_model = self->gateTableModel();
|
|
if (gate_model) {
|
|
OutputWaveforms *waveforms = gate_model->outputWaveforms();
|
|
if (waveforms) {
|
|
Table1 waveform = waveforms->voltageCurrentWaveform(in_slew, load_cap);
|
|
return waveform;
|
|
}
|
|
}
|
|
return Table1();
|
|
}
|
|
|
|
float
|
|
final_resistance()
|
|
{
|
|
GateTableModel *gate_model = self->gateTableModel();
|
|
if (gate_model) {
|
|
OutputWaveforms *waveforms = gate_model->outputWaveforms();
|
|
if (waveforms) {
|
|
return waveforms->finalResistance();
|
|
}
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
} // TimingArc methods
|
|
|
|
%extend OperatingConditions {
|
|
float process() { return self->process(); }
|
|
float voltage() { return self->voltage(); }
|
|
float temperature() { return self->temperature(); }
|
|
}
|
|
|
|
%extend LibertyLibraryIterator {
|
|
bool has_next() { return self->hasNext(); }
|
|
LibertyLibrary *next() { return self->next(); }
|
|
void finish() { delete self; }
|
|
} // LibertyLibraryIterator methods
|
|
|
|
%extend LibertyCellPortIterator {
|
|
bool has_next() { return self->hasNext(); }
|
|
LibertyPort *next() { return self->next(); }
|
|
void finish() { delete self; }
|
|
} // LibertyCellPortIterator methods
|
|
|
|
%extend LibertyPortMemberIterator {
|
|
bool has_next() { return self->hasNext(); }
|
|
LibertyPort *next() { return self->next(); }
|
|
void finish() { delete self; }
|
|
} // LibertyPortMemberIterator methods
|