OpenSTA/liberty/LibertyParser.cc

609 lines
13 KiB
C++

// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "LibertyParser.hh"
#include <cstdio>
#include <cstring>
#include "Report.hh"
#include "Error.hh"
#include "StringUtil.hh"
// Global namespace
int
LibertyParse_parse();
namespace sta {
typedef Vector<LibertyGroup*> LibertyGroupSeq;
static const char *liberty_filename;
static gzFile liberty_stream;
static int liberty_line;
// Previous lex reader state for include files.
static const char *liberty_filename_prev;
static int liberty_line_prev;
static gzFile liberty_stream_prev;
static LibertyGroupVisitor *liberty_group_visitor;
static LibertyGroupSeq liberty_group_stack;
static Report *liberty_report;
static LibertyStmt *
makeLibertyDefine(LibertyAttrValueSeq *values,
int line);
static LibertyAttrType
attrValueType(const char *value_type_name);
static LibertyGroupType
groupType(const char *group_type_name);
////////////////////////////////////////////////////////////////
void
parseLibertyFile(const char *filename,
LibertyGroupVisitor *library_visitor,
Report *report)
{
liberty_stream = gzopen(filename, "r");
if (liberty_stream) {
liberty_group_visitor = library_visitor;
liberty_group_stack.clear();
liberty_filename = filename;
liberty_filename_prev = nullptr;
liberty_stream_prev = nullptr;
liberty_line = 1;
liberty_report = report;
LibertyParse_parse();
gzclose(liberty_stream);
}
else
throw FileNotReadable(filename);
}
void
libertyGetChars(char *buf,
int &result,
size_t max_size)
{
char *status = gzgets(liberty_stream, buf, max_size);
if (status == Z_NULL)
result = 0; // YY_nullptr
else
result = strlen(buf);
}
void
libertyGetChars(char *buf,
size_t &result,
size_t max_size)
{
char *status = gzgets(liberty_stream, buf, max_size);
if (status == Z_NULL)
result = 0; // YY_nullptr
else
result = strlen(buf);
}
void
libertyGroupBegin(const char *type,
LibertyAttrValueSeq *params,
int line)
{
LibertyGroup *group = new LibertyGroup(type, params, line);
liberty_group_visitor->begin(group);
liberty_group_stack.push_back(group);
}
LibertyGroup *
libertyGroupEnd()
{
LibertyGroup *group = libertyGroup();
liberty_group_visitor->end(group);
liberty_group_stack.pop_back();
LibertyGroup *parent =
liberty_group_stack.empty() ? nullptr : liberty_group_stack.back();
if (parent && liberty_group_visitor->save(group)) {
parent->addSubgroup(group);
return group;
}
else {
delete group;
return nullptr;
}
}
////////////////////////////////////////////////////////////////
LibertyStmt::LibertyStmt(int line) :
line_(line)
{
}
LibertyGroup::LibertyGroup(const char *type,
LibertyAttrValueSeq *params,
int line) :
LibertyStmt(line),
type_(type),
params_(params),
attrs_(nullptr),
attr_map_(nullptr),
subgroups_(nullptr),
define_map_(nullptr)
{
}
void
LibertyGroup::addSubgroup(LibertyGroup *subgroup)
{
if (subgroups_ == nullptr)
subgroups_ = new LibertyGroupSeq;
subgroups_->push_back(subgroup);
}
void
LibertyGroup::addDefine(LibertyDefine *define)
{
if (define_map_ == nullptr)
define_map_ = new LibertyDefineMap;
const char *define_name = define->name();
LibertyDefine *prev_define = define_map_->findKey(define_name);
if (prev_define) {
define_map_->erase(define_name);
delete prev_define;
}
(*define_map_)[define_name] = define;
}
void
LibertyGroup::addAttribute(LibertyAttr *attr)
{
if (attrs_ == nullptr)
attrs_ = new LibertyAttrSeq;
attrs_->push_back(attr);
if (attr_map_)
(*attr_map_)[attr->name()] = attr;
}
LibertyGroup::~LibertyGroup()
{
stringDelete(type_);
if (params_) {
params_->deleteContents();
delete params_;
}
if (attrs_) {
LibertyAttrSeq::Iterator iter(attrs_);
attrs_->deleteContents();
delete attrs_;
delete attr_map_;
}
if (subgroups_) {
subgroups_->deleteContents();
delete subgroups_;
}
if (define_map_) {
define_map_->deleteContents();
delete define_map_;
}
}
const char *
LibertyGroup::firstName()
{
if (params_ && params_->size() > 0) {
LibertyAttrValue *value = (*params_)[0];
if (value->isString())
return value->stringValue();
}
return nullptr;
}
const char *
LibertyGroup::secondName()
{
if (params_ && params_->size() > 1) {
LibertyAttrValue *value = (*params_)[1];
if (value->isString())
return value->stringValue();
}
return nullptr;
}
LibertyAttr *
LibertyGroup::findAttr(const char *name)
{
if (attrs_) {
if (attr_map_ == nullptr) {
// Build attribute name map on demand.
LibertyAttrSeq::Iterator attr_iter(attrs_);
while (attr_iter.hasNext()) {
LibertyAttr *attr = attr_iter.next();
(*attr_map_)[attr->name()] = attr;
}
}
return attr_map_->findKey(name);
}
else
return nullptr;
}
LibertySubgroupIterator::LibertySubgroupIterator(LibertyGroup *group) :
LibertyGroupSeq::Iterator(group->subgroups())
{
}
LibertyAttrIterator::LibertyAttrIterator(LibertyGroup *group) :
LibertyAttrSeq::Iterator(group->attrs())
{
}
////////////////////////////////////////////////////////////////
LibertyAttr::LibertyAttr(const char *name,
int line) :
LibertyStmt(line),
name_(name)
{
}
LibertyAttr::~LibertyAttr()
{
stringDelete(name_);
}
LibertyStmt *
makeLibertySimpleAttr(const char *name,
LibertyAttrValue *value,
int line)
{
LibertyAttr *attr = new LibertySimpleAttr(name, value, line);
if (liberty_group_visitor)
liberty_group_visitor->visitAttr(attr);
LibertyGroup *group = libertyGroup();
if (group && liberty_group_visitor->save(attr)) {
group->addAttribute(attr);
return attr;
}
else {
delete attr;
return nullptr;
}
}
LibertyGroup *
libertyGroup()
{
return liberty_group_stack.back();
}
LibertySimpleAttr::LibertySimpleAttr(const char *name,
LibertyAttrValue *value,
int line) :
LibertyAttr(name, line),
value_(value)
{
}
LibertySimpleAttr::~LibertySimpleAttr()
{
delete value_;
}
LibertyAttrValueSeq *
LibertySimpleAttr::values() const
{
criticalError(1125, "valueIterator called for LibertySimpleAttribute");
return nullptr;
}
LibertyStmt *
makeLibertyComplexAttr(const char *name,
LibertyAttrValueSeq *values,
int line)
{
// Defines have the same syntax as complex attributes.
// Detect and convert them.
if (stringEq(name, "define")) {
LibertyStmt *define = makeLibertyDefine(values, line);
stringDelete(name);
LibertyAttrValueSeq::Iterator attr_iter(values);
while (attr_iter.hasNext())
delete attr_iter.next();
delete values;
return define;
}
else {
LibertyAttr *attr = new LibertyComplexAttr(name, values, line);
if (liberty_group_visitor) {
liberty_group_visitor->visitAttr(attr);
if (liberty_group_visitor->save(attr)) {
LibertyGroup *group = libertyGroup();
group->addAttribute(attr);
return attr;
}
}
delete attr;
return nullptr;
}
}
LibertyComplexAttr::LibertyComplexAttr(const char *name,
LibertyAttrValueSeq *values,
int line) :
LibertyAttr(name, line),
values_(values)
{
}
LibertyComplexAttr::~LibertyComplexAttr()
{
if (values_) {
values_->deleteContents();
delete values_;
}
}
LibertyAttrValue *
LibertyComplexAttr::firstValue()
{
if (values_ && values_->size() > 0)
return (*values_)[0];
else
return nullptr;
}
LibertyAttrValue *
makeLibertyStringAttrValue(char *value)
{
return new LibertyStringAttrValue(value);
}
LibertyStringAttrValue::LibertyStringAttrValue(const char *value) :
LibertyAttrValue(),
value_(value)
{
}
LibertyStringAttrValue::~LibertyStringAttrValue()
{
stringDelete(value_);
}
float
LibertyStringAttrValue::floatValue()
{
criticalError(1126, "LibertyStringAttrValue called for float value");
return 0.0;
}
const char *
LibertyStringAttrValue::stringValue()
{
return value_;
}
LibertyAttrValue *
makeLibertyFloatAttrValue(float value)
{
return new LibertyFloatAttrValue(value);
}
LibertyFloatAttrValue::LibertyFloatAttrValue(float value) :
value_(value)
{
}
float
LibertyFloatAttrValue::floatValue()
{
return value_;
}
const char *
LibertyFloatAttrValue::stringValue()
{
criticalError(1127, "LibertyStringAttrValue called for float value");
return nullptr;
}
////////////////////////////////////////////////////////////////
static LibertyStmt *
makeLibertyDefine(LibertyAttrValueSeq *values,
int line)
{
LibertyDefine *define = nullptr;
if (values->size() == 3) {
const char *define_name = (*values)[0]->stringValue();
const char *group_type_name = (*values)[1]->stringValue();
const char *value_type_name = (*values)[2]->stringValue();
LibertyAttrType value_type = attrValueType(value_type_name);
LibertyGroupType group_type = groupType(group_type_name);
define = new LibertyDefine(stringCopy(define_name), group_type,
value_type, line);
LibertyGroup *group = libertyGroup();
group->addDefine(define);
}
else
liberty_report->fileWarn(24, liberty_filename, line,
"define does not have three arguments.");
return define;
}
// The Liberty User Guide Version 2001.08 fails to define the strings
// used to define valid attribute types. Beyond "string" these are
// guesses.
static LibertyAttrType
attrValueType(const char *value_type_name)
{
if (stringEq(value_type_name, "string"))
return LibertyAttrType::attr_string;
else if (stringEq(value_type_name, "integer"))
return LibertyAttrType::attr_int;
else if (stringEq(value_type_name, "float"))
return LibertyAttrType::attr_double;
else if (stringEq(value_type_name, "boolean"))
return LibertyAttrType::attr_boolean;
else
return LibertyAttrType::attr_unknown;
}
static LibertyGroupType
groupType(const char *group_type_name)
{
if (stringEq(group_type_name, "library"))
return LibertyGroupType::library;
else if (stringEq(group_type_name, "cell"))
return LibertyGroupType::cell;
else if (stringEq(group_type_name, "pin"))
return LibertyGroupType::pin;
else if (stringEq(group_type_name, "timing"))
return LibertyGroupType::timing;
else
return LibertyGroupType::unknown;
}
LibertyDefine::LibertyDefine(const char *name,
LibertyGroupType group_type,
LibertyAttrType value_type,
int line) :
LibertyStmt(line),
name_(name),
group_type_(group_type),
value_type_(value_type)
{
}
LibertyDefine::~LibertyDefine()
{
stringDelete(name_);
}
////////////////////////////////////////////////////////////////
LibertyStmt *
makeLibertyVariable(char *var,
float value,
int line)
{
LibertyVariable *variable = new LibertyVariable(var, value, line);
liberty_group_visitor->visitVariable(variable);
if (liberty_group_visitor->save(variable))
return variable;
else {
delete variable;
return nullptr;
}
}
LibertyVariable::LibertyVariable(const char *var,
float value,
int line) :
LibertyStmt(line),
var_(var),
value_(value)
{
}
LibertyVariable::~LibertyVariable()
{
stringDelete(var_);
}
////////////////////////////////////////////////////////////////
bool
libertyInInclude()
{
return liberty_filename_prev != nullptr;
}
void
libertyIncludeBegin(const char *filename)
{
gzFile stream = gzopen(filename, "r" );
if (stream) {
liberty_stream_prev = liberty_stream;
liberty_filename_prev = liberty_filename;
liberty_line_prev = liberty_line;
liberty_stream = stream;
liberty_filename = filename;
liberty_line = 1;
}
else
liberty_report->fileWarn(25, sta::liberty_filename, sta::liberty_line,
"cannot open include file %s.", filename);
}
void
libertyIncludeEnd()
{
gzclose(liberty_stream);
liberty_stream = liberty_stream_prev;
liberty_filename = liberty_filename_prev;
liberty_line = liberty_line_prev;
liberty_filename_prev = nullptr;
liberty_stream_prev = nullptr;
}
void
libertyIncrLine()
{
sta::liberty_line++;
}
int
libertyLine()
{
return liberty_line;
}
void
libertyParseError(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
sta::liberty_report->vfileError(25, sta::liberty_filename, sta::liberty_line,
fmt, args);
va_end(args);
}
void
deleteLibertyGroups()
{
liberty_group_stack.deleteContentsClear();
}
} // namespace
////////////////////////////////////////////////////////////////
// Global namespace
void libertyParseFlushBuffer();
int
LibertyParse_error(const char *msg)
{
libertyParseFlushBuffer();
sta::liberty_report->fileError(26, sta::liberty_filename, sta::liberty_line,
"%s.", msg);
return 0;
}