Merge remote-tracking branch 'parallax/master'
This commit is contained in:
commit
a74903cd3c
|
|
@ -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
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]"
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
%%
|
||||
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
|
|
|
|||
|
|
@ -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_);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ class Power : public StaState
|
|||
{
|
||||
public:
|
||||
Power(StaState *sta);
|
||||
void clear();
|
||||
void power(const Corner *corner,
|
||||
// Return values.
|
||||
PowerResult &total,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) :
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
# backslash eol should be ignored in liberty file
|
||||
read_liberty liberty_backslash_eol.lib
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue