Merge remote-tracking branch 'parallax/master'

This commit is contained in:
Matt Liberty 2025-07-31 22:24:43 +00:00
commit a74903cd3c
35 changed files with 399 additions and 136 deletions

View File

@ -586,16 +586,22 @@ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/sta DESTINATION include)
################################################################
add_custom_target(sta_tags etags -o TAGS
add_custom_target(sta_tags
COMMAND etags -o TAGS
${STA_SOURCE}
*/*.hh
include/sta/*.hh
${CMAKE_CURRENT_BINARY_DIR}/include/sta/*.hh
${SWIG_FILES}
${SWIG_FILES}
# Append tcl tags.
# Note that the regexp used for tcl files is broken in etags so provide one that works.
COMMAND etags -a -o TAGS --language=none
--regex='/^[ \\t]*proc[ \\t]+[a-zA-Z0-9_]+/'
${STA_TCL_FILES}
${SWIG_TCL_FILES}
WORKING_DIRECTORY ${STA_HOME}
)
)
add_custom_command(
TARGET OpenSTA

View File

@ -228,6 +228,16 @@ Set these variables before using cmake to cirumvent the Xcode versions.
Homebrew does not support tclreadline, but the macports system does
(see https://www.macports.org).
## Install using a package manager
### Guix
OpenSTA is available in the [default repositories](https://hpc.guix.info/package/opensta):
```
guix install opensta
```
## Bug Reports
Use the Issues tab on the github repository to report bugs.

View File

@ -297,7 +297,7 @@ GraphDelayCalc::seedInvalidDelays()
void
GraphDelayCalc::seedRootSlews()
{
for (Vertex *vertex : *levelize_->roots())
for (Vertex *vertex : levelize_->roots())
seedRootSlew(vertex, arc_delay_calc_);
}
@ -1597,28 +1597,39 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge,
void
GraphDelayCalc::minPeriod(const Pin *pin,
const Corner *corner,
// Return values.
float &min_period,
bool &exists)
{
exists = false;
const MinMax *min_max = MinMax::max();
for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) {
// Sdf annotation.
float min_period1 = 0.0;
bool exists1 = false;
graph_->periodCheckAnnotation(pin, dcalc_ap->index(),
min_period, exists);
if (exists1
&& (!exists || min_period1 < min_period)) {
min_period = min_period1;
const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
// Sdf annotation.
float min_period1 = 0.0;
bool exists1 = false;
graph_->periodCheckAnnotation(pin, dcalc_ap->index(),
min_period, exists);
if (exists1
&& (!exists || min_period1 < min_period)) {
min_period = min_period1;
exists = true;
}
if (!exists) {
// Liberty timing group timing_type minimum_period.
Vertex *vertex = graph_->pinLoadVertex(pin);
Edge *edge;
TimingArc *arc;
graph_->minPeriodArc(vertex, RiseFall::rise(), edge, arc);
if (edge) {
exists = true;
min_period = delayAsFloat(graph_->arcDelay(edge, arc, dcalc_ap->index()));
}
}
if (!exists) {
// Liberty port min_period attribute.
LibertyPort *port = network_->libertyPort(pin);
if (port) {
// Liberty library.
Instance *inst = network_->instance(pin);
OperatingConditions *op_cond = sdc_->operatingConditions(min_max);
const Pvt *pvt = inst ? sdc_->pvt(inst, min_max) : nullptr;

View File

@ -871,6 +871,30 @@ Graph::minPulseWidthArc(Vertex *vertex,
arc = nullptr;
}
void
Graph::minPeriodArc(Vertex *vertex,
const RiseFall *rf,
// Return values.
Edge *&edge,
TimingArc *&arc)
{
VertexOutEdgeIterator edge_iter(vertex, this);
while (edge_iter.hasNext()) {
edge = edge_iter.next();
TimingArcSet *arc_set = edge->timingArcSet();
if (arc_set->role() == TimingRole::period()) {
for (TimingArc *arc1 : arc_set->arcs()) {
if (arc1->fromEdge()->asRiseFall() == rf) {
arc = arc1;
return;
}
}
}
}
edge = nullptr;
arc = nullptr;
}
////////////////////////////////////////////////////////////////
void

View File

@ -171,6 +171,11 @@ public:
// Return values.
Edge *&edge,
TimingArc *&arc);
void minPeriodArc(Vertex *vertex,
const RiseFall *rf,
// Return values.
Edge *&edge,
TimingArc *&arc);
// Sdf period check annotation.
void periodCheckAnnotation(const Pin *pin,
DcalcAPIndex ap_index,

View File

@ -114,8 +114,10 @@ public:
ArcDelayCalc *arc_delay_calc);
// Precedence:
// SDF annotation
// Liberty library
// Liberty port timing group timing_type minimum_period.
// Liberty port min_period attribute.
void minPeriod(const Pin *pin,
const Corner *corner,
// Return values.
float &min_period,
bool &exists);

View File

@ -26,6 +26,7 @@
#include <mutex>
#include <atomic>
#include <functional>
#include "MinMax.hh"
#include "RiseFallMinMax.hh"
@ -873,6 +874,16 @@ protected:
void addScaledPort(OperatingConditions *op_cond,
LibertyPort *scaled_port);
RiseFallMinMax clkTreeDelays1() const;
void setMemberFlag(bool value,
const std::function<void(LibertyPort*, bool)> &setter);
void setMemberFloat(float value,
const std::function<void(LibertyPort*, float)> &setter);
void setMemberMinMaxFloat(float value,
const MinMax *min_max,
const std::function<void(LibertyPort*,
float,
const MinMax *)> &setter);
LibertyCell *liberty_cell_;
BusDcl *bus_dcl_;

View File

@ -994,6 +994,14 @@ LibertyCell::findLibertyPortsMatching(PatternMatch *pattern) const
LibertyPort *port = port_iter.next();
if (pattern->match(port->name()))
matches.push_back(port);
if (port->hasMembers()) {
LibertyPortMemberIterator port_iter2(port);
while (port_iter2.hasNext()) {
LibertyPort *port2 = port_iter2.next();
if (pattern->match(port2->name()))
matches.push_back(port2);
}
}
}
return matches;
}
@ -2347,7 +2355,7 @@ void
LibertyPort::setFunction(FuncExpr *func)
{
function_ = func;
if (is_bus_ || is_bundle_) {
if (hasMembers()) {
LibertyPortMemberIterator member_iter(this);
int bit_offset = 0;
while (member_iter.hasNext()) {
@ -2388,6 +2396,7 @@ LibertyPort::setSlewLimit(float slew,
const MinMax *min_max)
{
slew_limit_.setValue(min_max, slew);
setMemberMinMaxFloat(slew, min_max, &LibertyPort::setSlewLimit);
}
void
@ -2404,6 +2413,7 @@ LibertyPort::setCapacitanceLimit(float cap,
const MinMax *min_max)
{
cap_limit_.setValue(min_max, cap);
setMemberMinMaxFloat(cap, min_max, &LibertyPort::setCapacitanceLimit);
}
void
@ -2420,6 +2430,7 @@ LibertyPort::setFanoutLoad(float fanout_load)
{
fanout_load_ = fanout_load;
fanout_load_exists_ = true;
setMemberFloat(fanout_load, &LibertyPort::setFanoutLoad);
}
void
@ -2470,6 +2481,13 @@ LibertyPort::setMinPeriod(float min_period)
{
min_period_ = min_period;
min_period_exists_ = true;
if (hasMembers()) {
LibertyPortMemberIterator member_iter(this);
while (member_iter.hasNext()) {
LibertyPort *port_bit = member_iter.next();
port_bit->setMinPeriod(min_period);
}
}
}
void
@ -2489,6 +2507,13 @@ LibertyPort::setMinPulseWidth(const RiseFall *hi_low,
int hi_low_index = hi_low->index();
min_pulse_width_[hi_low_index] = min_width;
min_pulse_width_exists_ |= (1 << hi_low_index);
if (hasMembers()) {
LibertyPortMemberIterator member_iter(this);
while (member_iter.hasNext()) {
LibertyPort *port_bit = member_iter.next();
port_bit->setMinPulseWidth(hi_low, min_width);
}
}
}
bool
@ -2538,24 +2563,28 @@ void
LibertyPort::setIsClock(bool is_clk)
{
is_clk_ = is_clk;
setMemberFlag(is_clk, &LibertyPort::setIsClock);
}
void
LibertyPort::setIsRegClk(bool is_clk)
{
is_reg_clk_ = is_clk;
setMemberFlag(is_clk, &LibertyPort::setIsRegClk);
}
void
LibertyPort::setIsRegOutput(bool is_reg_out)
{
is_reg_output_ = is_reg_out;
setMemberFlag(is_reg_out, &LibertyPort::setIsRegOutput);
}
void
LibertyPort::setIsLatchData(bool is_latch_data)
{
is_latch_data_ = is_latch_data;
setMemberFlag(is_latch_data, &LibertyPort::setIsLatchData);
}
void
@ -2592,24 +2621,28 @@ void
LibertyPort::setIsolationCellData(bool isolation_cell_data)
{
isolation_cell_data_ = isolation_cell_data;
setMemberFlag(isolation_cell_data, &LibertyPort::setIsolationCellData);
}
void
LibertyPort::setIsolationCellEnable(bool isolation_cell_enable)
{
isolation_cell_enable_ = isolation_cell_enable;
setMemberFlag(isolation_cell_enable, &LibertyPort::setIsolationCellEnable);
}
void
LibertyPort::setLevelShifterData(bool level_shifter_data)
{
level_shifter_data_ = level_shifter_data;
setMemberFlag(level_shifter_data, &LibertyPort::setLevelShifterData);
}
void
LibertyPort::setIsSwitch(bool is_switch)
{
is_switch_ = is_switch;
setMemberFlag(is_switch, &LibertyPort::setIsSwitch);
}
void
@ -2821,6 +2854,50 @@ LibertyPort::setClkTreeDelay(const TableModel *model,
clk_tree_delay_[from_rf->index()][to_rf->index()][min_max->index()] = model;
}
void
LibertyPort::setMemberFlag(bool value,
const std::function<void(LibertyPort*,
bool)> &setter)
{
if (hasMembers()) {
LibertyPortMemberIterator member_iter(this);
while (member_iter.hasNext()) {
LibertyPort *port_bit = member_iter.next();
setter(port_bit, value);
}
}
}
void
LibertyPort::setMemberFloat(float value,
const std::function<void(LibertyPort*,
float)> &setter)
{
if (hasMembers()) {
LibertyPortMemberIterator member_iter(this);
while (member_iter.hasNext()) {
LibertyPort *port_bit = member_iter.next();
setter(port_bit, value);
}
}
}
void
LibertyPort::setMemberMinMaxFloat(float value,
const MinMax *min_max,
const std::function<void(LibertyPort*,
float,
const MinMax *)> &setter)
{
if (hasMembers()) {
LibertyPortMemberIterator member_iter(this);
while (member_iter.hasNext()) {
LibertyPort *port_bit = member_iter.next();
setter(port_bit, value, min_max);
}
}
}
////////////////////////////////////////////////////////////////
LibertyPortSeq

View File

@ -65,6 +65,8 @@ proc_redirect report_lib_cell {
}
proc report_lib_cell_ { cell corner } {
global sta_report_default_digits
set lib [$cell liberty_library]
report_line "Cell [get_name $cell]"
report_line "Library [get_name $lib]"

View File

@ -289,6 +289,8 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell,
return makeMinPulseWidthArcs(cell, from_port, to_port, related_out,
TimingRole::width(), attrs);
case TimingType::minimum_period:
return makeMinPulseWidthArcs(cell, from_port, to_port, related_out,
TimingRole::period(), attrs);
case TimingType::nochange_high_high:
case TimingType::nochange_high_low:
case TimingType::nochange_low_high:

View File

@ -78,7 +78,7 @@ TOKEN ({ALPHA}|{DIGIT}|_)({ALPHA}|{DIGIT}|[._\-])*(:({ALPHA}|{DIGIT}|_)+)?
/* bus_naming_style : %s[%d] ; */
BUS_STYLE "%s"{BUS_LEFT}"%d"{BUS_RIGHT}
PUNCTUATION [,\:;|(){}+*&!'=]
TOKEN_END {PUNCTUATION}|[ \t\r\n]
TOKEN_END {PUNCTUATION}|[ \t\r\n]|\\{EOL}
EOL \r?\n
%%

View File

@ -4251,6 +4251,7 @@ LibertyReader::endTiming(LibertyGroup *group)
TimingType timing_type = timing_->attrs()->timingType();
if (timing_->relatedPortNames() == nullptr
&& !(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, group, "timing group missing related_pin/related_bus_pin.");

View File

@ -820,6 +820,20 @@ ConcreteParasitics::deleteParasitics(const Pin *drvr_pin,
}
}
void
ConcreteParasitics::deleteParasitics(const Pin *drvr_pin)
{
ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin];
if (parasitics) {
int ap_count = corners_->parasiticAnalysisPtCount();
int ap_rf_count = ap_count * RiseFall::index_count;
for (int i = 0; i < ap_rf_count; i++) {
delete parasitics[i];
parasitics[i] = nullptr;
}
}
}
void
ConcreteParasitics::deleteParasitics(const Net *net,
const ParasiticAnalysisPt *ap)
@ -1244,7 +1258,7 @@ ConcreteParasitics::makeParasiticNetwork(const Net *net,
delete parasitic;
if (net) {
for (const Pin *drvr_pin : *network_->drivers(net))
deleteParasitics(drvr_pin, ap);
deleteParasitics(drvr_pin);
}
}
parasitic = new ConcreteParasiticNetwork(net, includes_pin_caps, network_);

View File

@ -53,6 +53,7 @@ public:
const ParasiticAnalysisPt *ap) override;
void deleteParasitics(const Pin *drvr_pin,
const ParasiticAnalysisPt *ap) override;
void deleteParasitics(const Pin *drvr_pin);
bool isReducedParasiticNetwork(const Parasitic *parasitic) const override;
void setIsReducedParasiticNetwork(Parasitic *parasitic,

View File

@ -38,30 +38,11 @@ define_cmd_args "read_spef" \
proc_redirect read_spef {
parse_key_args "read_spef" args \
keys {-path -coupling_reduction_factor -reduce_to -corner} \
flags {-min -max -increment -pin_cap_included -keep_capacitive_coupling \
-reduce -delete_after_reduce -quiet -save}
keys {-path -coupling_reduction_factor -corner -name} \
flags {-min -max -increment -pin_cap_included -keep_capacitive_coupling -reduce}
check_argc_eq1 "read_spef" $args
set reduce [info exists flags(-reduce)]
if { [info exists flags(-quiet)] } {
# deprecated 2024-02-08
sta_warn 272 "read_spef -quiet is deprecated."
}
if { [info exists keys(-reduce_to)] } {
# deprecated 2024-02-08
sta_warn 273 "read_spef -reduce_to is deprecated. Use -reduce instead."
set reduce 1
}
if { [info exists flags(-delete_after_reduce)] } {
# deprecated 2024-02-08
sta_warn 274 "read_spef -delete_after_reduce is deprecated."
}
if { [info exists flags(-save)] } {
# deprecated 2024-02-08
sta_warn 275 "read_spef -save is deprecated."
}
set instance [top_instance]
if [info exists keys(-path)] {
set path $keys(-path)

View File

@ -104,6 +104,17 @@ Power::Power(StaState *sta) :
{
}
void
Power::clear()
{
global_activity_.init();
input_activity_.init();
user_activity_map_.clear();
seq_activity_map_.clear();
activity_map_.clear();
activities_valid_ = false;
}
void
Power::setGlobalActivity(float density,
float duty)
@ -737,7 +748,7 @@ Power::ensureActivities()
void
Power::seedActivities(BfsFwdIterator &bfs)
{
for (Vertex *vertex : *levelize_->roots()) {
for (Vertex *vertex : levelize_->roots()) {
const Pin *pin = vertex->pin();
// Clock activities are baked in.
if (!sdc_->isLeafPinClock(pin)

View File

@ -75,6 +75,7 @@ class Power : public StaState
{
public:
Power(StaState *sta);
void clear();
void power(const Corner *corner,
// Return values.
PowerResult &total,

View File

@ -2909,6 +2909,9 @@ proc set_load { args } {
parse_port_net_args [lindex $args 1] ports nets
if { $ports != {} } {
if { $subtract_pin_load } {
sta_warn 486 "-subtract_pin_load not allowed for port objects."
}
# -pin_load is the default.
if { $pin_load || (!$pin_load && !$wire_load) } {
foreach port $ports {
@ -2916,7 +2919,7 @@ proc set_load { args } {
}
} elseif { $wire_load } {
foreach port $ports {
set_port_ext_wire_cap $port $subtract_pin_load $rf $corner $min_max $cap
set_port_ext_wire_cap $port 0 $rf $corner $min_max $cap
}
}
}

View File

@ -546,7 +546,7 @@ SdfWriter::writeTimingChecks(const Instance *inst,
}
float min_period;
bool exists;
graph_delay_calc_->minPeriod(pin, min_period, exists);
graph_delay_calc_->minPeriod(pin, corner_, min_period, exists);
if (exists) {
ensureTimingCheckheaders(check_header, inst, inst_header);
writePeriodCheck(pin, min_period);

View File

@ -39,12 +39,21 @@ namespace sta {
class MinPeriodCheckVisitor
{
public:
MinPeriodCheckVisitor() {}
MinPeriodCheckVisitor(const Corner *corner);
virtual ~MinPeriodCheckVisitor() {}
virtual void visit(MinPeriodCheck &check,
StaState *sta) = 0;
const Corner *corner() { return corner_; }
protected:
const Corner *corner_;
};
MinPeriodCheckVisitor::MinPeriodCheckVisitor(const Corner *corner) :
corner_(corner)
{
}
CheckMinPeriods::CheckMinPeriods(StaState *sta) :
sta_(sta)
{
@ -64,7 +73,8 @@ CheckMinPeriods::clear()
class MinPeriodViolatorsVisitor : public MinPeriodCheckVisitor
{
public:
MinPeriodViolatorsVisitor(MinPeriodCheckSeq &checks);
MinPeriodViolatorsVisitor(const Corner *corner,
MinPeriodCheckSeq &checks);
virtual void visit(MinPeriodCheck &check,
StaState *sta);
@ -72,7 +82,9 @@ private:
MinPeriodCheckSeq &checks_;
};
MinPeriodViolatorsVisitor::MinPeriodViolatorsVisitor(MinPeriodCheckSeq &checks):
MinPeriodViolatorsVisitor::MinPeriodViolatorsVisitor(const Corner *corner,
MinPeriodCheckSeq &checks):
MinPeriodCheckVisitor(corner),
checks_(checks)
{
}
@ -86,10 +98,10 @@ MinPeriodViolatorsVisitor::visit(MinPeriodCheck &check,
}
MinPeriodCheckSeq &
CheckMinPeriods::violations()
CheckMinPeriods::violations(const Corner *corner)
{
clear();
MinPeriodViolatorsVisitor visitor(checks_);
MinPeriodViolatorsVisitor visitor(corner, checks_);
visitMinPeriodChecks(&visitor);
sort(checks_, MinPeriodSlackLess(sta_));
return checks_;
@ -113,16 +125,17 @@ CheckMinPeriods::visitMinPeriodChecks(Vertex *vertex,
{
Search *search = sta_->search();
GraphDelayCalc *graph_dcalc = sta_->graphDelayCalc();
const Corner *corner = visitor->corner();
Pin *pin = vertex->pin();
float min_period;
bool exists;
graph_dcalc->minPeriod(pin, min_period, exists);
graph_dcalc->minPeriod(pin, corner, min_period, exists);
if (exists) {
const ClockSet clks = search->clocks(vertex);
ClockSet::ConstIterator clk_iter(clks);
while (clk_iter.hasNext()) {
Clock *clk = clk_iter.next();
MinPeriodCheck check(pin, clk);
MinPeriodCheck check(pin, clk, corner);
visitor->visit(check, sta_);
}
}
@ -133,16 +146,17 @@ CheckMinPeriods::visitMinPeriodChecks(Vertex *vertex,
class MinPeriodSlackVisitor : public MinPeriodCheckVisitor
{
public:
MinPeriodSlackVisitor();
virtual void visit(MinPeriodCheck &check,
StaState *sta);
MinPeriodSlackVisitor(const Corner *corner);
void visit(MinPeriodCheck &check,
StaState *sta) override;
MinPeriodCheck *minSlackCheck();
private:
MinPeriodCheck *min_slack_check_;
};
MinPeriodSlackVisitor::MinPeriodSlackVisitor() :
MinPeriodSlackVisitor::MinPeriodSlackVisitor(const Corner *corner) :
MinPeriodCheckVisitor(corner),
min_slack_check_(nullptr)
{
}
@ -167,10 +181,10 @@ MinPeriodSlackVisitor::minSlackCheck()
}
MinPeriodCheck *
CheckMinPeriods::minSlackCheck()
CheckMinPeriods::minSlackCheck(const Corner *corner)
{
clear();
MinPeriodSlackVisitor visitor;
MinPeriodSlackVisitor visitor(corner);
visitMinPeriodChecks(&visitor);
MinPeriodCheck *check = visitor.minSlackCheck();
// Save check for cleanup.
@ -181,16 +195,18 @@ CheckMinPeriods::minSlackCheck()
////////////////////////////////////////////////////////////////
MinPeriodCheck::MinPeriodCheck(Pin *pin,
Clock *clk) :
Clock *clk,
const Corner *corner) :
pin_(pin),
clk_(clk)
clk_(clk),
corner_(corner)
{
}
MinPeriodCheck *
MinPeriodCheck::copy()
{
return new MinPeriodCheck(pin_, clk_);
return new MinPeriodCheck(pin_, clk_, corner_);
}
float
@ -205,7 +221,7 @@ MinPeriodCheck::minPeriod(const StaState *sta) const
GraphDelayCalc *graph_dcalc = sta->graphDelayCalc();
float min_period;
bool exists;
graph_dcalc->minPeriod(pin_, min_period, exists);
graph_dcalc->minPeriod(pin_, corner_, min_period, exists);
return min_period;
}

View File

@ -38,12 +38,12 @@ class MinPeriodCheckVisitor;
class CheckMinPeriods
{
public:
explicit CheckMinPeriods(StaState *sta);
CheckMinPeriods(StaState *sta);
~CheckMinPeriods();
void clear();
MinPeriodCheckSeq &violations();
MinPeriodCheckSeq &violations(const Corner *corner);
// Min period check with the least slack.
MinPeriodCheck *minSlackCheck();
MinPeriodCheck *minSlackCheck(const Corner *corner);
protected:
void visitMinPeriodChecks(MinPeriodCheckVisitor *visitor);
@ -57,7 +57,9 @@ protected:
class MinPeriodCheck
{
public:
MinPeriodCheck(Pin *pin, Clock *clk);
MinPeriodCheck(Pin *pin,
Clock *clk,
const Corner *corner);
MinPeriodCheck *copy();
Pin *pin() const { return pin_; }
Clock *clk() const { return clk_; }
@ -68,17 +70,18 @@ public:
private:
Pin *pin_;
Clock *clk_;
const Corner *corner_;
};
class MinPeriodSlackLess
{
public:
explicit MinPeriodSlackLess(StaState *sta);
MinPeriodSlackLess(StaState *sta);
bool operator()(const MinPeriodCheck *check1,
const MinPeriodCheck *check2) const;
private:
const StaState *sta_;
const StaState *sta_;
};
} // namespace

View File

@ -38,7 +38,7 @@ class MinPulseWidthCheckVisitor;
class CheckMinPulseWidths
{
public:
explicit CheckMinPulseWidths(StaState *sta);
CheckMinPulseWidths(StaState *sta);
~CheckMinPulseWidths();
void clear();
// Min pulse width checks for pins.
@ -96,7 +96,7 @@ protected:
class MinPulseWidthSlackLess
{
public:
explicit MinPulseWidthSlackLess(const StaState *sta);
MinPulseWidthSlackLess(const StaState *sta);
bool operator()(const MinPulseWidthCheck *check1,
const MinPulseWidthCheck *check2) const;

View File

@ -230,14 +230,14 @@ void
CheckTiming::checkUnconstrainedEndpoints()
{
PinSet unconstrained_ends(network_);
checkUnconstraintedOutputs(unconstrained_ends);
checkUnconstrainedOutputs(unconstrained_ends);
checkUnconstrainedSetups(unconstrained_ends);
pushPinErrors("Warning: There %is %d unconstrained endpoint%s.",
unconstrained_ends);
}
void
CheckTiming::checkUnconstraintedOutputs(PinSet &unconstrained_ends)
CheckTiming::checkUnconstrainedOutputs(PinSet &unconstrained_ends)
{
Instance *top_inst = network_->topInstance();
InstancePinIterator *pin_iter = network_->pinIterator(top_inst);

View File

@ -59,7 +59,7 @@ protected:
void checkUnconstrainedEndpoints();
bool hasClkedArrival(Vertex *vertex);
void checkNoOutputDelay(PinSet &ends);
void checkUnconstraintedOutputs(PinSet &unconstrained_ends);
void checkUnconstrainedOutputs(PinSet &unconstrained_ends);
void checkUnconstrainedSetups(PinSet &unconstrained_ends);
void checkLoops();
bool hasClkedDepature(Pin *pin);

View File

@ -197,7 +197,8 @@ Corners::makeDcalcAnalysisPts(Corner *corner)
switch (sdc_->analysisType()) {
case AnalysisType::single:
corner->setDcalcAnalysisPtcount(1);
makeDcalcAnalysisPt(corner, MinMax::max(), MinMax::min());
max_ap = makeDcalcAnalysisPt(corner, MinMax::max(), MinMax::min());
max_ap->setCheckClkSlewIndex(max_ap->index());
break;
case AnalysisType::bc_wc:
corner->setDcalcAnalysisPtcount(2);

View File

@ -51,16 +51,15 @@ Levelize::Levelize(StaState *sta) :
levels_valid_(false),
max_level_(0),
level_space_(10),
roots_(new VertexSet(graph_)),
relevelize_from_(new VertexSet(graph_)),
max_incremental_level_(100),
roots_(graph_),
relevelize_from_(graph_),
observer_(nullptr)
{
}
Levelize::~Levelize()
{
delete roots_;
delete relevelize_from_;
delete observer_;
loops_.deleteContents();
}
@ -83,8 +82,8 @@ Levelize::clear()
{
levelized_ = false;
levels_valid_ = false;
roots_->clear();
relevelize_from_->clear();
roots_.clear();
relevelize_from_.clear();
clearLoopEdges();
loops_.deleteContentsClear();
loop_edges_.clear();
@ -106,7 +105,8 @@ void
Levelize::ensureLevelized()
{
if (!levels_valid_) {
if (levelized_)
if (levelized_
&& relevelize_from_.size() < max_incremental_level_)
relevelize();
else
levelize();
@ -151,6 +151,7 @@ Levelize::levelize()
vertex->setVisited(false);
vertex->setOnPath(false);
}
relevelize_from_.clear();
levelized_ = true;
levels_valid_ = true;
stats.report("Levelize");
@ -159,7 +160,7 @@ Levelize::levelize()
void
Levelize::findRoots()
{
roots_->clear();
roots_.clear();
VertexIterator vertex_iter(graph_);
while (vertex_iter.hasNext()) {
Vertex *vertex = vertex_iter.next();
@ -167,17 +168,17 @@ Levelize::findRoots()
debugPrint(debug_, "levelize", 2, "root %s%s",
vertex->to_string(this).c_str(),
hasFanout(vertex) ? " fanout" : "");
roots_->insert(vertex);
roots_.insert(vertex);
}
}
if (debug_->check("levelize", 1)) {
size_t fanout_roots = 0;
for (Vertex *root : *roots_) {
for (Vertex *root : roots_) {
if (hasFanout(root))
fanout_roots++;
}
debugPrint(debug_, "levelize", 1, "Found %zu roots %zu with fanout",
roots_->size(),
roots_.size(),
fanout_roots);
}
}
@ -251,7 +252,7 @@ VertexSeq
Levelize::sortedRootsWithFanout()
{
VertexSeq roots;
for (Vertex *root : *roots_) {
for (Vertex *root : roots_) {
if (hasFanout(root))
roots.push_back(root);
}
@ -322,7 +323,7 @@ Levelize::findCycleBackEdges()
stack.emplace(vertex, new VertexOutEdgeIterator(vertex, graph_));
EdgeSet back_edges = findBackEdges(path, stack);
for (Edge *back_edge : back_edges)
roots_->insert(back_edge->from(graph_));
roots_.insert(back_edge->from(graph_));
back_edge_count += back_edges.size();
}
}
@ -378,7 +379,7 @@ Levelize::findTopologicalOrder()
}
std::deque<Vertex*> queue;
for (Vertex *root : *roots_)
for (Vertex *root : roots_)
queue.push_back(root);
VertexSeq topo_order;
@ -501,10 +502,11 @@ Levelize::reportPath(EdgeSeq &path) const
void
Levelize::assignLevels(VertexSeq &topo_sorted)
{
for (Vertex *root : *roots_)
for (Vertex *root : roots_)
setLevel(root, 0);
for (Vertex *vertex : topo_sorted) {
if (vertex->level() != -1) {
if (vertex->level() != -1
&& search_pred_.searchFrom(vertex)) {
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
@ -557,7 +559,7 @@ Levelize::setLevel(Vertex *vertex,
vertex->setLevel(level);
max_level_ = max(level, max_level_);
if (level >= Graph::vertex_level_max)
criticalError(616, "maximum logic level exceeded");
report_->critical(616, "maximum logic level exceeded");
}
void
@ -580,9 +582,9 @@ Levelize::invalidFrom(Vertex *vertex)
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
Vertex *from_vertex = edge->from(graph_);
relevelize_from_->insert(from_vertex);
relevelize_from_.insert(from_vertex);
}
relevelize_from_->insert(vertex);
relevelize_from_.insert(vertex);
levels_valid_ = false;
}
}
@ -591,8 +593,8 @@ void
Levelize::deleteVertexBefore(Vertex *vertex)
{
if (levelized_) {
roots_->erase(vertex);
relevelize_from_->erase(vertex);
roots_.erase(vertex);
relevelize_from_.erase(vertex);
}
}
@ -600,9 +602,9 @@ void
Levelize::relevelizeFrom(Vertex *vertex)
{
if (levelized_) {
debugPrint(debug_, "levelize", 1, "invalid relevelize from %s",
debugPrint(debug_, "levelize", 1, "relevelize from %s",
vertex->to_string(this).c_str());
relevelize_from_->insert(vertex);
relevelize_from_.insert(vertex);
levels_valid_ = false;
}
}
@ -633,23 +635,22 @@ Levelize::deleteEdgeBefore(Edge *edge)
void
Levelize::relevelize()
{
for (Vertex *vertex : *relevelize_from_) {
for (Vertex *vertex : relevelize_from_) {
debugPrint(debug_, "levelize", 1, "relevelize from %s",
vertex->to_string(this).c_str());
if (search_pred_.searchFrom(vertex)) {
if (isRoot(vertex)) {
setLevelIncr(vertex, 0);
roots_->insert(vertex);
}
VertexSet visited(graph_);
if (isRoot(vertex))
roots_.insert(vertex);
VertexSet path_vertices(graph_);
EdgeSeq path;
visit(vertex, nullptr, vertex->level(), 1, visited, path_vertices, path);
visit(vertex, nullptr, vertex->level(), 1, path_vertices, path);
}
}
ensureLatchLevels();
levels_valid_ = true;
relevelize_from_->clear();
relevelize_from_.clear();
checkLevels();
}
void
@ -657,14 +658,11 @@ Levelize::visit(Vertex *vertex,
Edge *from,
Level level,
Level level_space,
VertexSet &visited,
VertexSet &path_vertices,
EdgeSeq &path)
{
Pin *from_pin = vertex->pin();
setLevelIncr(vertex, level);
level += level_space;
visited.insert(vertex);
path_vertices.insert(vertex);
if (from)
path.push_back(from);
@ -679,9 +677,9 @@ Levelize::visit(Vertex *vertex,
if (path_vertices.find(to_vertex) != path_vertices.end())
// Back edges form feedback loops.
recordLoop(edge, path);
else if (visited.find(to_vertex) == visited.end()
&& to_vertex->level() < level)
visit(to_vertex, edge, level, level_space, visited, path_vertices, path);
else if (to_vertex->level() <= level)
visit(to_vertex, edge, level+level_space, level_space,
path_vertices, path);
}
if (edge->role() == TimingRole::latchDtoQ())
latch_d_to_q_edges_.insert(edge);
@ -691,9 +689,9 @@ Levelize::visit(Vertex *vertex,
&& !vertex->isBidirectDriver()) {
Vertex *to_vertex = graph_->pinDrvrVertex(from_pin);
if (search_pred_.searchTo(to_vertex)
&& (visited.find(to_vertex) == visited.end()
|| to_vertex->level() < level))
visit(to_vertex, nullptr, level, level_space, visited, path_vertices, path);
&& (to_vertex->level() <= level))
visit(to_vertex, nullptr, level+level_space, level_space,
path_vertices, path);
}
}
path_vertices.erase(vertex);
@ -724,6 +722,34 @@ Levelize::setLevelIncr(Vertex *vertex,
criticalError(617, "maximum logic level exceeded");
}
void
Levelize::checkLevels()
{
VertexIterator vertex_iter(graph_);
while (vertex_iter.hasNext()) {
Vertex *vertex = vertex_iter.next();
if (search_pred_.searchTo(vertex)) {
Level level = vertex->level();
VertexInEdgeIterator edge_iter1(vertex, graph_);
while (edge_iter1.hasNext()) {
Edge *edge = edge_iter1.next();
Vertex *from_vertex = edge->from(graph_);
Level from_level = from_vertex->level();
if (search_pred_.searchFrom(from_vertex)
&& search_pred_.searchThru(edge)
&& from_level >= level
// Loops with no entry edges are all level zero.
&& !(from_level == 0 && level == 0))
report_->warn(617, "level check failed %s %d -> %s %d",
from_vertex->name(network_),
from_vertex->level(),
vertex->name(network_),
level);
}
}
}
}
////////////////////////////////////////////////////////////////
GraphLoop::GraphLoop(EdgeSeq *edges) :

View File

@ -28,7 +28,7 @@
#include "NetworkClass.hh"
#include "SdcClass.hh"
#include "GraphClass.hh"
#include "Graph.hh"
#include "SearchPred.hh"
#include "StaState.hh"
@ -58,7 +58,7 @@ public:
void deleteEdgeBefore(Edge *edge);
int maxLevel() const { return max_level_; }
// Vertices with no fanin edges.
VertexSet *roots() { return roots_; }
VertexSet &roots() { return roots_; }
bool isRoot(Vertex *vertex);
bool hasFanout(Vertex *vertex);
// Reset to virgin state.
@ -69,6 +69,7 @@ public:
GraphLoopSeq &loops() { return loops_; }
// Set the observer for level changes.
void setObserver(LevelizeObserver *observer);
void checkLevels();
protected:
void levelize();
@ -91,7 +92,6 @@ protected:
Edge *from,
Level level,
Level level_space,
VertexSet &visited,
VertexSet &path_vertices,
EdgeSeq &path);
void setLevel(Vertex *vertex,
@ -107,8 +107,9 @@ protected:
bool levels_valid_;
Level max_level_;
Level level_space_;
VertexSet *roots_;
VertexSet *relevelize_from_;
size_t max_incremental_level_;
VertexSet roots_;
VertexSet relevelize_from_;
GraphLoopSeq loops_;
EdgeSet loop_edges_;
EdgeSet disabled_loop_edges_;

View File

@ -872,6 +872,8 @@ Properties::getProperty(const LibertyPort *port,
float cap = port->capacitance(RiseFall::rise(), MinMax::max());
return capacitancePropertyValue(cap);
}
else if (property == "is_clock")
return PropertyValue(port->isClock());
else if (property == "is_register_clock")
return PropertyValue(port->isRegClk());
@ -989,6 +991,10 @@ Properties::getProperty(const Pin *pin,
return PropertyValue(network->isHierarchical(pin));
else if (property == "is_port")
return PropertyValue(network->isTopLevelPort(pin));
else if (property == "is_clock") {
const LibertyPort *port = network->libertyPort(pin);
return PropertyValue(port->isClock());
}
else if (property == "is_register_clock") {
const LibertyPort *port = network->libertyPort(pin);
return PropertyValue(port && port->isRegClk());

View File

@ -1671,7 +1671,7 @@ Search::makeUnclkedPaths(Vertex *vertex,
void
Search::findRootVertices(VertexSet &vertices)
{
for (Vertex *vertex : *levelize_->roots()) {
for (Vertex *vertex : levelize_->roots()) {
const Pin *pin = vertex->pin();
if (!sdc_->isLeafPinClock(pin)
&& !sdc_->hasInputDelay(pin)

View File

@ -586,6 +586,7 @@ Sta::clear()
parasitics_->clear();
graph_delay_calc_->clear();
sim_->clear();
power_->clear();
if (check_min_pulse_widths_)
check_min_pulse_widths_->clear();
if (check_min_periods_)
@ -4407,7 +4408,7 @@ Sta::connectDrvrPinAfter(Vertex *vertex)
graph_delay_calc_->delayInvalid(vertex);
search_->requiredInvalid(vertex);
search_->endpointInvalid(vertex);
levelize_->invalidFrom(vertex);
levelize_->relevelizeFrom(vertex);
clk_network_->connectPinAfter(pin);
}
@ -4422,11 +4423,11 @@ Sta::connectLoadPinAfter(Vertex *vertex)
graph_delay_calc_->delayInvalid(from_vertex);
search_->requiredInvalid(from_vertex);
sdc_->clkHpinDisablesChanged(from_vertex->pin());
levelize_->relevelizeFrom(from_vertex);
}
Pin *pin = vertex->pin();
sdc_->clkHpinDisablesChanged(pin);
graph_delay_calc_->delayInvalid(vertex);
levelize_->invalidFrom(vertex);
search_->arrivalInvalid(vertex);
search_->endpointInvalid(vertex);
clk_network_->connectPinAfter(pin);
@ -4584,6 +4585,7 @@ Sta::deletePinBefore(const Pin *pin)
}
levelize_->deleteEdgeBefore(edge);
}
// Deletes edges to/from vertex also.
graph_->deleteVertex(vertex);
}
}
@ -4606,6 +4608,7 @@ Sta::deletePinBefore(const Pin *pin)
}
levelize_->deleteEdgeBefore(edge);
}
// Deletes edges to/from vertex also.
graph_->deleteVertex(vertex);
}
}
@ -5554,14 +5557,16 @@ MinPeriodCheckSeq &
Sta::minPeriodViolations()
{
minPeriodPreamble();
return check_min_periods_->violations();
const Corner *corner = cmdCorner();
return check_min_periods_->violations(corner);
}
MinPeriodCheck *
Sta::minPeriodSlack()
{
minPeriodPreamble();
return check_min_periods_->minSlackCheck();
const Corner *corner = cmdCorner();
return check_min_periods_->minSlackCheck(corner);
}
void

View File

@ -25,6 +25,7 @@
#include "spice/WriteSpice.hh"
#include <algorithm> // swap
#include <filesystem>
#include "Debug.hh"
#include "Units.hh"
@ -127,18 +128,6 @@ WriteSpice::initPowerGnd()
gnd_voltage_ = 0.0;
}
// Use c++17 fs::path(filename).stem()
static string
filenameStem(const char *filename)
{
string filename1 = filename;
const size_t last_slash_idx = filename1.find_last_of("\\/");
if (last_slash_idx != std::string::npos)
return filename1.substr(last_slash_idx + 1);
else
return filename1;
}
void
WriteSpice::writeHeader(string &title,
float max_time,
@ -146,9 +135,9 @@ WriteSpice::writeHeader(string &title,
{
streamPrint(spice_stream_, "* %s\n", title.c_str());
streamPrint(spice_stream_, ".include \"%s\"\n", model_filename_);
string subckt_filename_stem = filenameStem(subckt_filename_);
streamPrint(spice_stream_, ".include \"%s\"\n", subckt_filename_stem.c_str());
std::filesystem::path subckt_filename
= std::filesystem::path(subckt_filename_).filename();
streamPrint(spice_stream_, ".include \"%s\"\n", subckt_filename.c_str());
streamPrint(spice_stream_, ".tran %.3g %.3g\n", time_step, max_time);
// Suppress printing model parameters.
if (ckt_sim_ == CircuitSim::hspice)

View File

@ -0,0 +1,51 @@
library (liberty_backslash_eol) {
delay_model : "table_lookup";
simulation : false;
capacitive_load_unit\
(1,pF);
leakage_power_unit : "1pW";
current_unit : "1A";
pulling_resistance_unit : "1kohm";
time_unit : "1ns";
voltage_unit : "1v";
library_features : "report_delay_calculation";
input_threshold_pct_rise : 50;
input_threshold_pct_fall : 50;
output_threshold_pct_rise : 50;
output_threshold_pct_fall : 50;
slew_lower_threshold_pct_rise : 30;
slew_lower_threshold_pct_fall : 30;
slew_upper_threshold_pct_rise : 70;
slew_upper_threshold_pct_fall : 70;
slew_derate_from_library : 1.0;
nom_process : 1.0;
nom_temperature : 85.0;
nom_voltage : 0.75;
cell (my_inv) {
pin (A) {
capacitance : 1;
direction : "input";
}
pin (Y) {
function : "!A";
direction : "output";
timing () {
related_pin : "A";
timing_sense : "negative_unate";
cell_rise (scalar) {
values ("1");
}
cell_fall (scalar) {
values ("1");
}
rise_transition (scalar) {
values ("1");
}
fall_transition (scalar) {
values ("1");
}
}
}
}
}

View File

View File

@ -0,0 +1,2 @@
# backslash eol should be ignored in liberty file
read_liberty liberty_backslash_eol.lib

View File

@ -145,6 +145,7 @@ record_sta_tests {
get_objrefs
liberty_arcs_one2one_1
liberty_arcs_one2one_2
liberty_backslash_eol
liberty_ccsn
liberty_latch3
path_group_names