3803 lines
133 KiB
C++
3803 lines
133 KiB
C++
// 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.
|
|
|
|
#include "LibertyReader.hh"
|
|
|
|
#include <cctype>
|
|
#include <cstdlib>
|
|
#include <functional>
|
|
#include <string>
|
|
|
|
#include "ContainerHelpers.hh"
|
|
#include "EnumNameMap.hh"
|
|
#include "Report.hh"
|
|
#include "Debug.hh"
|
|
#include "Units.hh"
|
|
#include "Transition.hh"
|
|
#include "FuncExpr.hh"
|
|
#include "TimingArc.hh"
|
|
#include "TableModel.hh"
|
|
#include "LeakagePower.hh"
|
|
#include "InternalPower.hh"
|
|
#include "LinearModel.hh"
|
|
#include "Wireload.hh"
|
|
#include "EquivCells.hh"
|
|
#include "LibExprReader.hh"
|
|
#include "Liberty.hh"
|
|
#include "LibertyBuilder.hh"
|
|
#include "LibertyReaderPvt.hh"
|
|
#include "PortDirection.hh"
|
|
#include "ParseBus.hh"
|
|
#include "Network.hh"
|
|
|
|
extern int LibertyParse_debug;
|
|
|
|
namespace sta {
|
|
|
|
static void
|
|
scaleFloats(FloatSeq &floats,
|
|
float scale);
|
|
|
|
LibertyLibrary *
|
|
readLibertyFile(const char *filename,
|
|
bool infer_latches,
|
|
Network *network)
|
|
{
|
|
LibertyReader reader(filename, infer_latches, network);
|
|
return reader.readLibertyFile(filename);
|
|
}
|
|
|
|
LibertyReader::LibertyReader(const char *filename,
|
|
bool infer_latches,
|
|
Network *network) :
|
|
LibertyGroupVisitor(),
|
|
filename_(filename),
|
|
infer_latches_(infer_latches),
|
|
report_(network->report()),
|
|
debug_(network->debug()),
|
|
network_(network),
|
|
builder_(debug_, report_),
|
|
library_(nullptr)
|
|
{
|
|
defineVisitors();
|
|
}
|
|
|
|
LibertyLibrary *
|
|
LibertyReader::readLibertyFile(const char *filename)
|
|
{
|
|
//::LibertyParse_debug = 1;
|
|
parseLibertyFile(filename, this, report_);
|
|
return library_;
|
|
}
|
|
|
|
void
|
|
LibertyReader::defineGroupVisitor(const char *type,
|
|
LibraryGroupVisitor begin_visitor,
|
|
LibraryGroupVisitor end_visitor)
|
|
{
|
|
if (begin_visitor)
|
|
group_begin_map_[type] = begin_visitor;
|
|
if (end_visitor)
|
|
group_end_map_[type] = end_visitor;
|
|
}
|
|
|
|
void
|
|
LibertyReader::defineVisitors()
|
|
{
|
|
defineGroupVisitor("library", &LibertyReader::beginLibrary,
|
|
&LibertyReader::endLibrary);
|
|
defineGroupVisitor("cell", nullptr, &LibertyReader::endCell);
|
|
defineGroupVisitor("scaled_cell", nullptr, &LibertyReader::endScaledCell);
|
|
}
|
|
|
|
void
|
|
LibertyReader::visitAttr(const LibertySimpleAttr *)
|
|
{
|
|
}
|
|
|
|
void
|
|
LibertyReader::visitAttr(const LibertyComplexAttr *)
|
|
{
|
|
}
|
|
|
|
void
|
|
LibertyReader::begin(const LibertyGroup *group,
|
|
LibertyGroup *parent_group)
|
|
{
|
|
LibraryGroupVisitor *visitor = findKeyValuePtr(group_begin_map_, group->type());
|
|
if (visitor)
|
|
(this->**visitor)(group, parent_group);
|
|
}
|
|
|
|
void
|
|
LibertyReader::end(const LibertyGroup *group,
|
|
LibertyGroup *parent_group)
|
|
{
|
|
LibraryGroupVisitor *visitor = findKeyValuePtr(group_end_map_, group->type());
|
|
if (visitor)
|
|
(this->**visitor)(group, parent_group);
|
|
}
|
|
|
|
void
|
|
LibertyReader::beginLibrary(const LibertyGroup *library_group,
|
|
LibertyGroup *)
|
|
{
|
|
makeLibrary(library_group);
|
|
}
|
|
|
|
void
|
|
LibertyReader::endLibrary(const LibertyGroup *library_group,
|
|
LibertyGroup *)
|
|
{
|
|
// If a library has no cells endCell is not called.
|
|
readLibraryAttributes(library_group);
|
|
checkThresholds(library_group);
|
|
delete library_group;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::endCell(const LibertyGroup *cell_group,
|
|
LibertyGroup *library_group)
|
|
{
|
|
// Read library groups defined since the last cell was read.
|
|
// Normally they are all defined by the first cell, but there
|
|
// are libraries that define table templates and bus tyupes
|
|
// between cells.
|
|
readLibraryAttributes(library_group);
|
|
|
|
const char *name = cell_group->firstName();
|
|
if (name) {
|
|
debugPrint(debug_, "liberty", 1, "cell %s", name);
|
|
LibertyCell *cell = builder_.makeCell(library_, name, filename_);
|
|
readCell(cell, cell_group);
|
|
}
|
|
else
|
|
libWarn(1193, cell_group, "cell missing name.");
|
|
|
|
// Delete the cell group and preceding library attributes
|
|
// and groups so they are not revisited and reduce memory peak.
|
|
library_group->clear();
|
|
}
|
|
|
|
void
|
|
LibertyReader::endScaledCell(const LibertyGroup *scaled_cell_group,
|
|
LibertyGroup *library_group)
|
|
{
|
|
readLibraryAttributes(library_group);
|
|
readScaledCell(scaled_cell_group);
|
|
library_group->deleteSubgroup(scaled_cell_group);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::readLibraryAttributes(const LibertyGroup *library_group)
|
|
{
|
|
readTechnology(library_group);
|
|
readLibraryUnits(library_group);
|
|
readThresholds(library_group);
|
|
readDelayModel(library_group);
|
|
readBusStyle(library_group);
|
|
readBusTypes(nullptr, library_group);
|
|
readTableTemplates(library_group);
|
|
readVoltateMaps(library_group);
|
|
readWireloads(library_group);
|
|
readWireloadSelection(library_group);
|
|
readDefaultWireLoad(library_group);
|
|
readDefaultWireLoadMode(library_group);
|
|
readDefaultWireLoadSelection(library_group);
|
|
readOperatingConds(library_group);
|
|
readScaleFactors(library_group);
|
|
readOcvDerateFactors(nullptr, library_group);
|
|
readDefaultOcvDerateGroup(library_group);
|
|
readGroupAttrFloat("ocv_arc_depth", library_group,
|
|
[this](float v) { library_->setOcvArcDepth(v); });
|
|
readNormalizedDriverWaveform(library_group);
|
|
readSlewDegradations(library_group);
|
|
|
|
readLibAttrFloat(library_group, "nom_temperature",
|
|
&LibertyLibrary::setNominalTemperature, 1.0F);
|
|
readLibAttrFloat(library_group, "nom_voltage", &LibertyLibrary::setNominalVoltage,
|
|
volt_scale_);
|
|
readLibAttrFloat(library_group, "nom_process",
|
|
&LibertyLibrary::setNominalProcess, 1.0F);
|
|
readLibAttrFloat(library_group, "default_inout_pin_cap",
|
|
&LibertyLibrary::setDefaultBidirectPinCap, cap_scale_);
|
|
readLibAttrFloat(library_group, "default_input_pin_cap",
|
|
&LibertyLibrary::setDefaultInputPinCap, cap_scale_);
|
|
readLibAttrFloat(library_group, "default_output_pin_cap",
|
|
&LibertyLibrary::setDefaultOutputPinCap, cap_scale_);
|
|
readLibAttrFloatWarnZero(library_group, "default_max_transition",
|
|
&LibertyLibrary::setDefaultMaxSlew, time_scale_);
|
|
readLibAttrFloatWarnZero(library_group, "default_max_fanout",
|
|
&LibertyLibrary::setDefaultMaxFanout, 1.0F);
|
|
readLibAttrFloat(library_group, "default_intrinsic_rise",
|
|
&LibertyLibrary::setDefaultIntrinsic, RiseFall::rise(),
|
|
time_scale_);
|
|
readLibAttrFloat(library_group, "default_intrinsic_fall",
|
|
&LibertyLibrary::setDefaultIntrinsic, RiseFall::fall(),
|
|
time_scale_);
|
|
readLibAttrFloat(library_group, "default_inout_pin_rise_res",
|
|
&LibertyLibrary::setDefaultBidirectPinRes, RiseFall::rise(),
|
|
res_scale_);
|
|
readLibAttrFloat(library_group, "default_inout_pin_fall_res",
|
|
&LibertyLibrary::setDefaultBidirectPinRes, RiseFall::fall(),
|
|
res_scale_);
|
|
readLibAttrFloat(library_group, "default_output_pin_rise_res",
|
|
&LibertyLibrary::setDefaultOutputPinRes, RiseFall::rise(),
|
|
res_scale_);
|
|
readLibAttrFloat(library_group, "default_output_pin_fall_res",
|
|
&LibertyLibrary::setDefaultOutputPinRes, RiseFall::fall(),
|
|
res_scale_);
|
|
readLibAttrFloatWarnZero(library_group, "default_fanout_load",
|
|
&LibertyLibrary::setDefaultFanoutLoad, 1.0F);
|
|
readLibAttrFloat(library_group, "slew_derate_from_library",
|
|
&LibertyLibrary::setSlewDerateFromLibrary, 1.0F);
|
|
}
|
|
|
|
void
|
|
LibertyReader::makeLibrary(const LibertyGroup *libary_group)
|
|
{
|
|
const char *name = libary_group->firstName();
|
|
if (name) {
|
|
LibertyLibrary *library = network_->findLiberty(name);
|
|
if (library)
|
|
libWarn(1140, libary_group, "library %s already exists.", name);
|
|
// Make a new library even if a library with the same name exists.
|
|
// Both libraries may be accessed by min/max analysis points.
|
|
library_ = network_->makeLibertyLibrary(name, filename_);
|
|
// 1ns default
|
|
time_scale_ = 1E-9F;
|
|
// 1ohm default
|
|
res_scale_ = 1.0F;
|
|
// pF default
|
|
cap_scale_ = 1E-12F;
|
|
// 1v default
|
|
volt_scale_ = 1;
|
|
// Default is 1mA.
|
|
current_scale_ = 1E-3F;
|
|
// Default is 1;
|
|
power_scale_ = 1;
|
|
// Default is 1 micron.
|
|
distance_scale_ = 1e-6;
|
|
|
|
library_->units()->timeUnit()->setScale(time_scale_);
|
|
library_->units()->capacitanceUnit()->setScale(cap_scale_);
|
|
library_->units()->resistanceUnit()->setScale(res_scale_);
|
|
library_->units()->voltageUnit()->setScale(volt_scale_);
|
|
library_->units()->currentUnit()->setScale(current_scale_);
|
|
library_->units()->distanceUnit()->setScale(distance_scale_);
|
|
|
|
library_->setDelayModelType(DelayModelType::cmos_linear);
|
|
}
|
|
else
|
|
libError(1141, libary_group, "library missing name.");
|
|
}
|
|
|
|
// Energy scale is derived from other units.
|
|
float
|
|
LibertyReader::energyScale()
|
|
{
|
|
return volt_scale_ * volt_scale_ * cap_scale_;
|
|
}
|
|
|
|
void
|
|
LibertyReader::readTechnology(const LibertyGroup *library_group)
|
|
{
|
|
const LibertyComplexAttr *tech_attr = library_group->findComplexAttr("technology");
|
|
if (tech_attr) {
|
|
const LibertyAttrValue *tech_value = tech_attr->firstValue();
|
|
if (tech_value) {
|
|
const std::string &tech = tech_value->stringValue();
|
|
if (tech == "fpga")
|
|
library_->setDelayModelType(DelayModelType::cmos_linear);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readLibraryUnits(const LibertyGroup *library_group)
|
|
{
|
|
readUnit("time_unit", "s", time_scale_, library_->units()->timeUnit(), library_group);
|
|
readUnit("pulling_resistance_unit", "ohm", res_scale_,
|
|
library_->units()->resistanceUnit(), library_group);
|
|
readUnit("voltage_unit", "V", volt_scale_, library_->units()->voltageUnit(),
|
|
library_group);
|
|
readUnit("current_unit", "A", current_scale_, library_->units()->currentUnit(),
|
|
library_group);
|
|
readUnit("leakage_power_unit", "W", power_scale_, library_->units()->powerUnit(),
|
|
library_group);
|
|
readUnit("distance_unit", "m", distance_scale_, library_->units()->distanceUnit(),
|
|
library_group);
|
|
|
|
const LibertyComplexAttr *cap_attr =
|
|
library_group->findComplexAttr("capacitive_load_unit");
|
|
if (cap_attr) {
|
|
const LibertyAttrValueSeq &values = cap_attr->values();
|
|
if (values.size() == 2) {
|
|
LibertyAttrValue *value = values[0];
|
|
bool valid = false;
|
|
float scale;
|
|
if (value->isFloat()) {
|
|
scale = value->floatValue();
|
|
valid = true;
|
|
}
|
|
else if (value->isString()) {
|
|
try {
|
|
scale = std::stof(value->stringValue());
|
|
valid = true;
|
|
}
|
|
catch (...) {
|
|
valid = false;
|
|
}
|
|
}
|
|
|
|
if (valid) {
|
|
value = values[1];
|
|
if (value->isString()) {
|
|
const std::string suffix = value->stringValue();
|
|
if (stringEqual(suffix.c_str(), "ff"))
|
|
cap_scale_ = scale * 1E-15F;
|
|
else if (stringEqual(suffix.c_str(), "pf"))
|
|
cap_scale_ = scale * 1E-12F;
|
|
else
|
|
libWarn(1154, cap_attr, "capacitive_load_units are not ff or pf.");
|
|
}
|
|
else
|
|
libWarn(1155, cap_attr, "capacitive_load_units are not a string.");
|
|
}
|
|
else
|
|
libWarn(1157, cap_attr, "capacitive_load_units scale is not a float.");
|
|
}
|
|
else if (values.size() == 1)
|
|
libWarn(1156, cap_attr, "capacitive_load_units missing suffix.");
|
|
else
|
|
libWarn(1158, cap_attr, "capacitive_load_units missing scale and suffix.");
|
|
library_->units()->capacitanceUnit()->setScale(cap_scale_);
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readUnit(const char *unit_attr_name,
|
|
const char *unit_suffix,
|
|
float &scale_var,
|
|
Unit *unit,
|
|
const LibertyGroup *library_group)
|
|
{
|
|
const LibertySimpleAttr *unit_attr = library_group->findSimpleAttr(unit_attr_name);
|
|
if (unit_attr) {
|
|
const std::string *units = unit_attr->stringValue();
|
|
if (units) {
|
|
// Unit format is <multipler_digits><scale_suffix_char><unit_suffix>.
|
|
// Find the multiplier digits.
|
|
std::string units1 = *units;
|
|
size_t mult_end = units1.find_first_not_of("0123456789");
|
|
float mult = 1.0F;
|
|
std::string scale_suffix;
|
|
if (mult_end != units1.npos) {
|
|
std::string unit_mult = units1.substr(0, mult_end);
|
|
scale_suffix = units1.substr(mult_end);
|
|
if (unit_mult == "1")
|
|
mult = 1.0F;
|
|
else if (unit_mult == "10")
|
|
mult = 10.0F;
|
|
else if (unit_mult == "100")
|
|
mult = 100.0F;
|
|
else
|
|
libWarn(1150, unit_attr, "unknown unit multiplier %s.", unit_mult.c_str());
|
|
}
|
|
else
|
|
scale_suffix = *units;
|
|
|
|
float scale_mult = 1.0F;
|
|
if (scale_suffix.size() == strlen(unit_suffix) + 1) {
|
|
std::string suffix = scale_suffix.substr(1);
|
|
if (stringEqual(suffix.c_str(), unit_suffix)) {
|
|
char scale_char = tolower(scale_suffix[0]);
|
|
if (scale_char == 'k')
|
|
scale_mult = 1E+3F;
|
|
else if (scale_char == 'm')
|
|
scale_mult = 1E-3F;
|
|
else if (scale_char == 'u')
|
|
scale_mult = 1E-6F;
|
|
else if (scale_char == 'n')
|
|
scale_mult = 1E-9F;
|
|
else if (scale_char == 'p')
|
|
scale_mult = 1E-12F;
|
|
else if (scale_char == 'f')
|
|
scale_mult = 1E-15F;
|
|
else
|
|
libWarn(1151, unit_attr, "unknown unit scale %c.", scale_char);
|
|
}
|
|
else
|
|
libWarn(1152, unit_attr, "unknown unit suffix %s.", suffix.c_str());
|
|
}
|
|
else if (!stringEqual(scale_suffix.c_str(), unit_suffix))
|
|
libWarn(1153, unit_attr, "unknown unit suffix %s.", scale_suffix.c_str());
|
|
scale_var = scale_mult * mult;
|
|
unit->setScale(scale_var);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readDelayModel(const LibertyGroup *library_group)
|
|
{
|
|
const std::string *type_name = library_group->findAttrString("delay_model");
|
|
if (type_name) {
|
|
if (*type_name == "table_lookup")
|
|
library_->setDelayModelType(DelayModelType::table);
|
|
else if (*type_name == "generic_cmos")
|
|
library_->setDelayModelType(DelayModelType::cmos_linear);
|
|
else if (*type_name == "piecewise_cmos") {
|
|
library_->setDelayModelType(DelayModelType::cmos_pwl);
|
|
libWarn(1160, library_group, "delay_model %s not supported.", type_name->c_str());
|
|
}
|
|
else if (*type_name == "cmos2") {
|
|
library_->setDelayModelType(DelayModelType::cmos2);
|
|
libWarn(1161, library_group, "delay_model %s not supported.", type_name->c_str());
|
|
}
|
|
else if (*type_name == "polynomial") {
|
|
library_->setDelayModelType(DelayModelType::polynomial);
|
|
libWarn(1162, library_group, "delay_model %s not supported.", type_name->c_str());
|
|
}
|
|
// Evil IBM garbage.
|
|
else if (*type_name == "dcm") {
|
|
library_->setDelayModelType(DelayModelType::dcm);
|
|
libWarn(1163, library_group, "delay_model %s not supported..", type_name->c_str());
|
|
}
|
|
else
|
|
libWarn(1164, library_group, "unknown delay_model %s.", type_name->c_str());
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readBusStyle(const LibertyGroup *library_group)
|
|
{
|
|
const std::string *bus_style = library_group->findAttrString("bus_naming_style");
|
|
if (bus_style) {
|
|
// Assume bus style is of the form "%s[%d]".
|
|
if (bus_style->size() == 6
|
|
&& (*bus_style)[0] == '%'
|
|
&& (*bus_style)[1] == 's'
|
|
&& (*bus_style)[3] == '%'
|
|
&& (*bus_style)[4] == 'd')
|
|
library_->setBusBrkts((*bus_style)[2], (*bus_style)[5]);
|
|
else
|
|
libWarn(1165, library_group, "unknown bus_naming_style format.");
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readBusTypes(LibertyCell *cell,
|
|
const LibertyGroup *group)
|
|
{
|
|
for (const LibertyGroup *type_group : group->findSubgroups("type")) {
|
|
const char *name = type_group->firstName();
|
|
if (name) {
|
|
int from, to;
|
|
bool from_exists, to_exists;
|
|
type_group->findAttrInt("bit_from", from, from_exists);
|
|
type_group->findAttrInt("bit_to", to, to_exists);
|
|
if (from_exists && to_exists) {
|
|
if (cell)
|
|
cell->makeBusDcl(name, from, to);
|
|
else
|
|
library_->makeBusDcl(name, from, to);
|
|
}
|
|
else if (!from_exists)
|
|
libWarn(1179, type_group, "bus type missing bit_from.");
|
|
else if (!to_exists)
|
|
libWarn(1180, type_group, "bus type missing bit_to.");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readThresholds(const LibertyGroup *library_group)
|
|
{
|
|
for (const RiseFall *rf : RiseFall::range()) {
|
|
std::string suffix = rf->to_string();
|
|
readLibAttrFloat(library_group, ("input_threshold_pct_" + suffix).c_str(),
|
|
&LibertyLibrary::setInputThreshold, rf, 0.01F);
|
|
readLibAttrFloat(library_group, ("output_threshold_pct_" + suffix).c_str(),
|
|
&LibertyLibrary::setOutputThreshold, rf, 0.01F);
|
|
readLibAttrFloat(library_group, ("slew_lower_threshold_pct_" + suffix).c_str(),
|
|
&LibertyLibrary::setSlewLowerThreshold, rf, 0.01F);
|
|
readLibAttrFloat(library_group, ("slew_upper_threshold_pct_" + suffix).c_str(),
|
|
&LibertyLibrary::setSlewUpperThreshold, rf, 0.01F);
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::checkThresholds(const LibertyGroup *library_group) const
|
|
{
|
|
for (const RiseFall *rf : RiseFall::range()) {
|
|
if (library_->inputThreshold(rf) == 0.0)
|
|
libWarn(1145, library_group, "input_threshold_pct_%s not found.", rf->name());
|
|
if (library_->outputThreshold(rf) == 0.0)
|
|
libWarn(1146, library_group, "output_threshold_pct_%s not found.", rf->name());
|
|
if (library_->slewLowerThreshold(rf) == 0.0)
|
|
libWarn(1147, library_group, "slew_lower_threshold_pct_%s not found.", rf->name());
|
|
if (library_->slewUpperThreshold(rf) == 0.0)
|
|
libWarn(1148, library_group, "slew_upper_threshold_pct_%s not found.", rf->name());
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readTableTemplates(const LibertyGroup *library_group)
|
|
{
|
|
readTableTemplates(library_group, "lu_table_template", TableTemplateType::delay);
|
|
readTableTemplates(library_group, "output_current_template",
|
|
TableTemplateType::output_current);
|
|
readTableTemplates(library_group, "power_lut_template", TableTemplateType::power);
|
|
readTableTemplates(library_group, "ocv_table_template", TableTemplateType::ocv);
|
|
}
|
|
|
|
void
|
|
LibertyReader::readTableTemplates(const LibertyGroup *library_group,
|
|
const char *group_name,
|
|
TableTemplateType type)
|
|
{
|
|
for (const LibertyGroup *template_group : library_group->findSubgroups(group_name)) {
|
|
const char *name = template_group->firstName();
|
|
if (name) {
|
|
TableTemplate *tbl_template = library_->makeTableTemplate(name, type);
|
|
TableAxisPtr axis1 = makeTableTemplateAxis(template_group, 1);
|
|
if (axis1)
|
|
tbl_template->setAxis1(axis1);
|
|
TableAxisPtr axis2 = makeTableTemplateAxis(template_group, 2);
|
|
if (axis2)
|
|
tbl_template->setAxis2(axis2);
|
|
TableAxisPtr axis3 = makeTableTemplateAxis(template_group, 3);
|
|
if (axis3)
|
|
tbl_template->setAxis3(axis3);
|
|
}
|
|
else
|
|
libWarn(1175, template_group, "table template missing name.");
|
|
}
|
|
}
|
|
|
|
TableAxisPtr
|
|
LibertyReader::makeTableTemplateAxis(const LibertyGroup *template_group,
|
|
int axis_index)
|
|
{
|
|
std::string var_attr_name = "variable_" + std::to_string(axis_index);
|
|
const std::string *var_name = template_group->findAttrString(var_attr_name);
|
|
if (var_name) {
|
|
TableAxisVariable axis_var = stringTableAxisVariable(var_name->c_str());
|
|
if (axis_var == TableAxisVariable::unknown)
|
|
libWarn(1297, template_group, "axis type %s not supported.", var_name->c_str());
|
|
else {
|
|
std::string index_attr_name = "index_" + std::to_string(axis_index);
|
|
const LibertyComplexAttr *index_attr =
|
|
template_group->findComplexAttr(index_attr_name);
|
|
FloatSeq axis_values;
|
|
if (index_attr) {
|
|
axis_values = readFloatSeq(index_attr, 1.0F);
|
|
if (!axis_values.empty()) {
|
|
float prev = axis_values[0];
|
|
for (size_t i = 1; i < axis_values.size(); i++) {
|
|
float value = axis_values[i];
|
|
if (value <= prev) {
|
|
libWarn(1178, template_group, "non-increasing table index values.");
|
|
break;
|
|
}
|
|
prev = value;
|
|
}
|
|
}
|
|
}
|
|
const Units *units = library_->units();
|
|
float scale = tableVariableUnit(axis_var, units)->scale();
|
|
scaleFloats(axis_values, scale);
|
|
return make_shared<TableAxis>(axis_var, std::move(axis_values));
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static void
|
|
scaleFloats(FloatSeq &floats,
|
|
float scale)
|
|
{
|
|
size_t count = floats.size();
|
|
for (size_t i = 0; i < count; i++)
|
|
floats[i] *= scale;
|
|
}
|
|
|
|
void
|
|
LibertyReader::readVoltateMaps(const LibertyGroup *library_group)
|
|
{
|
|
for (const LibertyComplexAttr *volt_attr :
|
|
library_group->findComplexAttrs("voltage_map")) {
|
|
const LibertyAttrValueSeq &values = volt_attr->values();
|
|
if (values.size() == 2) {
|
|
const std::string &volt_name = values[0]->stringValue();
|
|
float volt;
|
|
bool valid;
|
|
values[1]->floatValue(volt, valid);
|
|
if (valid)
|
|
library_->addSupplyVoltage(volt_name.c_str(), volt);
|
|
else
|
|
libWarn(1166, volt_attr, "voltage_map voltage is not a float.");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readOperatingConds(const LibertyGroup *library_group)
|
|
{
|
|
for (const LibertyGroup *opcond_group :
|
|
library_group->findSubgroups("operating_conditions")) {
|
|
const char *name = opcond_group->firstName();
|
|
if (name) {
|
|
OperatingConditions *op_cond = library_->makeOperatingConditions(name);
|
|
float value;
|
|
bool exists;
|
|
opcond_group->findAttrFloat("process", value, exists);
|
|
if (exists)
|
|
op_cond->setProcess(value);
|
|
opcond_group->findAttrFloat("temperature", value, exists);
|
|
if (exists)
|
|
op_cond->setTemperature(value);
|
|
opcond_group->findAttrFloat("voltage", value, exists);
|
|
if (exists)
|
|
op_cond->setVoltage(value);
|
|
const std::string *tree_type = opcond_group->findAttrString("tree_type");
|
|
if (tree_type) {
|
|
WireloadTree wireload_tree = stringWireloadTree(tree_type->c_str());
|
|
op_cond->setWireloadTree(wireload_tree);
|
|
}
|
|
}
|
|
}
|
|
|
|
const std::string *default_op_cond =
|
|
library_group->findAttrString("default_operating_conditions");
|
|
if (default_op_cond) {
|
|
OperatingConditions *op_cond =
|
|
library_->findOperatingConditions(default_op_cond->c_str());
|
|
if (op_cond)
|
|
library_->setDefaultOperatingConditions(op_cond);
|
|
else
|
|
libWarn(1144, library_group, "default_operating_condition %s not found.",
|
|
default_op_cond->c_str());
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readScaleFactors(const LibertyGroup *library_group)
|
|
{
|
|
// Top level scale factors.
|
|
ScaleFactors *scale_factors = library_->makeScaleFactors("");
|
|
library_->setScaleFactors(scale_factors);
|
|
readScaleFactors(library_group, scale_factors);
|
|
|
|
// Named scale factors.
|
|
for (const LibertyGroup *scale_group : library_group->findSubgroups("scaling_factors")){
|
|
const char *name = scale_group->firstName();
|
|
if (name) {
|
|
ScaleFactors *scale_factors = library_->makeScaleFactors(name);
|
|
readScaleFactors(scale_group, scale_factors);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readScaleFactors(const LibertyGroup *scale_group,
|
|
ScaleFactors *scale_factors)
|
|
{
|
|
// Skip unknown type.
|
|
for (int type_index = 0; type_index < scale_factor_type_count - 1; type_index++) {
|
|
ScaleFactorType type = static_cast<ScaleFactorType>(type_index);
|
|
const char *type_name = scaleFactorTypeName(type);
|
|
// Skip unknown pvt.
|
|
for (int pvt_index = 0; pvt_index < scale_factor_pvt_count - 1; pvt_index++) {
|
|
ScaleFactorPvt pvt = static_cast<ScaleFactorPvt>(pvt_index);
|
|
const std::string pvt_name = scaleFactorPvtName(pvt);
|
|
std::string attr_name;
|
|
for (const RiseFall *rf : RiseFall::range()) {
|
|
if (scaleFactorTypeRiseFallSuffix(type)) {
|
|
const std::string rf_name = (rf == RiseFall::rise()) ? "rise" : "fall";
|
|
attr_name = "k_" + pvt_name + "_" + type_name + "_" + rf_name;
|
|
}
|
|
else if (scaleFactorTypeRiseFallPrefix(type)) {
|
|
const char *rf_name = (rf == RiseFall::rise()) ? "rise" : "fall";
|
|
attr_name = "k_" + pvt_name + "_" + rf_name + "_" + type_name;
|
|
}
|
|
else if (scaleFactorTypeLowHighSuffix(type)) {
|
|
const char *rf_name = (rf == RiseFall::rise()) ? "high":"low";
|
|
attr_name = "k_" + pvt_name + "_" + type_name + "_" + rf_name;
|
|
}
|
|
else
|
|
attr_name = "k_" + pvt_name + "_" + type_name;
|
|
float value;
|
|
bool exists;
|
|
scale_group->findAttrFloat(attr_name, value, exists);
|
|
if (exists)
|
|
scale_factors->setScale(type, pvt, rf, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readWireloads(const LibertyGroup *library_group)
|
|
{
|
|
for (const LibertyGroup *wl_group : library_group->findSubgroups("wire_load")) {
|
|
const char *name = wl_group->firstName();
|
|
if (name) {
|
|
Wireload *wireload = library_->makeWireload(name);
|
|
float value;
|
|
bool exists;
|
|
wl_group->findAttrFloat("resistance", value, exists);
|
|
if (exists)
|
|
wireload->setResistance(value * res_scale_);
|
|
|
|
wl_group->findAttrFloat("capacitance", value, exists);
|
|
if (exists)
|
|
wireload->setCapacitance(value * cap_scale_);
|
|
|
|
wl_group->findAttrFloat("slope", value, exists);
|
|
if (exists)
|
|
wireload->setSlope(value);
|
|
|
|
for (const LibertyComplexAttr *fanout_attr :
|
|
wl_group->findComplexAttrs("fanout_length")) {
|
|
float fanout, length;
|
|
bool exists;
|
|
getAttrFloat2(fanout_attr, fanout, length, exists);
|
|
if (exists)
|
|
wireload->addFanoutLength(fanout, length);
|
|
else
|
|
libWarn(1185, fanout_attr, "fanout_length is missing length and fanout.");
|
|
}
|
|
}
|
|
else
|
|
libWarn(1184, wl_group, "wire_load missing name.");
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readWireloadSelection(const LibertyGroup *library_group)
|
|
{
|
|
const LibertyGroup *sel_group = library_group->findSubgroup("wire_load_selection");
|
|
if (sel_group) {
|
|
const char *name = sel_group->firstName();
|
|
if (name == nullptr)
|
|
name = "";
|
|
WireloadSelection *wireload_selection = library_->makeWireloadSelection(name);
|
|
for (const LibertyComplexAttr *area_attr :
|
|
sel_group->findComplexAttrs("wire_load_from_area")) {
|
|
const LibertyAttrValueSeq &values = area_attr->values();
|
|
if (values.size() == 3) {
|
|
LibertyAttrValue *value = values[0];
|
|
if (value->isFloat()) {
|
|
float min_area = value->floatValue();
|
|
value = values[1];
|
|
if (value->isFloat()) {
|
|
float max_area = value->floatValue();
|
|
value = values[2];
|
|
if (value->isString()) {
|
|
const std::string &wireload_name = value->stringValue();
|
|
const Wireload *wireload =
|
|
library_->findWireload(wireload_name.c_str());
|
|
if (wireload)
|
|
wireload_selection->addWireloadFromArea(min_area, max_area,
|
|
wireload);
|
|
else
|
|
libWarn(1187, area_attr, "wireload %s not found.", wireload_name.c_str());
|
|
}
|
|
else
|
|
libWarn(1188, area_attr,
|
|
"wire_load_from_area wireload name not a string.");
|
|
}
|
|
else
|
|
libWarn(1189, area_attr, "wire_load_from_area min not a float.");
|
|
}
|
|
else
|
|
libWarn(1190, area_attr, "wire_load_from_area max not a float.");
|
|
}
|
|
else
|
|
libWarn(1191, area_attr, "wire_load_from_area missing parameters.");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readDefaultWireLoad(const LibertyGroup *library_group)
|
|
{
|
|
const std::string *wireload_name = library_group->findAttrString("default_wire_load");
|
|
if (wireload_name) {
|
|
const Wireload *wireload = library_->findWireload(wireload_name->c_str());
|
|
if (wireload)
|
|
library_->setDefaultWireload(wireload);
|
|
else
|
|
libWarn(1142, library_group, "default_wire_load %s not found.",
|
|
wireload_name->c_str());
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readDefaultWireLoadMode(const LibertyGroup *library_group)
|
|
{
|
|
const std::string *wire_load_mode =
|
|
library_group->findAttrString("default_wire_load_mode");
|
|
if (wire_load_mode) {
|
|
WireloadMode mode = stringWireloadMode(wire_load_mode->c_str());
|
|
if (mode != WireloadMode::unknown)
|
|
library_->setDefaultWireloadMode(mode);
|
|
else
|
|
libWarn(1174, library_group, "default_wire_load_mode %s not found.",
|
|
wire_load_mode->c_str());
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readDefaultWireLoadSelection(const LibertyGroup *library_group)
|
|
{
|
|
const std::string *selection_name =
|
|
library_group->findAttrString("default_wire_load_selection");
|
|
if (selection_name) {
|
|
const WireloadSelection *selection =
|
|
library_->findWireloadSelection(selection_name->c_str());
|
|
if (selection)
|
|
library_->setDefaultWireloadSelection(selection);
|
|
else
|
|
libWarn(1143, library_group, "default_wire_selection %s not found.",
|
|
selection_name->c_str());
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readModeDefs(LibertyCell *cell,
|
|
const LibertyGroup *cell_group)
|
|
{
|
|
for (const LibertyGroup *mode_group : cell_group->findSubgroups("mode_definition")) {
|
|
const char *name = mode_group->firstName();
|
|
if (name) {
|
|
ModeDef *mode_def = cell->makeModeDef(name);
|
|
for (const LibertyGroup *value_group : mode_group->findSubgroups("mode_value")) {
|
|
const char *value_name = value_group->firstName();
|
|
if (value_name) {
|
|
ModeValueDef *mode_value = mode_def->defineValue(value_name, nullptr, nullptr);
|
|
const std::string *sdf_cond = value_group->findAttrString("sdf_cond");
|
|
if (sdf_cond)
|
|
mode_value->setSdfCond(sdf_cond->c_str());
|
|
const std::string *when = value_group->findAttrString("when");
|
|
if (when) {
|
|
// line
|
|
FuncExpr *when_expr = parseFunc(when->c_str(), "when", cell,
|
|
value_group->line());
|
|
mode_value->setCond(when_expr);
|
|
}
|
|
}
|
|
else
|
|
libWarn(1264, value_group, "mode value missing name.");
|
|
}
|
|
}
|
|
else
|
|
libWarn(1263, mode_group, "mode definition missing name.");
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readSlewDegradations(const LibertyGroup *library_group)
|
|
{
|
|
for (const RiseFall *rf : RiseFall::range()) {
|
|
const std::string group_name = rf->to_string() + "_transition_degradation";
|
|
const LibertyGroup *degradation_group =
|
|
library_group->findSubgroup(group_name.c_str());
|
|
if (degradation_group) {
|
|
TableModel *table_model = readTableModel(degradation_group, rf,
|
|
TableTemplateType::delay,
|
|
time_scale_,
|
|
ScaleFactorType::transition);
|
|
if (LibertyLibrary::checkSlewDegradationAxes(table_model))
|
|
library_->setWireSlewDegradationTable(table_model, rf);
|
|
else
|
|
libWarn(1254, degradation_group, "unsupported model axis.");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readLibAttrFloat(const LibertyGroup *library_group,
|
|
const char *attr_name,
|
|
void (LibertyLibrary::*set_func)(float value),
|
|
float scale)
|
|
{
|
|
float value;
|
|
bool exists;
|
|
library_group->findAttrFloat(attr_name, value, exists);
|
|
if (exists)
|
|
(library_->*set_func)(value * scale);
|
|
}
|
|
|
|
void
|
|
LibertyReader::readLibAttrFloat(const LibertyGroup *library_group,
|
|
const char *attr_name,
|
|
void (LibertyLibrary::*set_func)(const RiseFall *rf,
|
|
float value),
|
|
const RiseFall *rf,
|
|
float scale)
|
|
{
|
|
float value;
|
|
bool exists;
|
|
library_group->findAttrFloat(attr_name, value, exists);
|
|
if (exists)
|
|
(library_->*set_func)(rf, value * scale);
|
|
}
|
|
|
|
void
|
|
LibertyReader::readLibAttrFloatWarnZero(const LibertyGroup *library_group,
|
|
const char *attr_name,
|
|
void (LibertyLibrary::*set_func)(float value),
|
|
float scale)
|
|
{
|
|
float value;
|
|
bool exists;
|
|
library_group->findAttrFloat(attr_name, value, exists);
|
|
if (exists) {
|
|
if (value == 0.0F) {
|
|
const LibertySimpleAttr *attr = library_group->findSimpleAttr(attr_name);
|
|
if (attr)
|
|
libWarn(1171, attr, "%s is 0.0.", attr_name);
|
|
else
|
|
libWarn(1172, library_group, "%s is 0.0.", attr_name);
|
|
}
|
|
(library_->*set_func)(value * scale);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::readCell(LibertyCell *cell,
|
|
const LibertyGroup *cell_group)
|
|
{
|
|
readBusTypes(cell, cell_group);
|
|
// Make ports first because they are referenced by functions, timing arcs, etc.
|
|
LibertyPortGroupMap port_group_map = makeCellPorts(cell, cell_group);
|
|
|
|
// Make ff/latch output ports.
|
|
makeSequentials(cell, cell_group);
|
|
|
|
readCellAttributes(cell, cell_group);
|
|
|
|
// Set port directions before making timing arcs etc.
|
|
for (auto const &[port_group, ports] : port_group_map)
|
|
readPortDir(ports, port_group);
|
|
|
|
for (auto const &[port_group, ports] : port_group_map) {
|
|
readPortAttributes(cell, ports, port_group);
|
|
makePortFuncs(cell, ports, port_group);
|
|
makeTimingArcs(cell, ports, port_group);
|
|
readInternalPowerGroups(cell, ports, port_group);
|
|
}
|
|
|
|
readTestCell(cell, cell_group);
|
|
|
|
cell->finish(infer_latches_, report_, debug_);
|
|
}
|
|
|
|
void
|
|
LibertyReader::readScaledCell(const LibertyGroup *scaled_cell_group)
|
|
{
|
|
const char *name = scaled_cell_group->firstName();
|
|
if (name) {
|
|
LibertyCell *owner = library_->findLibertyCell(name);
|
|
if (owner) {
|
|
const char *op_cond_name = scaled_cell_group->secondName();
|
|
if (op_cond_name) {
|
|
OperatingConditions *op_cond = library_->findOperatingConditions(op_cond_name);
|
|
if (op_cond) {
|
|
debugPrint(debug_, "liberty", 1, "scaled cell %s %s",
|
|
name, op_cond_name);
|
|
LibertyCell *scaled_cell = library_->makeScaledCell(name, filename_);
|
|
readCell(scaled_cell, scaled_cell_group);
|
|
checkScaledCell(scaled_cell, owner, scaled_cell_group, op_cond_name);
|
|
// Add scaled cell AFTER ports and timing arcs are defined.
|
|
owner->addScaledCell(op_cond, scaled_cell);
|
|
}
|
|
else
|
|
libWarn(1202, scaled_cell_group, "operating conditions %s not found.",
|
|
op_cond_name);
|
|
}
|
|
else
|
|
libWarn(1203, scaled_cell_group, "scaled_cell missing operating condition.");
|
|
}
|
|
else
|
|
libWarn(1204, scaled_cell_group, "scaled_cell cell %s has not been defined.", name);
|
|
}
|
|
else
|
|
libWarn(1205, scaled_cell_group, "scaled_cell missing name.");
|
|
}
|
|
|
|
// Minimal check that is not very specific about where the discrepancies are.
|
|
void
|
|
LibertyReader::checkScaledCell(LibertyCell *scaled_cell,
|
|
LibertyCell *owner,
|
|
const LibertyGroup *scaled_cell_group,
|
|
const char *op_cond_name)
|
|
{
|
|
if (equivCellPorts(scaled_cell, owner)) {
|
|
if (!equivCellPorts(scaled_cell, owner))
|
|
libWarn(1206, scaled_cell_group, "scaled_cell %s, %s ports do not match cell ports",
|
|
scaled_cell->name(),
|
|
op_cond_name);
|
|
if (!equivCellFuncs(scaled_cell, owner))
|
|
libWarn(1206, scaled_cell_group,
|
|
"scaled_cell %s, %s port functions do not match cell port functions.",
|
|
scaled_cell->name(),
|
|
op_cond_name);
|
|
}
|
|
else
|
|
libWarn(1207, scaled_cell_group, "scaled_cell ports do not match cell ports.");
|
|
if (!equivCellTimingArcSets(scaled_cell, owner))
|
|
libWarn(1208, scaled_cell_group,
|
|
"scaled_cell %s, %s timing does not match cell timing.",
|
|
scaled_cell->name(),
|
|
op_cond_name);
|
|
}
|
|
|
|
LibertyPortGroupMap
|
|
LibertyReader::makeCellPorts(LibertyCell *cell,
|
|
const LibertyGroup *cell_group)
|
|
{
|
|
LibertyPortGroupMap port_group_map;
|
|
for (const LibertyGroup *subgroup : cell_group->subgroups()) {
|
|
const std::string &type = subgroup->type();
|
|
if (type == "pin")
|
|
makePinPort(cell, subgroup, port_group_map);
|
|
else if (type == "bus")
|
|
makeBusPort(cell, subgroup, port_group_map);
|
|
else if (type == "bundle")
|
|
makeBundlePort(cell, subgroup, port_group_map);
|
|
else if (type == "pg_pin")
|
|
makePgPinPort(cell, subgroup);
|
|
}
|
|
return port_group_map;
|
|
}
|
|
|
|
void
|
|
LibertyReader::makePinPort(LibertyCell *cell,
|
|
const LibertyGroup *pin_group,
|
|
LibertyPortGroupMap &port_group_map)
|
|
{
|
|
for (const LibertyAttrValue *port_value : pin_group->params()) {
|
|
const std::string &port_name = port_value->stringValue();
|
|
LibertyPort *port = makePort(cell, port_name.c_str());
|
|
port_group_map[pin_group].push_back(port);
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::makeBusPort(LibertyCell *cell,
|
|
const LibertyGroup *bus_group,
|
|
LibertyPortGroupMap &port_group_map)
|
|
{
|
|
for (const LibertyAttrValue *port_value : bus_group->params()) {
|
|
const std::string &port_name = port_value->stringValue();
|
|
const LibertySimpleAttr *bus_type_attr = bus_group->findSimpleAttr("bus_type");
|
|
if (bus_type_attr) {
|
|
const std::string *bus_type = bus_type_attr->stringValue();
|
|
if (bus_type) {
|
|
// Look for bus dcl local to cell first.
|
|
BusDcl *bus_dcl = cell->findBusDcl(bus_type->c_str());
|
|
if (bus_dcl == nullptr)
|
|
bus_dcl = library_->findBusDcl(bus_type->c_str());
|
|
if (bus_dcl) {
|
|
debugPrint(debug_, "liberty", 1, " bus %s", port_name.c_str());
|
|
LibertyPort *bus_port = makeBusPort(cell, port_name.c_str(),
|
|
bus_dcl->from(), bus_dcl->to(),
|
|
bus_dcl);
|
|
port_group_map[bus_group].push_back(bus_port);
|
|
// Make ports for pin groups inside the bus group.
|
|
makeBusPinPorts(cell, bus_group, port_group_map);
|
|
}
|
|
else
|
|
libWarn(1235, bus_type_attr, "bus_type %s not found.", bus_type->c_str());
|
|
}
|
|
}
|
|
else
|
|
libWarn(1236, bus_type_attr, "bus_type not found.");
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::makeBusPinPorts(LibertyCell *cell,
|
|
const LibertyGroup *bus_group,
|
|
LibertyPortGroupMap &port_group_map)
|
|
{
|
|
for (const LibertyGroup *pin_group : bus_group->findSubgroups("pin")) {
|
|
for (const LibertyAttrValue *param : pin_group->params()) {
|
|
if (param->isString()) {
|
|
const std::string pin_name = param->stringValue();
|
|
debugPrint(debug_, "liberty", 1, " bus pin port %s", pin_name.c_str());
|
|
// Expand foo[3:0] port names.
|
|
PortNameBitIterator name_iter(cell, pin_name.c_str(), this, pin_group->line());
|
|
while (name_iter.hasNext()) {
|
|
LibertyPort *pin_port = name_iter.next();
|
|
if (pin_port) {
|
|
port_group_map[pin_group].push_back(pin_port);
|
|
}
|
|
else
|
|
libWarn(1232, pin_group, "pin %s not found.", pin_name.c_str());
|
|
}
|
|
}
|
|
else
|
|
libWarn(1233, pin_group, "pin name is not a string.");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::makeBundlePort(LibertyCell *cell,
|
|
const LibertyGroup *bundle_group,
|
|
LibertyPortGroupMap &port_group_map)
|
|
{
|
|
const std::string &bundle_name = bundle_group->firstName();
|
|
debugPrint(debug_, "liberty", 1, " bundle %s", bundle_name.c_str());
|
|
|
|
const LibertyComplexAttr *member_attr = bundle_group->findComplexAttr("members");
|
|
ConcretePortSeq *members = new ConcretePortSeq;
|
|
for (const LibertyAttrValue *member_value : member_attr->values()) {
|
|
if (member_value->isString()) {
|
|
const char *member_name = member_value->stringValue().c_str();
|
|
LibertyPort *member = cell->findLibertyPort(member_name);
|
|
if (member == nullptr)
|
|
member = makePort(cell, member_name);
|
|
members->push_back(member);
|
|
}
|
|
}
|
|
LibertyPort *bundle_port = builder_.makeBundlePort(cell, bundle_name.c_str(),
|
|
members);
|
|
port_group_map[bundle_group].push_back(bundle_port);
|
|
// Make ports for pin groups inside the bundle group.
|
|
makeBundlePinPorts(cell, bundle_group, port_group_map);
|
|
}
|
|
|
|
void
|
|
LibertyReader::makeBundlePinPorts(LibertyCell *cell,
|
|
const LibertyGroup *bundle_group,
|
|
LibertyPortGroupMap &port_group_map)
|
|
{
|
|
for (const LibertyGroup *pin_group : bundle_group->findSubgroups("pin")) {
|
|
for (LibertyAttrValue *param : pin_group->params()) {
|
|
if (param->isString()) {
|
|
const std::string pin_name = param->stringValue();
|
|
debugPrint(debug_, "liberty", 1, " bundle pin port %s", pin_name.c_str());
|
|
LibertyPort *pin_port = cell->findLibertyPort(pin_name.c_str());
|
|
if (pin_port == nullptr)
|
|
pin_port = makePort(cell, pin_name.c_str());
|
|
port_group_map[pin_group].push_back(pin_port);
|
|
}
|
|
else
|
|
libWarn(1234, pin_group, "pin name is not a string.");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::makePgPinPort(LibertyCell *cell,
|
|
const LibertyGroup *pg_pin_group)
|
|
{
|
|
const std::string &port_name = pg_pin_group->firstName();
|
|
LibertyPort *pg_port = makePort(cell, port_name.c_str());
|
|
|
|
const std::string *type_name = pg_pin_group->findAttrString("pg_type");
|
|
if (type_name) {
|
|
PwrGndType type = findPwrGndType(type_name->c_str());
|
|
PortDirection *dir = PortDirection::unknown();
|
|
switch (type) {
|
|
case PwrGndType::primary_ground:
|
|
case PwrGndType::backup_ground:
|
|
case PwrGndType::internal_ground:
|
|
dir = PortDirection::ground();
|
|
break;
|
|
case PwrGndType::primary_power:
|
|
case PwrGndType::backup_power:
|
|
case PwrGndType::internal_power:
|
|
dir = PortDirection::power();
|
|
break;
|
|
case PwrGndType::none:
|
|
libError(1291, pg_pin_group, "unknown pg_type.");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
pg_port->setPwrGndType(type);
|
|
pg_port->setDirection(dir);
|
|
}
|
|
|
|
const std::string *voltate_name = pg_pin_group->findAttrString("voltage_name");
|
|
if (voltate_name)
|
|
pg_port->setVoltageName(voltate_name->c_str());
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::readPortAttributes(LibertyCell *cell,
|
|
const LibertyPortSeq &ports,
|
|
const LibertyGroup *port_group)
|
|
{
|
|
readCapacitance(ports, port_group);
|
|
readMinPulseWidth(cell, ports, port_group);
|
|
readPortAttrFloat("min_period", &LibertyPort::setMinPeriod, ports,
|
|
port_group, time_scale_);
|
|
readPortAttrBool("clock", &LibertyPort::setIsClock, ports, port_group);
|
|
readPortAttrFloat("fanout_load", &LibertyPort::setFanoutLoad, ports,
|
|
port_group, 1.0F);
|
|
readPortAttrFloatMinMax("max_fanout", &LibertyPort::setFanoutLimit, ports,
|
|
port_group, MinMax::max(), 1.0F);
|
|
readPortAttrFloatMinMax("min_fanout", &LibertyPort::setFanoutLimit, ports,
|
|
port_group, MinMax::min(), 1.0F);
|
|
readPulseClock(ports, port_group);
|
|
readPortAttrBool("clock_gate_clock_pin", &LibertyPort::setIsClockGateClock,
|
|
ports, port_group);
|
|
readPortAttrBool("clock_gate_enable_pin", &LibertyPort::setIsClockGateEnable,
|
|
ports, port_group);
|
|
readPortAttrBool("clock_gate_out_pin", &LibertyPort::setIsClockGateOut,
|
|
ports, port_group);
|
|
readPortAttrBool("is_pll_feedback_pin", &LibertyPort::setIsPllFeedback,
|
|
ports, port_group);
|
|
readSignalType(cell, ports, port_group);
|
|
readPortAttrBool("isolation_cell_data_pin",
|
|
&LibertyPort::setIsolationCellData, ports, port_group);
|
|
readPortAttrBool("isolation_cell_enable_pin",
|
|
&LibertyPort::setIsolationCellEnable, ports, port_group);
|
|
readPortAttrBool("level_shifter_data_pin",
|
|
&LibertyPort::setLevelShifterData, ports, port_group);
|
|
readPortAttrBool("switch_pin", &LibertyPort::setIsSwitch, ports, port_group);
|
|
readPortAttrString("related_ground_pin", &LibertyPort::setRelatedGroundPin,
|
|
ports, port_group);
|
|
readPortAttrString("related_power_pin", &LibertyPort::setRelatedPowerPin,
|
|
ports, port_group);
|
|
readDriverWaveform(ports, port_group);
|
|
}
|
|
|
|
void
|
|
LibertyReader::readDriverWaveform(const LibertyPortSeq &ports,
|
|
const LibertyGroup *port_group)
|
|
{
|
|
for (const RiseFall *rf : RiseFall::range()) {
|
|
const char *attr_name = rf == RiseFall::rise()
|
|
? "driver_waveform_rise" : "driver_waveform_fall";
|
|
const std::string *name = port_group->findAttrString(attr_name);
|
|
if (name) {
|
|
DriverWaveform *waveform = library_->findDriverWaveform(name->c_str());
|
|
if (waveform) {
|
|
for (LibertyPort *port : ports)
|
|
port->setDriverWaveform(waveform, rf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readPortAttrString(const char *attr_name,
|
|
void (LibertyPort::*set_func)(const char *value),
|
|
const LibertyPortSeq &ports,
|
|
const LibertyGroup *group)
|
|
{
|
|
const std::string *value = group->findAttrString(attr_name);
|
|
if (value) {
|
|
for (LibertyPort *port : ports)
|
|
(port->*set_func)(value->c_str());
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readPortAttrFloat(const char *attr_name,
|
|
void (LibertyPort::*set_func)(float value),
|
|
const LibertyPortSeq &ports,
|
|
const LibertyGroup *group,
|
|
float scale)
|
|
{
|
|
float value;
|
|
bool exists;
|
|
group->findAttrFloat(attr_name, value, exists);
|
|
if (exists) {
|
|
for (LibertyPort *port : ports)
|
|
(port->*set_func)(value * scale);
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readPortAttrBool(const char *attr_name,
|
|
void (LibertyPort::*set_func)(bool value),
|
|
const LibertyPortSeq &ports,
|
|
const LibertyGroup *group)
|
|
{
|
|
const LibertySimpleAttr *attr = group->findSimpleAttr(attr_name);
|
|
if (attr) {
|
|
const LibertyAttrValue &attr_value = attr->value();
|
|
if (attr_value.isString()) {
|
|
const std::string &value = attr_value.stringValue();
|
|
if (stringEqual(value.c_str(), "true")) {
|
|
for (LibertyPort *port : ports)
|
|
(port->*set_func)(true);
|
|
}
|
|
else if (stringEqual(value.c_str(), "false")) {
|
|
for (LibertyPort *port : ports)
|
|
(port->*set_func)(false);
|
|
}
|
|
else
|
|
libWarn(1238, attr, "%s attribute is not boolean.", attr_name);
|
|
}
|
|
else
|
|
libWarn(1239, attr, "%s attribute is not boolean.", attr_name);
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readPortAttrFloatMinMax(const char *attr_name,
|
|
void (LibertyPort::*set_func)(float value,
|
|
const MinMax *min_max),
|
|
const LibertyPortSeq &ports,
|
|
const LibertyGroup *group,
|
|
const MinMax *min_max,
|
|
float scale)
|
|
{
|
|
float value;
|
|
bool exists;
|
|
group->findAttrFloat(attr_name, value, exists);
|
|
if (exists) {
|
|
for (LibertyPort *port : ports)
|
|
(port->*set_func)(value * scale, min_max);
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readPulseClock(const LibertyPortSeq &ports,
|
|
const LibertyGroup *port_group)
|
|
{
|
|
const std::string *pulse_clk = port_group->findAttrString("pulse_clock");
|
|
if (pulse_clk) {
|
|
const RiseFall *trigger = nullptr;
|
|
const RiseFall *sense = nullptr;
|
|
if (*pulse_clk == "rise_triggered_high_pulse") {
|
|
trigger = RiseFall::rise();
|
|
sense = RiseFall::rise();
|
|
}
|
|
else if (*pulse_clk == "rise_triggered_low_pulse") {
|
|
trigger = RiseFall::rise();
|
|
sense = RiseFall::fall();
|
|
}
|
|
else if (*pulse_clk == "fall_triggered_high_pulse") {
|
|
trigger = RiseFall::fall();
|
|
sense = RiseFall::rise();
|
|
}
|
|
else if (*pulse_clk == "fall_triggered_low_pulse") {
|
|
trigger = RiseFall::fall();
|
|
sense = RiseFall::fall();
|
|
}
|
|
else
|
|
libWarn(1242, port_group, "pulse_latch unknown pulse type.");
|
|
if (trigger) {
|
|
for (LibertyPort *port : ports)
|
|
port->setPulseClk(trigger, sense);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readSignalType(LibertyCell *cell,
|
|
const LibertyPortSeq &ports,
|
|
const LibertyGroup *port_group)
|
|
{
|
|
if (!dynamic_cast<TestCell *>(cell))
|
|
return;
|
|
const std::string *type = port_group->findAttrString("signal_type");
|
|
if (!type)
|
|
return;
|
|
ScanSignalType signal_type = ScanSignalType::none;
|
|
if (*type == "test_scan_enable")
|
|
signal_type = ScanSignalType::enable;
|
|
else if (*type == "test_scan_enable_inverted")
|
|
signal_type = ScanSignalType::enable_inverted;
|
|
else if (*type == "test_scan_clock")
|
|
signal_type = ScanSignalType::clock;
|
|
else if (*type == "test_scan_clock_a")
|
|
signal_type = ScanSignalType::clock_a;
|
|
else if (*type == "test_scan_clock_b")
|
|
signal_type = ScanSignalType::clock_b;
|
|
else if (*type == "test_scan_in")
|
|
signal_type = ScanSignalType::input;
|
|
else if (*type == "test_scan_in_inverted")
|
|
signal_type = ScanSignalType::input_inverted;
|
|
else if (*type == "test_scan_out")
|
|
signal_type = ScanSignalType::output;
|
|
else if (*type == "test_scan_out_inverted")
|
|
signal_type = ScanSignalType::output_inverted;
|
|
else {
|
|
libWarn(1299, port_group, "unknown signal_type %s.", type->c_str());
|
|
return;
|
|
}
|
|
for (LibertyPort *port : ports)
|
|
port->setScanSignalType(signal_type);
|
|
}
|
|
|
|
void
|
|
LibertyReader::readPortDir(const LibertyPortSeq &ports,
|
|
const LibertyGroup *port_group)
|
|
{
|
|
const LibertySimpleAttr *dir_attr = port_group->findSimpleAttr("direction");
|
|
// Note missing direction attribute is not an error because a bus group
|
|
// can have pin groups for the bus bits that have direcitons.
|
|
if (dir_attr) {
|
|
const std::string *dir = dir_attr->stringValue();
|
|
if (dir) {
|
|
PortDirection *port_dir = PortDirection::unknown();
|
|
if (*dir == "input")
|
|
port_dir = PortDirection::input();
|
|
else if (*dir == "output")
|
|
port_dir = PortDirection::output();
|
|
else if (*dir == "inout")
|
|
port_dir = PortDirection::bidirect();
|
|
else if (*dir == "internal")
|
|
port_dir = PortDirection::internal();
|
|
else
|
|
libWarn(1240, dir_attr, "unknown port direction.");
|
|
for (LibertyPort *port : ports)
|
|
port->setDirection(port_dir);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readCapacitance(const LibertyPortSeq &ports,
|
|
const LibertyGroup *port_group)
|
|
{
|
|
// capacitance
|
|
readPortAttrFloat("capacitance", &LibertyPort::setCapacitance, ports,
|
|
port_group, cap_scale_);
|
|
|
|
for (LibertyPort *port : ports) {
|
|
// rise/fall_capacitance
|
|
for (const RiseFall *rf : RiseFall::range()) {
|
|
std::string attr_name = rf->to_string() + "_capacitance";
|
|
float cap;
|
|
bool exists;
|
|
port_group->findAttrFloat(attr_name, cap, exists);
|
|
if (exists) {
|
|
for (const MinMax *min_max : MinMax::range())
|
|
port->setCapacitance(rf, min_max, cap * cap_scale_);
|
|
}
|
|
|
|
// rise/fall_capacitance_range(min_cap, max_cap);
|
|
attr_name = rf->to_string() + "_capacitance_range";
|
|
const LibertyComplexAttrSeq &range_attrs = port_group->findComplexAttrs(attr_name);
|
|
if (!range_attrs.empty()) {
|
|
const LibertyComplexAttr *attr = range_attrs[0];
|
|
const LibertyAttrValueSeq &values = attr->values();
|
|
if (values.size() == 2) {
|
|
float cap_min = values[0]->floatValue();
|
|
float cap_max = values[1]->floatValue();
|
|
port->setCapacitance(rf, MinMax::min(), cap_min * cap_scale_);
|
|
port->setCapacitance(rf, MinMax::max(), cap_max * cap_scale_);
|
|
}
|
|
}
|
|
}
|
|
if (!(port->isBus() || port->isBundle()))
|
|
setPortCapDefault(port);
|
|
|
|
for (const MinMax *min_max : MinMax::range()) {
|
|
// min/max_capacitance
|
|
std::string attr_name = min_max->to_string() + "_capacitance";
|
|
float limit;
|
|
bool exists;
|
|
port_group->findAttrFloat(attr_name, limit, exists);
|
|
if (exists)
|
|
port->setCapacitanceLimit(limit * cap_scale_, min_max);
|
|
|
|
// min/max_transition
|
|
attr_name = min_max->to_string() + "_transition";
|
|
port_group->findAttrFloat(attr_name, limit, exists);
|
|
if (exists)
|
|
port->setSlewLimit(limit * time_scale_, min_max);
|
|
}
|
|
|
|
// Default capacitance.
|
|
if (port->isBus() || port->isBundle()) {
|
|
// Do not clobber member port capacitances by setting the capacitance
|
|
// on a bus or bundle.
|
|
LibertyPortMemberIterator member_iter(port);
|
|
while (member_iter.hasNext()) {
|
|
LibertyPort *member = member_iter.next();
|
|
setPortCapDefault(member);
|
|
}
|
|
}
|
|
else
|
|
setPortCapDefault(port);
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::setPortCapDefault(LibertyPort *port)
|
|
{
|
|
for (const MinMax *min_max : MinMax::range()) {
|
|
for (const RiseFall *rf : RiseFall::range()) {
|
|
float cap;
|
|
bool exists;
|
|
port->capacitance(rf, min_max, cap, exists);
|
|
if (!exists)
|
|
port->setCapacitance(rf, min_max, defaultCap(port));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readMinPulseWidth(LibertyCell *cell,
|
|
const LibertyPortSeq &ports,
|
|
const LibertyGroup *port_group)
|
|
{
|
|
for (LibertyPort *port : ports) {
|
|
TimingArcAttrsPtr timing_attrs = nullptr;
|
|
for (const RiseFall *rf : RiseFall::range()) {
|
|
const char *mpw_attr_name = rf == RiseFall::rise()
|
|
? "min_pulse_width_high"
|
|
: "min_pulse_width_low";
|
|
float mpw;
|
|
bool exists;
|
|
port_group->findAttrFloat(mpw_attr_name, mpw, exists);
|
|
if (exists) {
|
|
mpw *= time_scale_;
|
|
port->setMinPulseWidth(rf, mpw);
|
|
|
|
// Make timing arcs for the port min_pulse_width_low/high attributes.
|
|
// This is redundant but makes sdf annotation consistent.
|
|
if (timing_attrs == nullptr) {
|
|
timing_attrs = std::make_shared<TimingArcAttrs>();
|
|
timing_attrs->setTimingType(TimingType::min_pulse_width);
|
|
}
|
|
TimingModel *check_model =
|
|
makeScalarCheckModel(cell, mpw, ScaleFactorType::min_pulse_width, rf);
|
|
timing_attrs->setModel(rf, check_model);
|
|
}
|
|
}
|
|
if (timing_attrs)
|
|
builder_.makeTimingArcs(cell, port, port, nullptr, timing_attrs,
|
|
port_group->line());
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::makePortFuncs(LibertyCell *cell,
|
|
const LibertyPortSeq &ports,
|
|
const LibertyGroup *port_group)
|
|
{
|
|
const LibertySimpleAttr *func_attr = port_group->findSimpleAttr("function");
|
|
if (func_attr) {
|
|
const std::string *func = func_attr->stringValue();
|
|
if (func) {
|
|
FuncExpr *func_expr = parseFunc(func->c_str(), "function", cell, func_attr->line());
|
|
for (LibertyPort *port : ports) {
|
|
port->setFunction(func_expr);
|
|
if (func_expr->checkSize(port)) {
|
|
libWarn(1195, func_attr->line(),
|
|
"port %s function size does not match port size.",
|
|
port->name());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const LibertySimpleAttr *tri_attr = port_group->findSimpleAttr("three_state");
|
|
if (tri_attr) {
|
|
const std::string *tri_disable = tri_attr->stringValue();
|
|
if (tri_disable) {
|
|
FuncExpr *tri_disable_expr = parseFunc(tri_disable->c_str(),
|
|
"three_state", cell,
|
|
tri_attr->line());
|
|
FuncExpr *tri_enable_expr = tri_disable_expr->invert();
|
|
for (LibertyPort *port : ports) {
|
|
port->setTristateEnable(tri_enable_expr);
|
|
if (port->direction() == PortDirection::output())
|
|
port->setDirection(PortDirection::tristate());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::makeSequentials(LibertyCell *cell,
|
|
const LibertyGroup *cell_group)
|
|
{
|
|
makeSequentials(cell, cell_group, true, "ff", "clocked_on", "next_state");
|
|
makeSequentials(cell, cell_group, true, "ff_bank", "clocked_on", "next_state");
|
|
makeSequentials(cell, cell_group, false, "latch", "enable", "data_in");
|
|
makeSequentials(cell, cell_group, false, "latch_bank", "enable", "data_in");
|
|
|
|
const LibertyGroup *lut_group = cell_group->findSubgroup("lut");;
|
|
if (lut_group) {
|
|
LibertyPort *out_port = nullptr;
|
|
LibertyPort *out_port_inv = nullptr;
|
|
size_t size;
|
|
makeSeqPorts(cell, lut_group, out_port, out_port_inv, size);
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::makeSequentials(LibertyCell *cell,
|
|
const LibertyGroup *cell_group,
|
|
bool is_register,
|
|
const char *seq_group_name,
|
|
const char *clk_attr_name,
|
|
const char *data_attr_name)
|
|
{
|
|
for (const LibertyGroup *seq_group : cell_group->findSubgroups(seq_group_name)) {
|
|
LibertyPort *out_port = nullptr;
|
|
LibertyPort *out_port_inv = nullptr;
|
|
size_t size;
|
|
makeSeqPorts(cell, seq_group, out_port, out_port_inv, size);
|
|
FuncExpr *clk_expr = makeSeqFunc(cell, seq_group, clk_attr_name, size);
|
|
FuncExpr *data_expr = makeSeqFunc(cell, seq_group, data_attr_name, size);
|
|
FuncExpr *clr_expr = makeSeqFunc(cell, seq_group, "clear", size);
|
|
FuncExpr *preset_expr = makeSeqFunc(cell, seq_group, "preset", size);
|
|
|
|
LogicValue clr_preset_var1 = LogicValue::unknown;
|
|
const LibertySimpleAttr *var1 = seq_group->findSimpleAttr("clear_preset_var1");
|
|
if (var1)
|
|
clr_preset_var1 = getAttrLogicValue(var1);
|
|
|
|
LogicValue clr_preset_var2 = LogicValue::unknown;
|
|
const LibertySimpleAttr *var2 = seq_group->findSimpleAttr("clear_preset_var2");
|
|
if (var2)
|
|
clr_preset_var2 = getAttrLogicValue(var2);
|
|
|
|
cell->makeSequential(size, is_register, clk_expr, data_expr, clr_expr,
|
|
preset_expr, clr_preset_var1, clr_preset_var2,
|
|
out_port, out_port_inv);
|
|
}
|
|
}
|
|
|
|
FuncExpr *
|
|
LibertyReader::makeSeqFunc(LibertyCell *cell,
|
|
const LibertyGroup *seq_group,
|
|
const char *attr_name,
|
|
int size)
|
|
{
|
|
FuncExpr *expr = nullptr;
|
|
const std::string *attr = seq_group->findAttrString(attr_name);
|
|
if (attr) {
|
|
expr = parseFunc(attr->c_str(), attr_name, cell, seq_group->line());
|
|
if (expr && expr->checkSize(size)) {
|
|
libWarn(1196, seq_group, "%s %s bus width mismatch.",
|
|
seq_group->type().c_str(), attr_name);
|
|
delete expr;
|
|
expr = nullptr;
|
|
}
|
|
}
|
|
return expr;
|
|
}
|
|
|
|
void
|
|
LibertyReader::makeSeqPorts(LibertyCell *cell,
|
|
const LibertyGroup *seq_group,
|
|
// Return values.
|
|
LibertyPort *&out_port,
|
|
LibertyPort *&out_port_inv,
|
|
size_t &size)
|
|
{
|
|
const char *out_name, *out_inv_name;
|
|
bool has_size;
|
|
seqPortNames(seq_group, out_name, out_inv_name, has_size, size);
|
|
if (out_name) {
|
|
if (has_size)
|
|
out_port = makeBusPort(cell, out_name, size - 1, 0, nullptr);
|
|
else
|
|
out_port = makePort(cell, out_name);
|
|
out_port->setDirection(PortDirection::internal());
|
|
}
|
|
if (out_inv_name) {
|
|
if (has_size)
|
|
out_port_inv = makeBusPort(cell, out_inv_name, size - 1, 0, nullptr);
|
|
else
|
|
out_port_inv = makePort(cell, out_inv_name);
|
|
out_port_inv->setDirection(PortDirection::internal());
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::seqPortNames(const LibertyGroup *group,
|
|
const char *&out_name,
|
|
const char *&out_inv_name,
|
|
bool &has_size,
|
|
size_t &size)
|
|
{
|
|
out_name = nullptr;
|
|
out_inv_name = nullptr;
|
|
if (group->params().size() == 1) {
|
|
// out_port
|
|
out_name = group->firstName();
|
|
size = 1;
|
|
has_size = false;
|
|
}
|
|
if (group->params().size() == 2) {
|
|
// out_port, out_port_inv
|
|
out_name = group->firstName();
|
|
out_inv_name = group->secondName();
|
|
size = 1;
|
|
has_size = false;
|
|
}
|
|
else if (group->params().size() == 3) {
|
|
LibertyAttrValue *third_value = group->params()[2];
|
|
if (third_value->isFloat()) {
|
|
// out_port, out_port_inv, bus_size
|
|
out_name = group->firstName();
|
|
out_inv_name = group->secondName();
|
|
size = static_cast<int>(third_value->floatValue());
|
|
has_size = true;
|
|
}
|
|
else {
|
|
// in_port (ignored), out_port, out_port_inv
|
|
out_name = group->secondName();
|
|
out_inv_name = third_value->stringValue().c_str();
|
|
has_size = true;
|
|
size = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::readCellAttributes(LibertyCell *cell,
|
|
const LibertyGroup *cell_group)
|
|
{
|
|
readCellAttrFloat("area", &LibertyCell::setArea, cell, cell_group, 1.0);
|
|
readCellAttrString("cell_footprint", &LibertyCell::setFootprint, cell, cell_group);
|
|
readCellAttrBool("dont_use", &LibertyCell::setDontUse, cell, cell_group);
|
|
readCellAttrBool("is_macro_cell", &LibertyCell::setIsMacro, cell, cell_group);
|
|
readCellAttrBool("is_pad", &LibertyCell::setIsPad, cell, cell_group);
|
|
readCellAttrBool("is_level_shifter", &LibertyCell::setIsLevelShifter, cell, cell_group);
|
|
readCellAttrBool("is_clock_cell", &LibertyCell::setIsClockCell, cell, cell_group);
|
|
readCellAttrBool("is_isolation_cell", &LibertyCell::setIsIsolationCell,cell,cell_group);
|
|
readCellAttrBool("always_on", &LibertyCell::setAlwaysOn,cell,cell_group);
|
|
readCellAttrBool("interface_timing", &LibertyCell::setInterfaceTiming,cell,cell_group);
|
|
readCellAttrFloat("cell_leakage_power", &LibertyCell::setLeakagePower, cell,
|
|
cell_group, power_scale_);
|
|
|
|
readCellAttrBool("is_memory", &LibertyCell::setIsMemory, cell, cell_group);
|
|
if (cell_group->findSubgroup("memory"))
|
|
cell->setIsMemory(true);
|
|
|
|
readCellAttrBool("pad_cell", &LibertyCell::setIsPad, cell, cell_group);
|
|
readLevelShifterType(cell, cell_group);
|
|
readSwitchCellType(cell, cell_group);
|
|
readCellAttrString("user_function_class", &LibertyCell::setUserFunctionClass,
|
|
cell, cell_group);
|
|
|
|
readOcvDerateFactors(cell, cell_group);
|
|
readCellOcvDerateGroup(cell, cell_group);
|
|
readGroupAttrFloat("ocv_arc_depth", cell_group,
|
|
[cell](float v) { cell->setOcvArcDepth(v); });
|
|
|
|
const std::string *clock_gate_type =
|
|
cell_group->findAttrString("clock_gating_integrated_cell");
|
|
if (clock_gate_type) {
|
|
if (stringBeginEqual(clock_gate_type->c_str(), "latch_posedge"))
|
|
cell->setClockGateType(ClockGateType::latch_posedge);
|
|
else if (stringBeginEqual(clock_gate_type->c_str(), "latch_negedge"))
|
|
cell->setClockGateType(ClockGateType::latch_negedge);
|
|
else
|
|
cell->setClockGateType(ClockGateType::other);
|
|
}
|
|
|
|
readScaleFactors(cell, cell_group);
|
|
readLeagageGrouops(cell, cell_group);
|
|
readStatetable(cell, cell_group);
|
|
readModeDefs(cell, cell_group);
|
|
}
|
|
|
|
void
|
|
LibertyReader::readScaleFactors(LibertyCell *cell,
|
|
const LibertyGroup *cell_group)
|
|
{
|
|
const std::string *scale_factors_name = cell_group->findAttrString("scaling_factors");
|
|
if (scale_factors_name) {
|
|
ScaleFactors *scale_factors = library_->findScaleFactors(scale_factors_name->c_str());
|
|
if (scale_factors)
|
|
cell->setScaleFactors(scale_factors);
|
|
else
|
|
libWarn(1230, cell_group, "scaling_factors %s not found.",
|
|
scale_factors_name->c_str());
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readCellAttrString(const char *attr_name,
|
|
void (LibertyCell::*set_func)(const char *value),
|
|
LibertyCell *cell,
|
|
const LibertyGroup *group)
|
|
{
|
|
const std::string *value = group->findAttrString(attr_name);
|
|
if (value)
|
|
(cell->*set_func)(value->c_str());
|
|
}
|
|
|
|
void
|
|
LibertyReader::readCellAttrFloat(const char *attr_name,
|
|
void (LibertyCell::*set_func)(float value),
|
|
LibertyCell *cell,
|
|
const LibertyGroup *group,
|
|
float scale)
|
|
{
|
|
float value;
|
|
bool exists;
|
|
group->findAttrFloat(attr_name, value, exists);
|
|
if (exists)
|
|
(cell->*set_func)(value * scale);
|
|
}
|
|
|
|
void
|
|
LibertyReader::readCellAttrBool(const char *attr_name,
|
|
void (LibertyCell::*set_func)(bool value),
|
|
LibertyCell *cell,
|
|
const LibertyGroup *group)
|
|
{
|
|
const LibertySimpleAttr *attr = group->findSimpleAttr(attr_name);
|
|
if (attr) {
|
|
const LibertyAttrValue &attr_value = attr->value();
|
|
if (attr_value.isString()) {
|
|
const std::string &value = attr_value.stringValue();
|
|
if (stringEqual(value.c_str(), "true"))
|
|
(cell->*set_func)(true);
|
|
else if (stringEqual(value.c_str(), "false"))
|
|
(cell->*set_func)(false);
|
|
else
|
|
libWarn(1279, attr, "%s attribute is not boolean.", attr_name);
|
|
}
|
|
else
|
|
libWarn(1280, attr, "%s attribute is not boolean.", attr_name);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::makeTimingArcs(LibertyCell *cell,
|
|
const LibertyPortSeq &ports,
|
|
const LibertyGroup *port_group)
|
|
{
|
|
for (const LibertyGroup *timing_group : port_group->findSubgroups("timing")) {
|
|
TimingArcAttrsPtr timing_attrs = std::make_shared<TimingArcAttrs>();
|
|
readTimingArcAttrs(cell, timing_group, timing_attrs);
|
|
makeTimingModels(cell, timing_group, timing_attrs);
|
|
|
|
LibertyPort *related_output_port = findLibertyPort(cell, timing_group,
|
|
"related_output_pin");
|
|
StringSeq related_port_names = findAttributStrings(timing_group, "related_pin");
|
|
StringSeq related_bus_names=findAttributStrings(timing_group,"related_bus_pins");
|
|
TimingType timing_type = timing_attrs->timingType();
|
|
|
|
for (LibertyPort *to_port : ports) {
|
|
if (timing_type == TimingType::combinational &&
|
|
to_port->direction()->isInput())
|
|
libWarn(1209, timing_group, "combinational timing to an input port.");
|
|
|
|
if (related_port_names.size() || related_bus_names.size()) {
|
|
for (const std::string &from_port_name : related_port_names) {
|
|
debugPrint(debug_, "liberty", 2, " timing %s -> %s",
|
|
from_port_name.c_str(), to_port->name());
|
|
makeTimingArcs(cell, from_port_name, to_port, related_output_port, true,
|
|
timing_attrs, timing_group->line());
|
|
}
|
|
for (const std::string &from_port_name : related_bus_names) {
|
|
debugPrint(debug_, "liberty", 2, " timing %s -> %s",
|
|
from_port_name.c_str(), to_port->name());
|
|
makeTimingArcs(cell, from_port_name, to_port, related_output_port, false,
|
|
timing_attrs, timing_group->line());
|
|
}
|
|
}
|
|
else if (!(timing_type == TimingType::min_pulse_width
|
|
|| timing_type == TimingType::minimum_period
|
|
|| timing_type == TimingType::min_clock_tree_path
|
|
|| timing_type == TimingType::max_clock_tree_path))
|
|
libWarn(1243, timing_group, "timing group missing related_pin/related_bus_pin.");
|
|
else
|
|
makeTimingArcs(cell, to_port, related_output_port,
|
|
timing_attrs, timing_group->line());
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readTimingArcAttrs(LibertyCell *cell,
|
|
const LibertyGroup *timing_group,
|
|
TimingArcAttrsPtr timing_attrs)
|
|
{
|
|
readTimingSense(timing_group, timing_attrs);
|
|
readTimingType(timing_group, timing_attrs);
|
|
readTimingWhen(cell, timing_group, timing_attrs);
|
|
readTimingMode(timing_group, timing_attrs);
|
|
readGroupAttrFloat("ocv_arc_depth", timing_group,
|
|
[timing_attrs](float v) { timing_attrs->setOcvArcDepth(v); });
|
|
}
|
|
|
|
void
|
|
LibertyReader::readGroupAttrFloat(const char *attr_name,
|
|
const LibertyGroup *group,
|
|
const std::function<void(float)> &set_func,
|
|
float scale)
|
|
{
|
|
float value;
|
|
bool exists;
|
|
group->findAttrFloat(attr_name, value, exists);
|
|
if (exists)
|
|
set_func(value * scale);
|
|
}
|
|
|
|
void
|
|
LibertyReader::readTimingSense(const LibertyGroup *timing_group,
|
|
TimingArcAttrsPtr timing_attrs)
|
|
{
|
|
const LibertySimpleAttr *sense_attr = timing_group->findSimpleAttr("timing_sense");
|
|
if (sense_attr) {
|
|
const std::string *sense_name = sense_attr->stringValue();
|
|
if (sense_name) {
|
|
if (*sense_name == "non_unate")
|
|
timing_attrs->setTimingSense(TimingSense::non_unate);
|
|
else if (*sense_name == "positive_unate")
|
|
timing_attrs->setTimingSense(TimingSense::positive_unate);
|
|
else if (*sense_name == "negative_unate")
|
|
timing_attrs->setTimingSense(TimingSense::negative_unate);
|
|
else
|
|
libWarn(1245, timing_group, "unknown timing_sense %s.", sense_name->c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readTimingType(const LibertyGroup *timing_group,
|
|
TimingArcAttrsPtr timing_attrs)
|
|
{
|
|
TimingType type = TimingType::combinational;
|
|
const LibertySimpleAttr *type_attr = timing_group->findSimpleAttr("timing_type");
|
|
if (type_attr) {
|
|
const std::string *type_name = type_attr->stringValue();
|
|
if (type_name) {
|
|
type = findTimingType(type_name->c_str());
|
|
if (type == TimingType::unknown) {
|
|
libWarn(1244, type_attr, "unknown timing_type %s.", type_name->c_str());
|
|
type = TimingType::combinational;
|
|
}
|
|
}
|
|
}
|
|
timing_attrs->setTimingType(type);
|
|
}
|
|
|
|
void
|
|
LibertyReader::readTimingWhen(const LibertyCell *cell,
|
|
const LibertyGroup *timing_group,
|
|
TimingArcAttrsPtr timing_attrs)
|
|
{
|
|
const LibertySimpleAttr *when_attr = timing_group->findSimpleAttr("when");
|
|
if (when_attr) {
|
|
const std::string *when = when_attr->stringValue();
|
|
if (when) {
|
|
FuncExpr *when_expr = parseFunc(when->c_str(), "when", cell, when_attr->line());
|
|
timing_attrs->setCond(when_expr);
|
|
}
|
|
}
|
|
|
|
const LibertySimpleAttr *cond_attr = timing_group->findSimpleAttr("sdf_cond");
|
|
if (cond_attr) {
|
|
const std::string *cond = cond_attr->stringValue();
|
|
if (cond)
|
|
timing_attrs->setSdfCond(cond->c_str());
|
|
}
|
|
cond_attr = timing_group->findSimpleAttr("sdf_cond_start");
|
|
if (cond_attr) {
|
|
const std::string *cond = cond_attr->stringValue();
|
|
if (cond)
|
|
timing_attrs->setSdfCondStart(cond->c_str());
|
|
}
|
|
cond_attr = timing_group->findSimpleAttr("sdf_cond_end");
|
|
if (cond_attr) {
|
|
const std::string *cond = cond_attr->stringValue();
|
|
if (cond)
|
|
timing_attrs->setSdfCondEnd(cond->c_str());
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readTimingMode(const LibertyGroup *timing_group,
|
|
TimingArcAttrsPtr timing_attrs)
|
|
{
|
|
const LibertyComplexAttrSeq &mode_attrs = timing_group->findComplexAttrs("mode");
|
|
if (!mode_attrs.empty()) {
|
|
const LibertyComplexAttr *mode_attr = mode_attrs[0];
|
|
const LibertyAttrValueSeq &mode_values = mode_attr->values();
|
|
if (mode_values.size() == 2) {
|
|
LibertyAttrValue *value = mode_values[0];
|
|
if (value->isString())
|
|
timing_attrs->setModeName(value->stringValue());
|
|
else
|
|
libWarn(1248, mode_attr, "mode name is not a string.");
|
|
|
|
value = mode_values[1];
|
|
if (value->isString())
|
|
timing_attrs->setModeValue(value->stringValue());
|
|
else
|
|
libWarn(1246, mode_attr, "mode value is not a string.");
|
|
}
|
|
else
|
|
libWarn(1249, mode_attr, "mode requirees 2 values.");
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::makeTimingModels(LibertyCell *cell,
|
|
const LibertyGroup *timing_group,
|
|
TimingArcAttrsPtr timing_attrs)
|
|
{
|
|
switch (cell->libertyLibrary()->delayModelType()) {
|
|
case DelayModelType::cmos_linear:
|
|
makeLinearModels(cell, timing_group, timing_attrs);
|
|
break;
|
|
case DelayModelType::table:
|
|
makeTableModels(cell, timing_group, timing_attrs);
|
|
break;
|
|
case DelayModelType::cmos_pwl:
|
|
case DelayModelType::cmos2:
|
|
case DelayModelType::polynomial:
|
|
case DelayModelType::dcm:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::makeLinearModels(LibertyCell *cell,
|
|
const LibertyGroup *timing_group,
|
|
TimingArcAttrsPtr timing_attrs)
|
|
{
|
|
LibertyLibrary *library = cell->libertyLibrary();
|
|
for (const RiseFall *rf : RiseFall::range()) {
|
|
std::string intr_attr_name = "intrinsic_" + rf->to_string();
|
|
float intr = 0.0;
|
|
bool intr_exists;
|
|
timing_group->findAttrFloat(intr_attr_name, intr, intr_exists);
|
|
if (intr_exists)
|
|
intr *= time_scale_;
|
|
else
|
|
library->defaultIntrinsic(rf, intr, intr_exists);
|
|
TimingModel *model = nullptr;
|
|
if (intr_exists) {
|
|
if (timingTypeIsCheck(timing_attrs->timingType()))
|
|
model = new CheckLinearModel(cell, intr);
|
|
else {
|
|
std::string res_attr_name = rf->to_string() + "_resistance";
|
|
float res = 0.0;
|
|
bool res_exists;
|
|
timing_group->findAttrFloat(res_attr_name, res, res_exists);
|
|
if (res_exists)
|
|
res *= res_scale_;
|
|
else
|
|
library->defaultPinResistance(rf, PortDirection::output(),
|
|
res, res_exists);
|
|
model = new GateLinearModel(cell, intr, res);
|
|
}
|
|
timing_attrs->setModel(rf, model);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::makeTableModels(LibertyCell *cell,
|
|
const LibertyGroup *timing_group,
|
|
TimingArcAttrsPtr timing_attrs)
|
|
{
|
|
bool found_model = false;
|
|
for (const RiseFall *rf : RiseFall::range()) {
|
|
std::string delay_attr_name = "cell_" + rf->to_string();
|
|
TableModel *delay = readGateTableModel(timing_group, delay_attr_name.c_str(), rf,
|
|
TableTemplateType::delay, time_scale_,
|
|
ScaleFactorType::cell);
|
|
std::string transition_attr_name = rf->to_string() + "_transition";
|
|
TableModel *transition = readGateTableModel(timing_group,
|
|
transition_attr_name.c_str(),
|
|
rf, TableTemplateType::delay,
|
|
time_scale_,
|
|
ScaleFactorType::transition);
|
|
if (delay || transition) {
|
|
std::string delay_sigma_attr_name = "ocv_sigma_cell_" + rf->to_string();
|
|
TableModelsEarlyLate delay_sigmas =
|
|
readEarlyLateTableModels(timing_group,
|
|
delay_sigma_attr_name.c_str(),
|
|
rf, TableTemplateType::delay,
|
|
time_scale_,
|
|
ScaleFactorType::unknown);
|
|
|
|
std::string slew_sigma_attr_name = "ocv_sigma_" + rf->to_string()
|
|
+ "_transition";
|
|
TableModelsEarlyLate slew_sigmas =
|
|
readEarlyLateTableModels(timing_group,
|
|
slew_sigma_attr_name.c_str(),
|
|
rf, TableTemplateType::delay,
|
|
time_scale_,
|
|
ScaleFactorType::unknown);
|
|
|
|
ReceiverModelPtr receiver_model = readReceiverCapacitance(timing_group, rf);
|
|
OutputWaveforms *output_waveforms = readOutputWaveforms(timing_group, rf);
|
|
|
|
timing_attrs->setModel(rf, new GateTableModel(cell, delay,
|
|
std::move(delay_sigmas),
|
|
transition,
|
|
std::move(slew_sigmas),
|
|
receiver_model,
|
|
output_waveforms));
|
|
TimingType timing_type = timing_attrs->timingType();
|
|
if (isGateTimingType(timing_type)) {
|
|
if (transition == nullptr)
|
|
libWarn(1210, timing_group, "missing %s_transition.", rf->name());
|
|
if (delay == nullptr)
|
|
libWarn(1211, timing_group, "missing cell_%s.", rf->name());
|
|
}
|
|
found_model = true;
|
|
}
|
|
else {
|
|
std::string constraint_attr_name = rf->to_string() + "_constraint";
|
|
ScaleFactorType scale_factor_type =
|
|
timingTypeScaleFactorType(timing_attrs->timingType());
|
|
TableModel *constraint = readCheckTableModel(timing_group,
|
|
constraint_attr_name.c_str(),
|
|
rf, TableTemplateType::delay,
|
|
time_scale_, scale_factor_type);
|
|
if (constraint) {
|
|
std::string constraint_sigma_attr_name = "ocv_sigma_" + rf->to_string()
|
|
+ "_constraint";
|
|
TableModelsEarlyLate constraint_sigmas =
|
|
readEarlyLateTableModels(timing_group,
|
|
constraint_sigma_attr_name.c_str(),
|
|
rf, TableTemplateType::delay,
|
|
time_scale_,
|
|
ScaleFactorType::unknown);
|
|
timing_attrs->setModel(rf, new CheckTableModel(cell, constraint,
|
|
std::move(constraint_sigmas)));
|
|
found_model = true;
|
|
}
|
|
}
|
|
}
|
|
if (!found_model)
|
|
libWarn(1311, timing_group, "no table models found in timing group.");
|
|
}
|
|
|
|
|
|
bool
|
|
LibertyReader::isGateTimingType(TimingType timing_type)
|
|
{
|
|
return timing_type == TimingType::clear
|
|
|| timing_type == TimingType::combinational
|
|
|| timing_type == TimingType::combinational_fall
|
|
|| timing_type == TimingType::combinational_rise
|
|
|| timing_type == TimingType::falling_edge
|
|
|| timing_type == TimingType::preset
|
|
|| timing_type == TimingType::rising_edge
|
|
|| timing_type == TimingType::three_state_disable
|
|
|| timing_type == TimingType::three_state_disable_rise
|
|
|| timing_type == TimingType::three_state_disable_fall
|
|
|| timing_type == TimingType::three_state_enable
|
|
|| timing_type == TimingType::three_state_enable_fall
|
|
|| timing_type == TimingType::three_state_enable_rise;
|
|
}
|
|
|
|
TableModel *
|
|
LibertyReader::readGateTableModel(const LibertyGroup *timing_group,
|
|
const char *table_group_name,
|
|
const RiseFall *rf,
|
|
TableTemplateType template_type,
|
|
float scale,
|
|
ScaleFactorType scale_factor_type)
|
|
{
|
|
const LibertyGroup *table_group = timing_group->findSubgroup(table_group_name);
|
|
if (table_group) {
|
|
TableModel *model = readTableModel(table_group, rf, template_type, scale,
|
|
scale_factor_type);
|
|
if (model && !GateTableModel::checkAxes(model))
|
|
libWarn(1251, table_group, "unsupported model axis.");
|
|
return model;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
TableModel *
|
|
LibertyReader::readCheckTableModel(const LibertyGroup *timing_group,
|
|
const char *table_group_name,
|
|
const RiseFall *rf,
|
|
TableTemplateType template_type,
|
|
float scale,
|
|
ScaleFactorType scale_factor_type)
|
|
{
|
|
const LibertyGroup *table_group = timing_group->findSubgroup(table_group_name);
|
|
if (table_group) {
|
|
TableModel *model = readTableModel(table_group, rf, template_type, scale,
|
|
scale_factor_type);
|
|
if (model && !CheckTableModel::checkAxes(model))
|
|
libWarn(1252, table_group, "unsupported model axis.");
|
|
return model;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
TableModelsEarlyLate
|
|
LibertyReader::readEarlyLateTableModels(const LibertyGroup *timing_group,
|
|
const char *table_group_name,
|
|
const RiseFall *rf,
|
|
TableTemplateType template_type,
|
|
float scale,
|
|
ScaleFactorType scale_factor_type)
|
|
{
|
|
TableModelsEarlyLate models{};
|
|
for (const LibertyGroup *table_group : timing_group->findSubgroups(table_group_name)) {
|
|
TableModel *model = readTableModel(table_group, rf, template_type, scale,
|
|
scale_factor_type);
|
|
const std::string *early_late = table_group->findAttrString("sigma_type");
|
|
if (early_late == nullptr
|
|
|| *early_late == "early_and_late") {
|
|
models[EarlyLate::early()->index()] = model;
|
|
models[EarlyLate::late()->index()] = model;
|
|
}
|
|
else if (*early_late == "early")
|
|
models[EarlyLate::early()->index()] = model;
|
|
else if (*early_late == "late")
|
|
models[EarlyLate::late()->index()] = model;
|
|
|
|
//if (model && !GateTableModel::checkAxes(model))
|
|
// libWarn(1182, table_group, "unsupported model axis.");
|
|
}
|
|
return models;
|
|
}
|
|
|
|
ReceiverModelPtr
|
|
LibertyReader::readReceiverCapacitance(const LibertyGroup *timing_group,
|
|
const RiseFall *rf)
|
|
{
|
|
ReceiverModelPtr receiver_model = nullptr;
|
|
readReceiverCapacitance(timing_group, "receiver_capacitance", 0, rf,
|
|
receiver_model);
|
|
readReceiverCapacitance(timing_group, "receiver_capacitance1", 0, rf,
|
|
receiver_model);
|
|
readReceiverCapacitance(timing_group, "receiver_capacitance2", 1, rf,
|
|
receiver_model);
|
|
return receiver_model;
|
|
}
|
|
|
|
void
|
|
LibertyReader::readReceiverCapacitance(const LibertyGroup *timing_group,
|
|
const char *cap_group_name,
|
|
int index,
|
|
const RiseFall *rf,
|
|
ReceiverModelPtr &receiver_model)
|
|
{
|
|
std::string cap_group_name1 = cap_group_name;
|
|
cap_group_name1 += "_" + rf->to_string();
|
|
const LibertyGroup *cap_group = timing_group->findSubgroup(cap_group_name1);
|
|
if (cap_group) {
|
|
const LibertySimpleAttr *segment_attr = cap_group->findSimpleAttr("segment");
|
|
if (segment_attr) {
|
|
// For receiver_capacitance groups with mulitiple segments this
|
|
// overrides the index passed in beginReceiverCapacitance1Rise/Fall.
|
|
int segment;
|
|
bool exists;
|
|
getAttrInt(segment_attr, segment, exists);
|
|
if (exists)
|
|
index = segment;
|
|
}
|
|
TableModel *model = readTableModel(cap_group, rf, TableTemplateType::delay,
|
|
cap_scale_, ScaleFactorType::pin_cap);
|
|
if (ReceiverModel::checkAxes(model)) {
|
|
if (receiver_model == nullptr)
|
|
receiver_model = std::make_shared<ReceiverModel>();
|
|
receiver_model->setCapacitanceModel(std::move(*model), index, rf);
|
|
}
|
|
else
|
|
libWarn(1219, cap_group, "unsupported model axis.");
|
|
delete model;
|
|
}
|
|
}
|
|
|
|
OutputWaveforms *
|
|
LibertyReader::readOutputWaveforms(const LibertyGroup *timing_group,
|
|
const RiseFall *rf)
|
|
{
|
|
const std::string current_group_name = "output_current_" + rf->to_string();
|
|
const LibertyGroup *current_group = timing_group->findSubgroup(current_group_name);
|
|
if (current_group) {
|
|
OutputWaveformSeq output_currents;
|
|
for (const LibertyGroup *vector_group : current_group->findSubgroups("vector")) {
|
|
float ref_time;
|
|
bool ref_time_exists;
|
|
vector_group->findAttrFloat("reference_time", ref_time, ref_time_exists);
|
|
if (ref_time_exists) {
|
|
ref_time *= time_scale_;
|
|
TableModel *table = readTableModel(vector_group, rf,
|
|
TableTemplateType::output_current,
|
|
current_scale_, ScaleFactorType::unknown);
|
|
if (table) {
|
|
TableTemplate *tbl_template = table->tblTemplate();
|
|
const TableAxis *slew_axis, *cap_axis;
|
|
// Canonicalize axis order.
|
|
if (tbl_template->axis1()->variable()==TableAxisVariable::input_net_transition){
|
|
slew_axis = table->axis1();
|
|
cap_axis = table->axis2();
|
|
}
|
|
else {
|
|
slew_axis = table->axis2();
|
|
cap_axis = table->axis1();
|
|
}
|
|
|
|
if (slew_axis->size() == 1 && cap_axis->size() == 1) {
|
|
// Convert 1x1xN Table (order 3) to 1D Table.
|
|
float slew = slew_axis->axisValue(0);
|
|
float cap = cap_axis->axisValue(0);
|
|
TablePtr table_ptr = table->table();
|
|
FloatTable *values3 = table_ptr->values3();
|
|
FloatSeq row = std::move((*values3)[0]);
|
|
values3->erase(values3->begin());
|
|
Table *table1 = new Table(std::move(row), table->table()->axis3ptr());
|
|
output_currents.emplace_back(slew, cap, table1, ref_time);
|
|
}
|
|
else
|
|
libWarn(1223, vector_group,
|
|
"vector index_1 and index_2 must have exactly one value.");
|
|
}
|
|
delete table;
|
|
}
|
|
else
|
|
libWarn(1224, vector_group, "vector reference_time not found.");
|
|
}
|
|
if (!output_currents.empty())
|
|
return makeOutputWaveforms(current_group, output_currents, rf);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
OutputWaveforms *
|
|
LibertyReader::makeOutputWaveforms(const LibertyGroup *current_group,
|
|
OutputWaveformSeq &output_currents,
|
|
const RiseFall *rf)
|
|
{
|
|
std::set<float> slew_set, cap_set;
|
|
FloatSeq slew_values;
|
|
FloatSeq cap_values;
|
|
for (const OutputWaveform &waveform : output_currents) {
|
|
float slew = waveform.slew();
|
|
// Filter duplilcate slews and capacitances.
|
|
if (!slew_set.contains(slew)) {
|
|
slew_set.insert(slew);
|
|
slew_values.push_back(slew);
|
|
}
|
|
float cap = waveform.cap();
|
|
if (!cap_set.contains(cap)) {
|
|
cap_set.insert(cap);
|
|
cap_values.push_back(cap);
|
|
}
|
|
}
|
|
sort(slew_values, std::less<float>());
|
|
sort(cap_values, std::less<float>());
|
|
size_t slew_size = slew_values.size();
|
|
size_t cap_size = cap_values.size();
|
|
TableAxisPtr slew_axis =
|
|
make_shared<TableAxis>(TableAxisVariable::input_net_transition,
|
|
std::move(slew_values));
|
|
TableAxisPtr cap_axis =
|
|
make_shared<TableAxis>(TableAxisVariable::total_output_net_capacitance,
|
|
std::move(cap_values));
|
|
FloatSeq ref_times(slew_size);
|
|
Table1Seq current_waveforms(slew_size * cap_size);
|
|
for (OutputWaveform &waveform : output_currents) {
|
|
size_t slew_index, cap_index;
|
|
bool slew_exists, cap_exists;
|
|
slew_axis->findAxisIndex(waveform.slew(), slew_index, slew_exists);
|
|
cap_axis->findAxisIndex(waveform.cap(), cap_index, cap_exists);
|
|
if (slew_exists && cap_exists) {
|
|
size_t index = slew_index * cap_axis->size() + cap_index;
|
|
current_waveforms[index] = waveform.releaseCurrents();
|
|
ref_times[slew_index] = waveform.referenceTime();
|
|
}
|
|
else
|
|
libWarn(1221, current_group, "output current waveform %.2e %.2e not found.",
|
|
waveform.slew(),
|
|
waveform.cap());
|
|
}
|
|
Table ref_time_tbl(std::move(ref_times), slew_axis);
|
|
OutputWaveforms *output_current = new OutputWaveforms(slew_axis, cap_axis, rf,
|
|
current_waveforms,
|
|
std::move(ref_time_tbl));
|
|
return output_current;
|
|
}
|
|
|
|
TableModel *
|
|
LibertyReader::readTableModel(const LibertyGroup *table_group,
|
|
const RiseFall *rf,
|
|
TableTemplateType template_type,
|
|
float scale,
|
|
ScaleFactorType scale_factor_type)
|
|
{
|
|
const char *template_name = table_group->firstName();
|
|
if (library_ && template_name) {
|
|
TableTemplate *tbl_template = library_->findTableTemplate(template_name,
|
|
template_type);
|
|
if (tbl_template) {
|
|
TablePtr table = readTableModel(table_group, tbl_template, scale);
|
|
if (table) {
|
|
TableModel *table_model = new TableModel(table, tbl_template,
|
|
scale_factor_type, rf);
|
|
return table_model;
|
|
}
|
|
}
|
|
else
|
|
libWarn(1253, table_group, "table template %s not found.", template_name);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
TablePtr
|
|
LibertyReader::readTableModel(const LibertyGroup *table_group,
|
|
const TableTemplate *tbl_template,
|
|
float scale)
|
|
{
|
|
const LibertyComplexAttr *values_attr = table_group->findComplexAttr("values");
|
|
if (values_attr) {
|
|
TableAxisPtr axis1 = makeTableAxis(table_group, "index_1", tbl_template->axis1ptr());
|
|
TableAxisPtr axis2 = makeTableAxis(table_group, "index_2", tbl_template->axis2ptr());
|
|
TableAxisPtr axis3 = makeTableAxis(table_group, "index_3", tbl_template->axis3ptr());
|
|
if (axis1 && axis2 && axis3) {
|
|
// 3D table
|
|
FloatTable float_table = makeFloatTable(values_attr, table_group,
|
|
axis1->size() * axis2->size(),
|
|
axis3->size(), scale);
|
|
return make_shared<Table>(std::move(float_table), axis1, axis2, axis3);
|
|
}
|
|
else if (axis1 && axis2) {
|
|
FloatTable float_table = makeFloatTable(values_attr, table_group,
|
|
axis1->size(), axis2->size(), scale);
|
|
return make_shared<Table>(std::move(float_table), axis1, axis2);
|
|
}
|
|
else if (axis1) {
|
|
FloatTable table = makeFloatTable(values_attr, table_group, 1,
|
|
axis1->size(), scale);
|
|
return make_shared<Table>(std::move(table[0]), axis1);
|
|
}
|
|
else if (axis1 == nullptr && axis2 == nullptr && axis3 == nullptr) {
|
|
FloatTable table = makeFloatTable(values_attr, table_group, 1, 1, scale);
|
|
float value = table[0][0];
|
|
return std::make_shared<Table>(value);
|
|
}
|
|
}
|
|
else
|
|
libWarn(1257, table_group, "%s is missing values.", table_group->type().c_str());
|
|
return nullptr;
|
|
}
|
|
|
|
TableAxisPtr
|
|
LibertyReader::makeTableAxis(const LibertyGroup *table_group,
|
|
const char *index_attr_name,
|
|
TableAxisPtr template_axis)
|
|
{
|
|
const LibertyComplexAttr *index_attr = table_group->findComplexAttr(index_attr_name);
|
|
if (index_attr) {
|
|
FloatSeq axis_values = readFloatSeq(index_attr, 1.0F);
|
|
if (axis_values.empty())
|
|
libWarn(1177, index_attr, "missing table index values.");
|
|
else {
|
|
// Check monotonicity of the values.
|
|
float prev = axis_values[0];
|
|
for (size_t i = 1; i < axis_values.size(); i++) {
|
|
float value = axis_values[i];
|
|
if (value <= prev)
|
|
libWarn(1173, index_attr, "non-increasing table index values.");
|
|
prev = value;
|
|
}
|
|
|
|
TableAxisVariable axis_var = template_axis->variable();
|
|
const Units *units = library_->units();
|
|
float scale = tableVariableUnit(axis_var, units)->scale();
|
|
scaleFloats(axis_values, scale);
|
|
return make_shared<TableAxis>(axis_var, std::move(axis_values));
|
|
}
|
|
}
|
|
return template_axis;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::makeTimingArcs(LibertyCell *cell,
|
|
const std::string &from_port_name,
|
|
LibertyPort *to_port,
|
|
LibertyPort *related_out_port,
|
|
bool one_to_one,
|
|
TimingArcAttrsPtr timing_attrs,
|
|
int timing_line)
|
|
{
|
|
PortNameBitIterator from_port_iter(cell, from_port_name.c_str(), this, timing_line);
|
|
if (from_port_iter.size() == 1 && !to_port->hasMembers()) {
|
|
// one -> one
|
|
if (from_port_iter.hasNext()) {
|
|
LibertyPort *from_port = from_port_iter.next();
|
|
if (from_port->direction()->isOutput())
|
|
libWarn(1212, timing_line, "timing group from output port.");
|
|
builder_.makeTimingArcs(cell, from_port, to_port, related_out_port,
|
|
timing_attrs, timing_line);
|
|
}
|
|
}
|
|
else if (from_port_iter.size() > 1 && !to_port->hasMembers()) {
|
|
// bus -> one
|
|
while (from_port_iter.hasNext()) {
|
|
LibertyPort *from_port = from_port_iter.next();
|
|
if (from_port->direction()->isOutput())
|
|
libWarn(1213, timing_line, "timing group from output port.");
|
|
builder_.makeTimingArcs(cell, from_port, to_port, related_out_port,
|
|
timing_attrs, timing_line);
|
|
}
|
|
}
|
|
else if (from_port_iter.size() == 1 && to_port->hasMembers()) {
|
|
// one -> bus
|
|
if (from_port_iter.hasNext()) {
|
|
LibertyPort *from_port = from_port_iter.next();
|
|
if (from_port->direction()->isOutput())
|
|
libWarn(1214, timing_line, "timing group from output port.");
|
|
LibertyPortMemberIterator bit_iter(to_port);
|
|
while (bit_iter.hasNext()) {
|
|
LibertyPort *to_port_bit = bit_iter.next();
|
|
builder_.makeTimingArcs(cell, from_port, to_port_bit, related_out_port,
|
|
timing_attrs, timing_line);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// bus -> bus
|
|
if (one_to_one) {
|
|
int from_size = from_port_iter.size();
|
|
int to_size = to_port->size();
|
|
LibertyPortMemberIterator to_port_iter(to_port);
|
|
// warn about different sizes
|
|
if (from_size != to_size)
|
|
libWarn(1216, timing_line,
|
|
"timing port %s and related port %s are different sizes.",
|
|
from_port_name.c_str(),
|
|
to_port->name());
|
|
// align to/from iterators for one-to-one mapping
|
|
while (from_size > to_size) {
|
|
from_size--;
|
|
from_port_iter.next();
|
|
}
|
|
while (to_size > from_size) {
|
|
to_size--;
|
|
to_port_iter.next();
|
|
}
|
|
// make timing arcs
|
|
while (from_port_iter.hasNext() && to_port_iter.hasNext()) {
|
|
LibertyPort *from_port_bit = from_port_iter.next();
|
|
LibertyPort *to_port_bit = to_port_iter.next();
|
|
if (from_port_bit->direction()->isOutput())
|
|
libWarn(1215, timing_line, "timing group from output port.");
|
|
builder_.makeTimingArcs(cell, from_port_bit, to_port_bit,
|
|
related_out_port, timing_attrs,
|
|
timing_line);
|
|
}
|
|
}
|
|
else {
|
|
// cross product
|
|
while (from_port_iter.hasNext()) {
|
|
LibertyPort *from_port_bit = from_port_iter.next();
|
|
LibertyPortMemberIterator to_port_iter(to_port);
|
|
while (to_port_iter.hasNext()) {
|
|
LibertyPort *to_port_bit = to_port_iter.next();
|
|
builder_.makeTimingArcs(cell, from_port_bit, to_port_bit,
|
|
related_out_port, timing_attrs,
|
|
timing_line);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::makeTimingArcs(LibertyCell *cell,
|
|
LibertyPort *to_port,
|
|
LibertyPort *related_out_port,
|
|
TimingArcAttrsPtr timing_attrs,
|
|
int timing_line)
|
|
{
|
|
if (to_port->hasMembers()) {
|
|
LibertyPortMemberIterator bit_iter(to_port);
|
|
while (bit_iter.hasNext()) {
|
|
LibertyPort *to_port_bit = bit_iter.next();
|
|
builder_.makeTimingArcs(cell, nullptr, to_port_bit,
|
|
related_out_port, timing_attrs,
|
|
timing_line);
|
|
}
|
|
}
|
|
else
|
|
builder_.makeTimingArcs(cell, nullptr, to_port,
|
|
related_out_port, timing_attrs,
|
|
timing_line);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::readLeagageGrouops(LibertyCell *cell,
|
|
const LibertyGroup *cell_group)
|
|
{
|
|
for (const LibertyGroup *leak_group : cell_group->findSubgroups("leakage_power")) {
|
|
FuncExpr *when = readFuncExpr(cell, leak_group, "when");
|
|
float power;
|
|
bool exists;
|
|
leak_group->findAttrFloat("value", power, exists);
|
|
if (exists) {
|
|
LibertyPort *related_pg_port = findLibertyPort(cell, leak_group, "related_pg_pin");
|
|
cell->makeLeakagePower(related_pg_port, when, power * power_scale_);
|
|
}
|
|
else
|
|
libWarn(1307, leak_group, "leakage_power missing value.");
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readInternalPowerGroups(LibertyCell *cell,
|
|
const LibertyPortSeq &ports,
|
|
const LibertyGroup *port_group)
|
|
{
|
|
for (LibertyPort *port : ports) {
|
|
for (const LibertyGroup *ipwr_group : port_group->findSubgroups("internal_power")) {
|
|
LibertyPortSeq related_ports = findLibertyPorts(cell, ipwr_group, "related_pin");
|
|
LibertyPort *related_pg_port = findLibertyPort(cell, ipwr_group, "related_pg_pin");
|
|
std::shared_ptr<FuncExpr> when;
|
|
FuncExpr *when1 = readFuncExpr(cell, ipwr_group, "when");
|
|
if (when1)
|
|
when = std::shared_ptr<FuncExpr>(when1);
|
|
InternalPowerModels models;
|
|
// rise/fall_power group
|
|
for (const RiseFall *rf : RiseFall::range()) {
|
|
std::string pwr_attr_name = rf->to_string() + "_power";
|
|
const LibertyGroup *pwr_group = ipwr_group->findSubgroup(pwr_attr_name);
|
|
if (pwr_group) {
|
|
TableModel *model = readTableModel(pwr_group, rf, TableTemplateType::power,
|
|
energyScale(),
|
|
ScaleFactorType::internal_power);
|
|
models[rf->index()] = std::make_shared<InternalPowerModel>(model);
|
|
}
|
|
}
|
|
// power group (rise/fall power are the same)
|
|
const LibertyGroup *pwr_group = ipwr_group->findSubgroup("power");
|
|
if (pwr_group) {
|
|
TableModel *model = readTableModel(pwr_group, RiseFall::rise(),
|
|
TableTemplateType::power,
|
|
energyScale(),
|
|
ScaleFactorType::internal_power);
|
|
auto pwr_model = std::make_shared<InternalPowerModel>(model);
|
|
for (const RiseFall *rf : RiseFall::range())
|
|
models[rf->index()] = pwr_model;
|
|
}
|
|
if (related_ports.empty())
|
|
cell->makeInternalPower(port, nullptr, related_pg_port, when, models);
|
|
else {
|
|
for (LibertyPort *related_port : related_ports)
|
|
cell->makeInternalPower(port, related_port, related_pg_port, when, models);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
FuncExpr *
|
|
LibertyReader::readFuncExpr(LibertyCell *cell,
|
|
const LibertyGroup *group,
|
|
const char *attr_name)
|
|
{
|
|
const std::string *attr = group->findAttrString(attr_name);
|
|
if (attr)
|
|
return parseFunc(attr->c_str(), attr_name, cell, group->line());
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
LibertyPort *
|
|
LibertyReader::findLibertyPort(LibertyCell *cell,
|
|
const LibertyGroup *group,
|
|
const char *port_name_attr)
|
|
{
|
|
const LibertySimpleAttr *attr = group->findSimpleAttr(port_name_attr);
|
|
if (attr) {
|
|
const std::string *port_name = attr->stringValue();
|
|
if (port_name) {
|
|
LibertyPort *port = cell->findLibertyPort(port_name->c_str());
|
|
if (port)
|
|
return port;
|
|
else
|
|
libWarn(1290, attr, "port %s not found.", port_name->c_str());
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
StringSeq
|
|
LibertyReader::findAttributStrings(const LibertyGroup *group,
|
|
const char *name_attr)
|
|
{
|
|
const LibertySimpleAttr *attr = group->findSimpleAttr(name_attr);
|
|
if (attr) {
|
|
const std::string *strings = attr->stringValue();
|
|
if (strings) {
|
|
return parseTokens(*strings, ' ');
|
|
}
|
|
}
|
|
return StringSeq();
|
|
}
|
|
|
|
LibertyPortSeq
|
|
LibertyReader::findLibertyPorts(LibertyCell *cell,
|
|
const LibertyGroup *group,
|
|
const char *port_name_attr)
|
|
{
|
|
LibertyPortSeq ports;
|
|
StringSeq port_names = findAttributStrings(group, port_name_attr);
|
|
for (const std::string &port_name : port_names) {
|
|
LibertyPort *port = findPort(cell, port_name.c_str());
|
|
if (port)
|
|
ports.push_back(port);
|
|
else
|
|
libWarn(1306, group, "port %s not found.", port_name.c_str());
|
|
}
|
|
return ports;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
TimingModel *
|
|
LibertyReader::makeScalarCheckModel(LibertyCell *cell,
|
|
float value,
|
|
ScaleFactorType scale_factor_type,
|
|
const RiseFall *rf)
|
|
{
|
|
TablePtr table = std::make_shared<Table>(value);
|
|
TableTemplate *tbl_template =
|
|
library_->findTableTemplate("scalar", TableTemplateType::delay);
|
|
TableModel *table_model = new TableModel(table, tbl_template,
|
|
scale_factor_type, rf);
|
|
TableModelsEarlyLate sigmas{};
|
|
CheckTableModel *check_model = new CheckTableModel(cell, table_model,
|
|
std::move(sigmas));
|
|
return check_model;
|
|
}
|
|
|
|
void
|
|
LibertyReader::checkLatchEnableSense(FuncExpr *enable_func,
|
|
int line)
|
|
{
|
|
LibertyPortSet enable_ports = enable_func->ports();
|
|
for (LibertyPort *enable_port : enable_ports) {
|
|
TimingSense enable_sense = enable_func->portTimingSense(enable_port);
|
|
switch (enable_sense) {
|
|
case TimingSense::positive_unate:
|
|
case TimingSense::negative_unate:
|
|
break;
|
|
case TimingSense::non_unate:
|
|
libWarn(1200, line, "latch enable function is non-unate for port %s.",
|
|
enable_port->name());
|
|
break;
|
|
case TimingSense::none:
|
|
case TimingSense::unknown:
|
|
libWarn(1201, line, "latch enable function is unknown for port %s.",
|
|
enable_port->name());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::readNormalizedDriverWaveform(const LibertyGroup *library_group)
|
|
{
|
|
for (const LibertyGroup *waveform_group :
|
|
library_group->findSubgroups("normalized_driver_waveform")) {
|
|
const char *template_name = waveform_group->firstName();
|
|
if (template_name) {
|
|
TableTemplate *tbl_template = library_->findTableTemplate(template_name,
|
|
TableTemplateType::delay);
|
|
if (!tbl_template) {
|
|
libWarn(1256, waveform_group, "table template %s not found.", template_name);
|
|
continue;
|
|
}
|
|
TablePtr table = readTableModel(waveform_group, tbl_template, time_scale_);
|
|
if (!table)
|
|
continue;
|
|
if (table->axis1()->variable() != TableAxisVariable::input_net_transition) {
|
|
libWarn(1265, waveform_group,
|
|
"normalized_driver_waveform variable_1 must be input_net_transition");
|
|
continue;
|
|
}
|
|
if (table->axis2()->variable() != TableAxisVariable::normalized_voltage) {
|
|
libWarn(1225, waveform_group,
|
|
"normalized_driver_waveform variable_2 must be normalized_voltage");
|
|
continue;
|
|
}
|
|
std::string driver_waveform_name;
|
|
const std::string *name_attr = waveform_group->findAttrString("driver_waveform_name");
|
|
if (name_attr)
|
|
driver_waveform_name = *name_attr;
|
|
library_->makeDriverWaveform(driver_waveform_name, table);
|
|
}
|
|
else
|
|
libWarn(1227, waveform_group, "normalized_driver_waveform missing template.");
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::readLevelShifterType(LibertyCell *cell,
|
|
const LibertyGroup *cell_group)
|
|
{
|
|
const std::string *level_shifter_type = cell_group->findAttrString("level_shifter_type");
|
|
if (level_shifter_type) {
|
|
if (*level_shifter_type == "HL")
|
|
cell->setLevelShifterType(LevelShifterType::HL);
|
|
else if (*level_shifter_type == "LH")
|
|
cell->setLevelShifterType(LevelShifterType::LH);
|
|
else if (*level_shifter_type == "HL_LH")
|
|
cell->setLevelShifterType(LevelShifterType::HL_LH);
|
|
else
|
|
libWarn(1228, cell_group, "level_shifter_type must be HL, LH, or HL_LH");
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readSwitchCellType(LibertyCell *cell,
|
|
const LibertyGroup *cell_group)
|
|
{
|
|
const std::string *switch_cell_type = cell_group->findAttrString("switch_cell_type");
|
|
if (switch_cell_type) {
|
|
if (*switch_cell_type == "coarse_grain")
|
|
cell->setSwitchCellType(SwitchCellType::coarse_grain);
|
|
else if (*switch_cell_type == "fine_grain")
|
|
cell->setSwitchCellType(SwitchCellType::fine_grain);
|
|
else
|
|
libWarn(1229, cell_group, "switch_cell_type must be coarse_grain or fine_grain");
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readCellOcvDerateGroup(LibertyCell *cell,
|
|
const LibertyGroup *cell_group)
|
|
{
|
|
const std::string *derate_name = cell_group->findAttrString("ocv_derate_group");
|
|
if (derate_name) {
|
|
OcvDerate *derate = cell->findOcvDerate(derate_name->c_str());
|
|
if (derate == nullptr)
|
|
derate = library_->findOcvDerate(derate_name->c_str());
|
|
if (derate)
|
|
cell->setOcvDerate(derate);
|
|
else
|
|
libWarn(1237, cell_group, "OCV derate group named %s not found.",
|
|
derate_name->c_str());
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readStatetable(LibertyCell *cell,
|
|
const LibertyGroup *cell_group)
|
|
{
|
|
for (const LibertyGroup *statetable_group : cell_group->findSubgroups("statetable")) {
|
|
const char *input_ports_arg = statetable_group->firstName();
|
|
const char *internal_ports_arg = statetable_group->params().size() >= 2
|
|
? statetable_group->secondName() : nullptr;
|
|
StringSeq input_ports;
|
|
if (input_ports_arg)
|
|
input_ports = parseTokens(input_ports_arg, ' ');
|
|
StringSeq internal_ports;
|
|
if (internal_ports_arg)
|
|
internal_ports = parseTokens(internal_ports_arg, ' ');
|
|
|
|
const LibertySimpleAttr *table_attr = statetable_group->findSimpleAttr("table");
|
|
if (table_attr) {
|
|
const std::string *table_str = table_attr->stringValue();
|
|
StringSeq table_rows = parseTokens(table_str->c_str(), ',');
|
|
size_t input_count = input_ports.size();
|
|
size_t internal_count = internal_ports.size();
|
|
StatetableRows table;
|
|
for (const std::string &row : table_rows) {
|
|
const StringSeq row_groups = parseTokens(row, ':');
|
|
if (row_groups.size() != 3) {
|
|
libWarn(1300, table_attr, "table row must have 3 groups separated by ':'.");
|
|
break;
|
|
}
|
|
StringSeq inputs = parseTokens(row_groups[0], ' ');
|
|
if (inputs.size() != input_count) {
|
|
libWarn(1301,table_attr,"table row has %zu input values but %zu are required.",
|
|
inputs.size(), input_count);
|
|
break;
|
|
}
|
|
StringSeq currents = parseTokens(row_groups[1], ' ');
|
|
if (currents.size() != internal_count) {
|
|
libWarn(1302,table_attr,
|
|
"table row has %zu current values but %zu are required.",
|
|
currents.size(), internal_count);
|
|
break;
|
|
}
|
|
StringSeq nexts = parseTokens(row_groups[2], ' ');
|
|
if (nexts.size() != internal_count) {
|
|
libWarn(1303, table_attr, "table row has %zu next values but %zu are required.",
|
|
nexts.size(), internal_count);
|
|
break;
|
|
}
|
|
|
|
StateInputValues input_values = parseStateInputValues(inputs, table_attr);
|
|
StateInternalValues current_values=parseStateInternalValues(currents,table_attr);
|
|
StateInternalValues next_values = parseStateInternalValues(nexts, table_attr);
|
|
table.emplace_back(input_values, current_values, next_values);
|
|
}
|
|
|
|
LibertyPortSeq input_port_ptrs;
|
|
for (const std::string &input : input_ports) {
|
|
LibertyPort *port = cell->findLibertyPort(input.c_str());
|
|
if (port)
|
|
input_port_ptrs.push_back(port);
|
|
else
|
|
libWarn(1298, statetable_group, "statetable input port %s not found.",
|
|
input.c_str());
|
|
}
|
|
LibertyPortSeq internal_port_ptrs;
|
|
for (const std::string &internal : internal_ports) {
|
|
LibertyPort *port = cell->findLibertyPort(internal.c_str());
|
|
if (port == nullptr)
|
|
port = makePort(cell, internal.c_str());
|
|
internal_port_ptrs.push_back(port);
|
|
}
|
|
cell->makeStatetable(input_port_ptrs, internal_port_ptrs, table);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyReader::readTestCell(LibertyCell *cell,
|
|
const LibertyGroup *cell_group)
|
|
{
|
|
const LibertyGroup *test_cell_group = cell_group->findSubgroup("test_cell");
|
|
if (test_cell_group) {
|
|
if (cell->testCell())
|
|
libWarn(1262, test_cell_group, "cell %s test_cell redefinition.", cell->name());
|
|
else {
|
|
std::string test_cell_name = std::string(cell->name()) + "/test_cell";
|
|
TestCell *test_cell = new TestCell(cell->libertyLibrary(),
|
|
std::move(test_cell_name),
|
|
cell->filename());
|
|
cell->setTestCell(test_cell);
|
|
readCell(test_cell, test_cell_group);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
LibertyPort *
|
|
LibertyReader::makePort(LibertyCell *cell,
|
|
const char *port_name)
|
|
{
|
|
std::string sta_name = portLibertyToSta(port_name);
|
|
return builder_.makePort(cell, sta_name.c_str());
|
|
}
|
|
|
|
LibertyPort *
|
|
LibertyReader::makeBusPort(LibertyCell *cell,
|
|
const char *bus_name,
|
|
int from_index,
|
|
int to_index,
|
|
BusDcl *bus_dcl)
|
|
{
|
|
std::string sta_name = portLibertyToSta(bus_name);
|
|
return builder_.makeBusPort(cell, bus_name, from_index, to_index, bus_dcl);
|
|
}
|
|
|
|
// Also used by LibExprParser::makeFuncExprPort.
|
|
LibertyPort *
|
|
libertyReaderFindPort(const LibertyCell *cell,
|
|
const char *port_name)
|
|
{
|
|
LibertyPort *port = cell->findLibertyPort(port_name);
|
|
if (port == nullptr) {
|
|
const LibertyLibrary *library = cell->libertyLibrary();
|
|
char brkt_left = library->busBrktLeft();
|
|
char brkt_right = library->busBrktRight();
|
|
const char escape = '\\';
|
|
// Pins at top level with bus names have escaped brackets.
|
|
std::string escaped_port_name = escapeChars(port_name, brkt_left, brkt_right, escape);
|
|
port = cell->findLibertyPort(escaped_port_name.c_str());
|
|
}
|
|
return port;
|
|
}
|
|
|
|
LibertyPort *
|
|
LibertyReader::findPort(LibertyCell *cell,
|
|
const char *port_name)
|
|
{
|
|
return libertyReaderFindPort(cell, port_name);
|
|
}
|
|
|
|
float
|
|
LibertyReader::defaultCap(LibertyPort *port)
|
|
{
|
|
PortDirection *dir = port->direction();
|
|
float cap = 0.0;
|
|
if (dir->isInput())
|
|
cap = library_->defaultInputPinCap();
|
|
else if (dir->isOutput()
|
|
|| dir->isTristate())
|
|
cap = library_->defaultOutputPinCap();
|
|
else if (dir->isBidirect())
|
|
cap = library_->defaultBidirectPinCap();
|
|
return cap;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
static EnumNameMap<StateInputValue> state_input_value_name_map =
|
|
{{StateInputValue::low, "L"},
|
|
{StateInputValue::high, "H"},
|
|
{StateInputValue::dont_care, "-"},
|
|
{StateInputValue::low_high, "L/H"},
|
|
{StateInputValue::high_low, "H/L"},
|
|
{StateInputValue::rise, "R"},
|
|
{StateInputValue::fall, "F"},
|
|
{StateInputValue::not_rise, "~R"},
|
|
{StateInputValue::not_fall, "~F"}
|
|
};
|
|
|
|
static EnumNameMap<StateInternalValue> state_internal_value_name_map =
|
|
{{StateInternalValue::low, "L"},
|
|
{StateInternalValue::high, "H"},
|
|
{StateInternalValue::unspecified, "-"},
|
|
{StateInternalValue::low_high, "L/H"},
|
|
{StateInternalValue::high_low, "H/L"},
|
|
{StateInternalValue::unknown, "X"},
|
|
{StateInternalValue::hold, "N"}
|
|
};
|
|
|
|
StateInputValues
|
|
LibertyReader::parseStateInputValues(StringSeq &inputs,
|
|
const LibertySimpleAttr *attr)
|
|
{
|
|
StateInputValues input_values;
|
|
for (std::string input : inputs) {
|
|
bool exists;
|
|
StateInputValue value;
|
|
state_input_value_name_map.find(input.c_str(), value, exists);
|
|
if (!exists) {
|
|
libWarn(1304, attr, "table input value '%s' not recognized.",
|
|
input.c_str());
|
|
value = StateInputValue::dont_care;
|
|
}
|
|
input_values.push_back(value);
|
|
}
|
|
return input_values;
|
|
}
|
|
|
|
StateInternalValues
|
|
LibertyReader::parseStateInternalValues(StringSeq &states,
|
|
const LibertySimpleAttr *attr)
|
|
{
|
|
StateInternalValues state_values;
|
|
for (std::string state : states) {
|
|
bool exists;
|
|
StateInternalValue value;
|
|
state_internal_value_name_map.find(state.c_str(), value, exists);
|
|
if (!exists) {
|
|
libWarn(1305, attr, "table internal value '%s' not recognized.",
|
|
state.c_str());
|
|
value = StateInternalValue::unknown;
|
|
}
|
|
state_values.push_back(value);
|
|
}
|
|
return state_values;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
FloatTable
|
|
LibertyReader::makeFloatTable(const LibertyComplexAttr *values_attr,
|
|
const LibertyGroup *table_group,
|
|
size_t rows,
|
|
size_t cols,
|
|
float scale)
|
|
{
|
|
FloatTable table;
|
|
table.reserve(rows);
|
|
for (const LibertyAttrValue *value : values_attr->values()) {
|
|
FloatSeq row;
|
|
row.reserve(cols);
|
|
if (value->isString())
|
|
row = parseStringFloatList(value->stringValue(), scale, values_attr);
|
|
else if (value->isFloat())
|
|
row.push_back(value->floatValue() * scale);
|
|
else
|
|
libWarn(1258, values_attr, "%s is not a list of floats.",
|
|
values_attr->name().c_str());
|
|
if (row.size() != cols) {
|
|
libWarn(1259, values_attr, "%s row has %zu columns but axis has %zu.",
|
|
table_group->type().c_str(),
|
|
row.size(),
|
|
cols);
|
|
for (size_t c = row.size(); c < cols; c++)
|
|
row.push_back(0.0);
|
|
}
|
|
table.push_back(std::move(row));
|
|
}
|
|
if (table.size() != rows) {
|
|
if (rows == 0)
|
|
libWarn(1260, values_attr, "%s missing axis values.",
|
|
table_group->type().c_str());
|
|
else
|
|
libWarn(1261, values_attr, "%s has %zu rows but axis has %zu.",
|
|
table_group->type().c_str(),
|
|
table.size(),
|
|
rows);
|
|
for (size_t r = table.size(); r < rows; r++) {
|
|
FloatSeq row(cols, 0.0);
|
|
table.push_back(std::move(row));
|
|
}
|
|
}
|
|
return table;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::getAttrInt(const LibertySimpleAttr *attr,
|
|
// Return values.
|
|
int &value,
|
|
bool &exists)
|
|
{
|
|
value = 0;
|
|
exists = false;
|
|
const LibertyAttrValue &attr_value = attr->value();
|
|
if (attr_value.isFloat()) {
|
|
float float_val = attr_value.floatValue();
|
|
value = static_cast<int>(float_val);
|
|
exists = true;
|
|
}
|
|
else
|
|
libWarn(1268, attr, "%s attribute is not an integer.",attr->name().c_str());
|
|
}
|
|
|
|
// Get two floats in a complex attribute.
|
|
// attr(float1, float2);
|
|
void
|
|
LibertyReader::getAttrFloat2(const LibertyComplexAttr *attr,
|
|
// Return values.
|
|
float &value1,
|
|
float &value2,
|
|
bool &exists)
|
|
{
|
|
exists = false;
|
|
const LibertyAttrValueSeq &values = attr->values();
|
|
if (values.size() == 2) {
|
|
LibertyAttrValue *value = values[0];
|
|
getAttrFloat(attr, value, value1, exists);
|
|
if (!exists)
|
|
libWarn(1272, attr, "%s is not a float.", attr->name().c_str());
|
|
|
|
value = values[1];
|
|
getAttrFloat(attr, value, value2, exists);
|
|
if (!exists)
|
|
libWarn(1273, attr, "%s is not a float.", attr->name().c_str());
|
|
}
|
|
else
|
|
libWarn(1274, attr, "%s requires 2 valules.", attr->name().c_str());
|
|
}
|
|
|
|
void
|
|
LibertyReader::getAttrFloat(const LibertyComplexAttr *attr,
|
|
const LibertyAttrValue *attr_value,
|
|
// Return values.
|
|
float &value,
|
|
bool &valid)
|
|
{
|
|
if (attr_value->isFloat()) {
|
|
valid = true;
|
|
value = attr_value->floatValue();
|
|
}
|
|
else if (attr_value->isString()) {
|
|
const std::string &str = attr_value->stringValue();
|
|
variableValue(str.c_str(), value, valid);
|
|
if (!valid) {
|
|
char *end;
|
|
value = strtof(str.c_str(), &end);
|
|
if ((*end && !isspace(*end))
|
|
|| str == "inf")
|
|
libWarn(1183, attr->line(), "%s value %s is not a float.",
|
|
attr->name().c_str(),
|
|
str.c_str());
|
|
valid = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse string of comma separated floats.
|
|
// Note that some brain damaged vendors (that used to "Think") are not
|
|
// consistent about including the delimiters.
|
|
FloatSeq
|
|
LibertyReader::parseStringFloatList(const std::string &float_list,
|
|
float scale,
|
|
const LibertySimpleAttr *attr)
|
|
{
|
|
FloatSeq values;
|
|
values.reserve(std::max<size_t>(10, float_list.size() / 5));
|
|
const char *token = float_list.c_str();
|
|
while (*token != '\0') {
|
|
// Some (brain dead) libraries enclose floats in brackets.
|
|
if (*token == '{')
|
|
token++;
|
|
char *end;
|
|
float value = strtof(token, &end) * scale;
|
|
if (end == token
|
|
|| !(*end == '\0'
|
|
|| isspace(*end)
|
|
|| *end == ','
|
|
|| *end == '}')) {
|
|
std::string token_end = token;
|
|
if (end != token) {
|
|
token_end.clear();
|
|
for (const char *t = token; t <= end; t++)
|
|
token_end += *t;
|
|
}
|
|
libWarn(1310, attr, "%s is not a float.", token_end.c_str());
|
|
token += token_end.size();
|
|
}
|
|
else {
|
|
values.push_back(value);
|
|
token = end;
|
|
}
|
|
while (*token == ',' || *token == ' ' || *token == '}')
|
|
token++;
|
|
}
|
|
return values;
|
|
}
|
|
|
|
FloatSeq
|
|
LibertyReader::parseStringFloatList(const std::string &float_list,
|
|
float scale,
|
|
const LibertyComplexAttr *attr)
|
|
{
|
|
FloatSeq values;
|
|
values.reserve(std::max<size_t>(10, float_list.size() / 5));
|
|
const char *token = float_list.c_str();
|
|
while (*token != '\0') {
|
|
if (*token == '{')
|
|
token++;
|
|
char *end;
|
|
float value = strtof(token, &end) * scale;
|
|
if (end == token
|
|
|| !(*end == '\0'
|
|
|| isspace(*end)
|
|
|| *end == ','
|
|
|| *end == '}')) {
|
|
std::string token_end = token;
|
|
if (end != token) {
|
|
token_end.clear();
|
|
for (const char *t = token; t <= end; t++)
|
|
token_end += *t;
|
|
}
|
|
libWarn(1275, attr, "%s is not a float.", token_end.c_str());
|
|
token += token_end.size();
|
|
}
|
|
else {
|
|
values.push_back(value);
|
|
token = end;
|
|
}
|
|
while (*token == ',' || *token == ' ' || *token == '}')
|
|
token++;
|
|
}
|
|
return values;
|
|
}
|
|
|
|
FloatSeq
|
|
LibertyReader::readFloatSeq(const LibertyComplexAttr *attr,
|
|
float scale)
|
|
{
|
|
FloatSeq values;
|
|
const LibertyAttrValueSeq &attr_values = attr->values();
|
|
if (attr_values.size() == 1) {
|
|
LibertyAttrValue *value = attr_values[0];
|
|
if (value->isString()) {
|
|
values = parseStringFloatList(value->stringValue(), scale, attr);
|
|
}
|
|
else if (value->isFloat()) {
|
|
values.push_back(value->floatValue() * scale);
|
|
}
|
|
else
|
|
libWarn(1276, attr, "%s is missing values.", attr->name().c_str());
|
|
}
|
|
else if (attr_values.size() > 1) {
|
|
for (LibertyAttrValue *val : attr_values) {
|
|
if (val->isFloat())
|
|
values.push_back(val->floatValue() * scale);
|
|
else if (val->isString()) {
|
|
FloatSeq parsed = parseStringFloatList(val->stringValue(), scale, attr);
|
|
values.insert(values.end(), parsed.begin(), parsed.end());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
libWarn(1277, attr, "%s has no values.", attr->name().c_str());
|
|
return values;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::getAttrBool(const LibertySimpleAttr *attr,
|
|
// Return values.
|
|
bool &value,
|
|
bool &exists)
|
|
{
|
|
exists = false;
|
|
const LibertyAttrValue &val = attr->value();
|
|
if (val.isString()) {
|
|
const std::string &str = val.stringValue();
|
|
if (stringEqual(str.c_str(), "true")) {
|
|
value = true;
|
|
exists = true;
|
|
}
|
|
else if (stringEqual(str.c_str(), "false")) {
|
|
value = false;
|
|
exists = true;
|
|
}
|
|
else
|
|
libWarn(1288, attr, "%s attribute is not boolean.", attr->name().c_str());
|
|
}
|
|
else
|
|
libWarn(1289, attr, "%s attribute is not boolean.", attr->name().c_str());
|
|
}
|
|
|
|
// Read L/H/X string attribute values as bool.
|
|
LogicValue
|
|
LibertyReader::getAttrLogicValue(const LibertySimpleAttr *attr)
|
|
{
|
|
const std::string *str = attr->stringValue();
|
|
if (str) {
|
|
if (*str == "L")
|
|
return LogicValue::zero;
|
|
else if (*str == "H")
|
|
return LogicValue::one;
|
|
else if (*str == "X")
|
|
return LogicValue::unknown;
|
|
else
|
|
libWarn(1282, attr, "attribute %s value %s not recognized.",
|
|
attr->name().c_str(), str->c_str());
|
|
// fall thru
|
|
}
|
|
return LogicValue::unknown;
|
|
}
|
|
|
|
const EarlyLateAll *
|
|
LibertyReader::getAttrEarlyLate(const LibertySimpleAttr *attr)
|
|
{
|
|
const std::string *value = attr->stringValue();
|
|
if (*value == "early")
|
|
return EarlyLateAll::early();
|
|
else if (*value == "late")
|
|
return EarlyLateAll::late();
|
|
else if (*value == "early_and_late")
|
|
return EarlyLateAll::all();
|
|
else {
|
|
libWarn(1283, attr, "unknown early/late value.");
|
|
return EarlyLateAll::all();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
FuncExpr *
|
|
LibertyReader::parseFunc(const char *func,
|
|
const char *attr_name,
|
|
const LibertyCell *cell,
|
|
int line)
|
|
{
|
|
std::string error_msg;
|
|
stringPrint(error_msg, "%s, line %d %s",
|
|
filename_,
|
|
line,
|
|
attr_name);
|
|
return parseFuncExpr(func, cell, error_msg.c_str(), report_);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::visitVariable(LibertyVariable *var)
|
|
{
|
|
const std::string &var_name = var->variable();
|
|
float value;
|
|
bool exists;
|
|
findKeyValue(var_map_, var_name, value, exists);
|
|
var_map_[var_name] = var->value();
|
|
}
|
|
|
|
void
|
|
LibertyReader::variableValue(const char *var,
|
|
float &value,
|
|
bool &exists)
|
|
{
|
|
findKeyValue(var_map_, var, value, exists);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::libWarn(int id,
|
|
const LibertyGroup *group,
|
|
const char *fmt,
|
|
...) const
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
report_->vfileWarn(id, filename_, group->line(), fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void
|
|
LibertyReader::libWarn(int id,
|
|
const LibertySimpleAttr *attr,
|
|
const char *fmt,
|
|
...) const
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
report_->vfileWarn(id, filename_, attr->line(), fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void
|
|
LibertyReader::libWarn(int id,
|
|
const LibertyComplexAttr *attr,
|
|
const char *fmt,
|
|
...) const
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
report_->vfileWarn(id, filename_, attr->line(), fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void
|
|
LibertyReader::libWarn(int id,
|
|
int line,
|
|
const char *fmt,
|
|
...) const
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
report_->vfileWarn(id, filename_, line, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void
|
|
LibertyReader::libError(int id,
|
|
const LibertyGroup *group,
|
|
const char *fmt,
|
|
...) const
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
report_->vfileError(id, filename_, group->line(), fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void
|
|
LibertyReader::libError(int id,
|
|
const LibertySimpleAttr *attr,
|
|
const char *fmt,
|
|
...) const
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
report_->vfileError(id, filename_, attr->line(), fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void
|
|
LibertyReader::libError(int id,
|
|
const LibertyComplexAttr *attr,
|
|
const char *fmt,
|
|
...) const
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
report_->vfileError(id, filename_, attr->line(), fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
LibertyReader::readDefaultOcvDerateGroup(const LibertyGroup *library_group)
|
|
{
|
|
const std::string *derate_name =
|
|
library_group->findAttrString("default_ocv_derate_group");
|
|
if (derate_name) {
|
|
OcvDerate *derate = library_->findOcvDerate(derate_name->c_str());
|
|
if (derate)
|
|
library_->setDefaultOcvDerate(derate);
|
|
else
|
|
libWarn(1284, library_group, "OCV derate group named %s not found.",
|
|
derate_name->c_str());
|
|
}
|
|
}
|
|
|
|
// Read cell or library level ocv_derate groups.
|
|
void
|
|
LibertyReader::readOcvDerateFactors(LibertyCell *cell,
|
|
const LibertyGroup *parent_group)
|
|
{
|
|
for (const LibertyGroup *ocv_derate_group :
|
|
parent_group->findSubgroups("ocv_derate")) {
|
|
const char *name = ocv_derate_group->firstName();
|
|
if (name) {
|
|
OcvDerate *ocv_derate = cell
|
|
? cell->makeOcvDerate(name)
|
|
: library_->makeOcvDerate(name);
|
|
for (const LibertyGroup *factors_group :
|
|
ocv_derate_group->findSubgroups("ocv_derate_factors")) {
|
|
const RiseFallBoth *rf_type = RiseFallBoth::riseFall();
|
|
const std::string *rf_attr = factors_group->findAttrString("rf_type");
|
|
if (rf_attr) {
|
|
if (*rf_attr == "rise")
|
|
rf_type = RiseFallBoth::rise();
|
|
else if (*rf_attr == "fall")
|
|
rf_type = RiseFallBoth::fall();
|
|
else if (*rf_attr == "rise_and_fall")
|
|
rf_type = RiseFallBoth::riseFall();
|
|
else
|
|
libError(1286, factors_group, "unknown rise/fall.");
|
|
}
|
|
|
|
const EarlyLateAll *derate_type = EarlyLateAll::all();
|
|
const std::string *derate_attr = factors_group->findAttrString("derate_type");
|
|
if (derate_attr) {
|
|
if (*derate_attr == "early")
|
|
derate_type = EarlyLateAll::early();
|
|
else if (*derate_attr == "late")
|
|
derate_type = EarlyLateAll::late();
|
|
else if (*derate_attr == "early_and_late")
|
|
derate_type = EarlyLateAll::all();
|
|
else {
|
|
libWarn(1309, factors_group, "unknown early/late value.");
|
|
}
|
|
}
|
|
|
|
PathType path_type = PathType::clk_and_data;
|
|
const std::string *path_attr = factors_group->findAttrString("path_type");
|
|
if (path_attr) {
|
|
if (*path_attr == "clock")
|
|
path_type = PathType::clk;
|
|
else if (*path_attr == "data")
|
|
path_type = PathType::data;
|
|
else if (*path_attr == "clock_and_data")
|
|
path_type = PathType::clk_and_data;
|
|
else
|
|
libWarn(1287, factors_group, "unknown derate type.");
|
|
}
|
|
|
|
const char *template_name = factors_group->firstName();
|
|
if (template_name) {
|
|
TableTemplate *tbl_template =
|
|
library_->findTableTemplate(template_name, TableTemplateType::ocv);
|
|
if (tbl_template) {
|
|
TablePtr table = readTableModel(factors_group, tbl_template, 1.0F);
|
|
if (table) {
|
|
for (const EarlyLate *early_late : derate_type->range()) {
|
|
for (const RiseFall *rf : rf_type->range()) {
|
|
if (path_type == PathType::clk_and_data) {
|
|
ocv_derate->setDerateTable(rf, early_late, PathType::clk, table);
|
|
ocv_derate->setDerateTable(rf, early_late, PathType::data, table);
|
|
}
|
|
else
|
|
ocv_derate->setDerateTable(rf, early_late, path_type, table);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
libWarn(1308, factors_group, "table template %s not found.", template_name);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
libWarn(1285, ocv_derate_group, "ocv_derate missing name.");
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
PortNameBitIterator::PortNameBitIterator(LibertyCell *cell,
|
|
const char *port_name,
|
|
LibertyReader *visitor,
|
|
int line) :
|
|
cell_(cell),
|
|
visitor_(visitor),
|
|
line_(line),
|
|
port_(nullptr),
|
|
bit_iterator_(nullptr),
|
|
range_bus_port_(nullptr),
|
|
range_name_next_(nullptr),
|
|
size_(0)
|
|
{
|
|
init(port_name);
|
|
}
|
|
|
|
void
|
|
PortNameBitIterator::init(const char *port_name)
|
|
{
|
|
LibertyPort *port = visitor_->findPort(cell_, port_name);
|
|
if (port) {
|
|
if (port->isBus())
|
|
bit_iterator_ = new LibertyPortMemberIterator(port);
|
|
else
|
|
port_ = port;
|
|
size_ = port->size();
|
|
}
|
|
else {
|
|
// Check for bus range.
|
|
LibertyLibrary *library = visitor_->library();
|
|
bool is_bus, is_range, subscript_wild;
|
|
std::string bus_name;
|
|
int from, to;
|
|
parseBusName(port_name, library->busBrktLeft(),
|
|
library->busBrktRight(), '\\',
|
|
is_bus, is_range, bus_name, from, to, subscript_wild);
|
|
if (is_range) {
|
|
port = visitor_->findPort(cell_, port_name);
|
|
if (port) {
|
|
if (port->isBus()) {
|
|
if (port->busIndexInRange(from)
|
|
&& port->busIndexInRange(to)) {
|
|
range_bus_port_ = port;
|
|
range_from_ = from;
|
|
range_to_ = to;
|
|
range_bit_ = from;
|
|
}
|
|
else
|
|
visitor_->libWarn(1292, line_, "port %s subscript out of range.",
|
|
port_name);
|
|
}
|
|
else
|
|
visitor_->libWarn(1293, line_, "port range %s of non-bus port %s.",
|
|
port_name,
|
|
bus_name.c_str());
|
|
}
|
|
else {
|
|
range_bus_name_ = bus_name;
|
|
range_from_ = from;
|
|
range_to_ = to;
|
|
range_bit_ = from;
|
|
findRangeBusNameNext();
|
|
}
|
|
size_ = abs(from - to) + 1;
|
|
}
|
|
else
|
|
visitor_->libWarn(1294, line_, "port %s not found.", port_name);
|
|
}
|
|
}
|
|
|
|
PortNameBitIterator::~PortNameBitIterator()
|
|
{
|
|
delete bit_iterator_;
|
|
}
|
|
|
|
bool
|
|
PortNameBitIterator::hasNext()
|
|
{
|
|
return port_
|
|
|| (bit_iterator_ && bit_iterator_->hasNext())
|
|
|| (range_bus_port_
|
|
&& ((range_from_ > range_to_)
|
|
? range_bit_ >= range_to_
|
|
: range_bit_ <= range_from_))
|
|
|| (!range_bus_name_.empty()
|
|
&& range_name_next_);
|
|
}
|
|
|
|
LibertyPort *
|
|
PortNameBitIterator::next()
|
|
{
|
|
if (port_) {
|
|
LibertyPort *next = port_;
|
|
port_ = nullptr;
|
|
return next;
|
|
}
|
|
else if (bit_iterator_)
|
|
return bit_iterator_->next();
|
|
else if (range_bus_port_) {
|
|
LibertyPort *next = range_bus_port_->findLibertyBusBit(range_bit_);
|
|
if (range_from_ > range_to_)
|
|
range_bit_--;
|
|
else
|
|
range_bit_++;
|
|
return next;
|
|
}
|
|
else if (!range_bus_name_.empty()) {
|
|
LibertyPort *next = range_name_next_;
|
|
findRangeBusNameNext();
|
|
return next;
|
|
}
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
void
|
|
PortNameBitIterator::findRangeBusNameNext()
|
|
{
|
|
if ((range_from_ > range_to_)
|
|
? range_bit_ >= range_to_
|
|
: range_bit_ <= range_to_) {
|
|
LibertyLibrary *library = visitor_->library();
|
|
std::string bus_bit_name = range_bus_name_ + library->busBrktLeft()
|
|
+ std::to_string(range_bit_) + library->busBrktRight();
|
|
range_name_next_ = visitor_->findPort(cell_, bus_bit_name.c_str());
|
|
if (range_name_next_) {
|
|
if (range_from_ > range_to_)
|
|
range_bit_--;
|
|
else
|
|
range_bit_++;
|
|
}
|
|
else
|
|
visitor_->libWarn(1295, line_, "port %s not found.", bus_bit_name.c_str());
|
|
}
|
|
else
|
|
range_name_next_ = nullptr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
OutputWaveform::OutputWaveform(float slew,
|
|
float cap,
|
|
Table *currents,
|
|
float reference_time) :
|
|
slew_(slew),
|
|
cap_(cap),
|
|
currents_(currents),
|
|
reference_time_(reference_time)
|
|
{
|
|
}
|
|
|
|
Table *
|
|
OutputWaveform::releaseCurrents()
|
|
{
|
|
return currents_.release();
|
|
}
|
|
|
|
} // namespace
|