650 lines
20 KiB
C++
650 lines
20 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 "LibertyWriter.hh"
|
|
|
|
#include <cstdlib>
|
|
#include <algorithm>
|
|
|
|
#include "Units.hh"
|
|
#include "FuncExpr.hh"
|
|
#include "PortDirection.hh"
|
|
#include "Liberty.hh"
|
|
#include "TimingRole.hh"
|
|
#include "TimingArc.hh"
|
|
#include "TimingModel.hh"
|
|
#include "TableModel.hh"
|
|
#include "StaState.hh"
|
|
|
|
namespace sta {
|
|
|
|
using std::abs;
|
|
|
|
class LibertyWriter
|
|
{
|
|
public:
|
|
LibertyWriter(const LibertyLibrary *lib,
|
|
const char *filename,
|
|
FILE *stream,
|
|
Report *report);
|
|
void writeLibrary();
|
|
|
|
protected:
|
|
void writeHeader();
|
|
void writeFooter();
|
|
void writeTableTemplates();
|
|
void writeTableTemplate(const TableTemplate *tbl_template);
|
|
void writeBusDcls();
|
|
void writeCells();
|
|
void writeCell(const LibertyCell *cell);
|
|
void writePort(const LibertyPort *port);
|
|
void writeBusPort(const LibertyPort *port);
|
|
void writePortAttrs(const LibertyPort *port);
|
|
void writeTimingArcSet(const TimingArcSet *arc_set);
|
|
void writeTimingModels(const TimingArc *arc,
|
|
const RiseFall *rf);
|
|
void writeTableModel(const TableModel *model);
|
|
void writeTableModel0(const TableModel *model);
|
|
void writeTableModel1(const TableModel *model);
|
|
void writeTableModel2(const TableModel *model);
|
|
void writeTableAxis4(const TableAxis *axis,
|
|
int index);
|
|
void writeTableAxis10(const TableAxis *axis,
|
|
int index);
|
|
|
|
const char *asString(bool value);
|
|
const char *asString(const PortDirection *dir);
|
|
const char *timingTypeString(const TimingArcSet *arc_set);
|
|
bool isAutoWidthArc(const LibertyPort *port,
|
|
const TimingArcSet *arc_set);
|
|
|
|
const LibertyLibrary *library_;
|
|
const char *filename_;
|
|
FILE *stream_;
|
|
Report *report_;
|
|
const Unit *time_unit_;
|
|
const Unit *cap_unit_;
|
|
};
|
|
|
|
void
|
|
writeLiberty(LibertyLibrary *lib,
|
|
const char *filename,
|
|
StaState *sta)
|
|
{
|
|
FILE *stream = fopen(filename, "w");
|
|
if (stream) {
|
|
LibertyWriter writer(lib, filename, stream, sta->report());
|
|
writer.writeLibrary();
|
|
fclose(stream);
|
|
}
|
|
else
|
|
throw FileNotWritable(filename);
|
|
}
|
|
|
|
LibertyWriter::LibertyWriter(const LibertyLibrary *lib,
|
|
const char *filename,
|
|
FILE *stream,
|
|
Report *report) :
|
|
library_(lib),
|
|
filename_(filename),
|
|
stream_(stream),
|
|
report_(report),
|
|
time_unit_(lib->units()->timeUnit()),
|
|
cap_unit_(lib->units()->capacitanceUnit())
|
|
{
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeLibrary()
|
|
{
|
|
writeHeader();
|
|
fprintf(stream_, "\n");
|
|
writeTableTemplates();
|
|
writeBusDcls();
|
|
fprintf(stream_, "\n");
|
|
writeCells();
|
|
writeFooter();
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeHeader()
|
|
{
|
|
fprintf(stream_, "library (%s) {\n", library_->name());
|
|
fprintf(stream_, " comment : \"\";\n");
|
|
fprintf(stream_, " delay_model : table_lookup;\n");
|
|
fprintf(stream_, " simulation : false;\n");
|
|
const Unit *cap_unit = library_->units()->capacitanceUnit();
|
|
fprintf(stream_, " capacitive_load_unit (1,%s);\n",
|
|
cap_unit->scaledSuffix());
|
|
fprintf(stream_, " leakage_power_unit : 1pW;\n");
|
|
const Unit *current_unit = library_->units()->currentUnit();
|
|
fprintf(stream_, " current_unit : \"1%s\";\n",
|
|
current_unit->scaledSuffix());
|
|
const Unit *res_unit = library_->units()->resistanceUnit();
|
|
fprintf(stream_, " pulling_resistance_unit : \"1%s\";\n",
|
|
res_unit->scaledSuffix());
|
|
const Unit *time_unit = library_->units()->timeUnit();
|
|
fprintf(stream_, " time_unit : \"1%s\";\n",
|
|
time_unit->scaledSuffix());
|
|
const Unit *volt_unit = library_->units()->voltageUnit();
|
|
fprintf(stream_, " voltage_unit : \"1%s\";\n",
|
|
volt_unit->scaledSuffix());
|
|
fprintf(stream_, " library_features(report_delay_calculation);\n");
|
|
fprintf(stream_, "\n");
|
|
|
|
fprintf(stream_, " input_threshold_pct_rise : %.0f;\n",
|
|
library_->inputThreshold(RiseFall::rise()) * 100);
|
|
fprintf(stream_, " input_threshold_pct_fall : %.0f;\n",
|
|
library_->inputThreshold(RiseFall::fall()) * 100);
|
|
fprintf(stream_, " output_threshold_pct_rise : %.0f;\n",
|
|
library_->inputThreshold(RiseFall::rise()) * 100);
|
|
fprintf(stream_, " output_threshold_pct_fall : %.0f;\n",
|
|
library_->inputThreshold(RiseFall::fall()) * 100);
|
|
fprintf(stream_, " slew_lower_threshold_pct_rise : %.0f;\n",
|
|
library_->slewLowerThreshold(RiseFall::rise()) * 100);
|
|
fprintf(stream_, " slew_lower_threshold_pct_fall : %.0f;\n",
|
|
library_->slewLowerThreshold(RiseFall::fall()) * 100);
|
|
fprintf(stream_, " slew_upper_threshold_pct_rise : %.0f;\n",
|
|
library_->slewUpperThreshold(RiseFall::rise()) * 100);
|
|
fprintf(stream_, " slew_upper_threshold_pct_fall : %.0f;\n",
|
|
library_->slewUpperThreshold(RiseFall::rise()) * 100);
|
|
fprintf(stream_, " slew_derate_from_library : %.1f;\n",
|
|
library_->slewDerateFromLibrary());
|
|
fprintf(stream_, "\n");
|
|
|
|
bool exists;
|
|
float max_fanout;
|
|
library_->defaultFanoutLoad(max_fanout, exists);
|
|
if (exists)
|
|
fprintf(stream_, " default_max_fanout : %.0f;\n", max_fanout);
|
|
float max_slew;
|
|
library_->defaultMaxSlew(max_slew, exists);
|
|
if (exists)
|
|
fprintf(stream_, " default_max_transition : %s;\n",
|
|
time_unit_->asString(max_slew, 3));
|
|
float max_cap;
|
|
library_->defaultMaxCapacitance(max_cap, exists);
|
|
if (exists)
|
|
fprintf(stream_, " default_max_capacitance : %s;\n",
|
|
cap_unit_->asString(max_cap, 3));
|
|
float fanout_load;
|
|
library_->defaultFanoutLoad(fanout_load, exists);
|
|
if (exists)
|
|
fprintf(stream_, " default_fanout_load : %.2f;\n", fanout_load);
|
|
fprintf(stream_, "\n");
|
|
|
|
fprintf(stream_, " nom_process : %.1f;\n",
|
|
library_->nominalProcess());
|
|
fprintf(stream_, " nom_temperature : %.1f;\n",
|
|
library_->nominalTemperature());
|
|
fprintf(stream_, " nom_voltage : %.2f;\n",
|
|
library_->nominalVoltage());
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeTableTemplates()
|
|
{
|
|
for (TableTemplate *tbl_template : library_->tableTemplates())
|
|
writeTableTemplate(tbl_template);
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeTableTemplate(const TableTemplate *tbl_template)
|
|
{
|
|
const TableAxis *axis1 = tbl_template->axis1();
|
|
const TableAxis *axis2 = tbl_template->axis2();
|
|
const TableAxis *axis3 = tbl_template->axis3();
|
|
// skip scalar templates
|
|
if (axis1) {
|
|
fprintf(stream_, " lu_table_template(%s) {\n", tbl_template->name());
|
|
fprintf(stream_, " variable_1 : %s;\n",
|
|
tableVariableString(axis1->variable()));
|
|
if (axis2)
|
|
fprintf(stream_, " variable_2 : %s;\n",
|
|
tableVariableString(axis2->variable()));
|
|
if (axis3)
|
|
fprintf(stream_, " variable_3 : %s;\n",
|
|
tableVariableString(axis3->variable()));
|
|
if (axis1 && axis1->values())
|
|
writeTableAxis4(axis1, 1);
|
|
if (axis2 && axis2->values())
|
|
writeTableAxis4(axis2, 2);
|
|
if (axis3 && axis3->values())
|
|
writeTableAxis4(axis3, 3);
|
|
fprintf(stream_, " }\n");
|
|
}
|
|
}
|
|
|
|
// indent 4
|
|
void
|
|
LibertyWriter::writeTableAxis4(const TableAxis *axis,
|
|
int index)
|
|
{
|
|
fprintf(stream_, " index_%d(\"", index);
|
|
const Unit *unit = tableVariableUnit(axis->variable(), library_->units());
|
|
bool first = true;
|
|
for (size_t i = 0; i < axis->size(); i++) {
|
|
if (!first)
|
|
fprintf(stream_, ", ");
|
|
fprintf(stream_, "%s", unit->asString(axis->axisValue(i), 5));
|
|
first = false;
|
|
}
|
|
fprintf(stream_, "\");\n");
|
|
}
|
|
|
|
// indent 10
|
|
void
|
|
LibertyWriter::writeTableAxis10(const TableAxis *axis,
|
|
int index)
|
|
{
|
|
fprintf(stream_, " ");
|
|
writeTableAxis4(axis, index);
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeBusDcls()
|
|
{
|
|
BusDclSeq dcls = library_->busDcls();
|
|
for (BusDcl *dcl : dcls) {
|
|
fprintf(stream_, " type (\"%s\") {\n", dcl->name());
|
|
fprintf(stream_, " base_type : array;\n");
|
|
fprintf(stream_, " data_type : bit;\n");
|
|
fprintf(stream_, " bit_width : %d;\n", abs(dcl->from() - dcl->to() + 1));
|
|
fprintf(stream_, " bit_from : %d;\n", dcl->from());
|
|
fprintf(stream_, " bit_to : %d;\n", dcl->to());
|
|
fprintf(stream_, " }\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeCells()
|
|
{
|
|
LibertyCellIterator cell_iter(library_);
|
|
while (cell_iter.hasNext()) {
|
|
const LibertyCell *cell = cell_iter.next();
|
|
writeCell(cell);
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeCell(const LibertyCell *cell)
|
|
{
|
|
fprintf(stream_, " cell (\"%s\") {\n", cell->name());
|
|
float area = cell->area();
|
|
if (area > 0.0)
|
|
fprintf(stream_, " area : %.3f \n", area);
|
|
if (cell->isMacro())
|
|
fprintf(stream_, " is_macro_cell : true;\n");
|
|
if (cell->interfaceTiming())
|
|
fprintf(stream_, " interface_timing : true;\n");
|
|
const char *footprint = cell->footprint();
|
|
if (footprint)
|
|
fprintf(stream_, " cell_footprint : \"%s\";\n", footprint);
|
|
const char *user_function_class = cell->userFunctionClass();
|
|
if (user_function_class)
|
|
fprintf(stream_, " user_function_class : \"%s\";\n",
|
|
user_function_class);
|
|
|
|
LibertyCellPortIterator port_iter(cell);
|
|
while (port_iter.hasNext()) {
|
|
const LibertyPort *port = port_iter.next();
|
|
if (!port->direction()->isInternal()) {
|
|
if (port->isBus())
|
|
writeBusPort(port);
|
|
else if (port->isBundle())
|
|
report_->error(1340, "%s/%s bundled ports not supported.",
|
|
library_->name(),
|
|
cell->name());
|
|
else
|
|
writePort(port);
|
|
}
|
|
}
|
|
|
|
fprintf(stream_, " }\n");
|
|
fprintf(stream_, "\n");
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeBusPort(const LibertyPort *port)
|
|
{
|
|
fprintf(stream_, " bus(\"%s\") {\n", port->name());
|
|
if (port->busDcl())
|
|
fprintf(stream_, " bus_type : %s;\n", port->busDcl()->name());
|
|
writePortAttrs(port);
|
|
|
|
LibertyPortMemberIterator member_iter(port);
|
|
while (member_iter.hasNext()) {
|
|
LibertyPort *member = member_iter.next();
|
|
writePort(member);
|
|
}
|
|
fprintf(stream_, " }\n");
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writePort(const LibertyPort *port)
|
|
{
|
|
fprintf(stream_, " pin(\"%s\") {\n", port->name());
|
|
writePortAttrs(port);
|
|
fprintf(stream_, " }\n");
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writePortAttrs(const LibertyPort *port)
|
|
{
|
|
fprintf(stream_, " direction : %s;\n" , asString(port->direction()));
|
|
auto func = port->function();
|
|
if (func
|
|
// cannot ref internal ports until sequentials are written
|
|
&& !(func->port()
|
|
&& func->port()->direction()->isInternal()))
|
|
fprintf(stream_, " function : \"%s\";\n", func->to_string().c_str());
|
|
auto tristate_enable = port->tristateEnable();
|
|
if (tristate_enable) {
|
|
if (tristate_enable->op() == FuncExpr::op_not) {
|
|
FuncExpr *three_state = tristate_enable->left();
|
|
fprintf(stream_, " three_state : \"%s\";\n",
|
|
three_state->to_string().c_str());
|
|
}
|
|
else {
|
|
FuncExpr three_state(FuncExpr::op_not, tristate_enable, nullptr, nullptr);
|
|
fprintf(stream_, " three_state : \"%s\";\n",
|
|
three_state.to_string().c_str());
|
|
}
|
|
}
|
|
if (port->isClock())
|
|
fprintf(stream_, " clock : true;\n");
|
|
fprintf(stream_, " capacitance : %s;\n",
|
|
cap_unit_->asString(port->capacitance(), 4));
|
|
|
|
float limit;
|
|
bool exists;
|
|
port->slewLimit(MinMax::max(), limit, exists);
|
|
if (exists)
|
|
fprintf(stream_, " max_transition : %s;\n",
|
|
time_unit_->asString(limit, 3));
|
|
port->capacitanceLimit(MinMax::max(), limit, exists);
|
|
if (exists)
|
|
fprintf(stream_, " max_capacitance : %s;\n",
|
|
cap_unit_->asString(limit, 3));
|
|
|
|
for (TimingArcSet *arc_set : port->libertyCell()->timingArcSets(nullptr,port)) {
|
|
if (!isAutoWidthArc(port, arc_set))
|
|
writeTimingArcSet(arc_set);
|
|
}
|
|
}
|
|
|
|
// Check if arc is added for port min_pulse_width_high/low attribute.
|
|
bool
|
|
LibertyWriter::isAutoWidthArc(const LibertyPort *port,
|
|
const TimingArcSet *arc_set)
|
|
{
|
|
if (arc_set->role() == TimingRole::width()) {
|
|
float min_width;
|
|
bool exists1, exists2;
|
|
port->minPulseWidth(RiseFall::rise(), min_width, exists1);
|
|
port->minPulseWidth(RiseFall::fall(), min_width, exists2);
|
|
return exists1 || exists2;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeTimingArcSet(const TimingArcSet *arc_set)
|
|
{
|
|
fprintf(stream_, " timing() {\n");
|
|
if (arc_set->from())
|
|
fprintf(stream_, " related_pin : \"%s\";\n", arc_set->from()->name());
|
|
TimingSense sense = arc_set->sense();
|
|
if (sense != TimingSense::unknown
|
|
&& sense != TimingSense::non_unate)
|
|
fprintf(stream_, " timing_sense : %s;\n",
|
|
to_string(sense));
|
|
const char *timing_type = timingTypeString(arc_set);
|
|
if (timing_type)
|
|
fprintf(stream_, " timing_type : %s;\n", timing_type);
|
|
|
|
for (const RiseFall *rf : RiseFall::range()) {
|
|
TimingArc *arc = arc_set->arcTo(rf);
|
|
if (arc) {
|
|
// Min pulse width arcs are wrt to the leading edge of the pulse.
|
|
const RiseFall *model_rf = (arc_set->role() == TimingRole::width())
|
|
? rf->opposite()
|
|
: rf;
|
|
writeTimingModels(arc, model_rf);
|
|
}
|
|
}
|
|
|
|
fprintf(stream_, " }\n");
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeTimingModels(const TimingArc *arc,
|
|
const RiseFall *rf)
|
|
{
|
|
TimingModel *model = arc->model();
|
|
const GateTableModel *gate_model = dynamic_cast<GateTableModel*>(model);
|
|
const CheckTableModel *check_model = dynamic_cast<CheckTableModel*>(model);
|
|
if (gate_model) {
|
|
const TableModel *delay_model = gate_model->delayModel();
|
|
const char *template_name = delay_model->tblTemplate()->name();
|
|
fprintf(stream_, " cell_%s(%s) {\n", rf->name(), template_name);
|
|
writeTableModel(delay_model);
|
|
fprintf(stream_, " }\n");
|
|
|
|
const TableModel *slew_model = gate_model->slewModel();
|
|
if (slew_model) {
|
|
template_name = slew_model->tblTemplate()->name();
|
|
fprintf(stream_, " %s_transition(%s) {\n", rf->name(), template_name);
|
|
writeTableModel(slew_model);
|
|
fprintf(stream_, " }\n");
|
|
}
|
|
}
|
|
else if (check_model) {
|
|
const TableModel *model = check_model->model();
|
|
const char *template_name = model->tblTemplate()->name();
|
|
fprintf(stream_, " %s_constraint(%s) {\n", rf->name(), template_name);
|
|
writeTableModel(model);
|
|
fprintf(stream_, " }\n");
|
|
}
|
|
else
|
|
report_->error(1341, "%s/%s/%s timing model not supported.",
|
|
library_->name(),
|
|
arc->from()->libertyCell()->name(),
|
|
arc->from()->name());
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeTableModel(const TableModel *model)
|
|
{
|
|
switch (model->order()) {
|
|
case 0:
|
|
writeTableModel0(model);
|
|
break;
|
|
case 1:
|
|
writeTableModel1(model);
|
|
break;
|
|
case 2:
|
|
writeTableModel2(model);
|
|
break;
|
|
case 3:
|
|
report_->error(1342, "3 axis table models not supported.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeTableModel0(const TableModel *model)
|
|
{
|
|
float value = model->value(0, 0, 0);
|
|
fprintf(stream_, " values(\"%s\");\n",
|
|
time_unit_->asString(value, 5));
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeTableModel1(const TableModel *model)
|
|
{
|
|
writeTableAxis10(model->axis1(), 1);
|
|
fprintf(stream_, " values(\"");
|
|
bool first_col = true;
|
|
for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) {
|
|
float value = model->value(index1, 0, 0);
|
|
if (!first_col)
|
|
fprintf(stream_, ",");
|
|
fprintf(stream_, "%s", time_unit_->asString(value, 5));
|
|
first_col = false;
|
|
}
|
|
fprintf(stream_, "\");\n");
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeTableModel2(const TableModel *model)
|
|
{
|
|
writeTableAxis10(model->axis1(), 1);
|
|
writeTableAxis10(model->axis2(), 2);
|
|
fprintf(stream_, " values(\"");
|
|
bool first_row = true;
|
|
for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) {
|
|
if (!first_row) {
|
|
fprintf(stream_, "\\\n");
|
|
fprintf(stream_, " \"");
|
|
}
|
|
bool first_col = true;
|
|
for (size_t index2 = 0; index2 < model->axis2()->size(); index2++) {
|
|
float value = model->value(index1, index2, 0);
|
|
if (!first_col)
|
|
fprintf(stream_, ",");
|
|
fprintf(stream_, "%s", time_unit_->asString(value, 5));
|
|
first_col = false;
|
|
}
|
|
fprintf(stream_, "\"");
|
|
first_row = false;
|
|
}
|
|
fprintf(stream_, ");\n");
|
|
}
|
|
|
|
void
|
|
LibertyWriter::writeFooter()
|
|
{
|
|
fprintf(stream_, "}\n");
|
|
}
|
|
|
|
const char *
|
|
LibertyWriter::asString(bool value)
|
|
{
|
|
return value ? "true" : "false";
|
|
}
|
|
|
|
const char *
|
|
LibertyWriter::asString(const PortDirection *dir)
|
|
{
|
|
if (dir == PortDirection::input())
|
|
return "input";
|
|
else if (dir == PortDirection::output()
|
|
|| (dir == PortDirection::tristate()))
|
|
return "output";
|
|
else if (dir == PortDirection::internal())
|
|
return "internal";
|
|
else if (dir == PortDirection::bidirect())
|
|
return "inout";
|
|
else if (dir == PortDirection::ground()
|
|
|| dir == PortDirection::power())
|
|
return "input";
|
|
return "unknown";
|
|
}
|
|
|
|
const char *
|
|
LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
|
|
{
|
|
const TimingRole *role = arc_set->role();
|
|
if (role == TimingRole::combinational())
|
|
return "combinational";
|
|
else if (role == TimingRole::tristateDisable())
|
|
return "three_state_disable";
|
|
else if (role == TimingRole::tristateEnable())
|
|
return "three_state_enable";
|
|
else if (role == TimingRole::regClkToQ()
|
|
|| role == TimingRole::latchEnToQ()) {
|
|
const TimingArc *arc = arc_set->arcs()[0];
|
|
if (arc->fromEdge()->asRiseFall() == RiseFall::rise())
|
|
return "rising_edge";
|
|
else
|
|
return "falling_edge";
|
|
}
|
|
else if (role == TimingRole::latchDtoQ())
|
|
return nullptr;
|
|
else if (role == TimingRole::regSetClr()) {
|
|
const TimingArc *arc = arc_set->arcs()[0];
|
|
if (arc->toEdge()->asRiseFall() == RiseFall::rise())
|
|
return "preset";
|
|
else
|
|
return "clear";
|
|
}
|
|
else if (role == TimingRole::setup()
|
|
|| role == TimingRole::recovery()) {
|
|
const TimingArc *arc = arc_set->arcs()[0];
|
|
if (arc->fromEdge()->asRiseFall() == RiseFall::rise())
|
|
return "setup_rising";
|
|
else
|
|
return "setup_falling";
|
|
}
|
|
else if (role == TimingRole::hold()
|
|
|| role == TimingRole::removal()) {
|
|
const TimingArc *arc = arc_set->arcs()[0];
|
|
if (arc->fromEdge()->asRiseFall() == RiseFall::rise())
|
|
return "hold_rising";
|
|
else
|
|
return "hold_falling";
|
|
}
|
|
else if (role == TimingRole::nonSeqSetup()) {
|
|
const TimingArc *arc = arc_set->arcs()[0];
|
|
if (arc->fromEdge()->asRiseFall() == RiseFall::rise())
|
|
return "non_seq_setup_rising";
|
|
else
|
|
return "non_seq_setup_falling";
|
|
}
|
|
else if (role == TimingRole::nonSeqHold()) {
|
|
const TimingArc *arc = arc_set->arcs()[0];
|
|
if (arc->fromEdge()->asRiseFall() == RiseFall::rise())
|
|
return "non_seq_hold_rising";
|
|
else
|
|
return "non_seq_hold_falling";
|
|
}
|
|
else if (role == TimingRole::clockTreePathMin())
|
|
return "min_clock_tree_path";
|
|
else if (role == TimingRole::clockTreePathMax())
|
|
return "max_clock_tree_path";
|
|
else if (role == TimingRole::width())
|
|
return "min_pulse_width";
|
|
else {
|
|
report_->error(1343, "%s/%s/%s timing arc type %s not supported.",
|
|
library_->name(),
|
|
arc_set->to()->libertyCell()->name(),
|
|
arc_set->to()->name(),
|
|
role->to_string().c_str());
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
} // namespace
|