Merge remote-tracking branch 'parallax/master'
This commit is contained in:
commit
4966240d28
14
README.md
14
README.md
|
|
@ -247,22 +247,20 @@ should be a short description of the problem. Attach a test case to
|
|||
reproduce the issue as described below. Issues without test cases are
|
||||
unlikely to get a response.
|
||||
|
||||
The files in the test case should be collected into a directory named
|
||||
YYYYMMDD where YYYY is the year, MM is the month, and DD is the
|
||||
day (this format allows "ls" to report them in chronological order).
|
||||
The contents of the directory should be collected into a compressed
|
||||
tarfile named YYYYMMDD.tgz.
|
||||
|
||||
The test case should have a tcl command file recreates the issue named
|
||||
run.tcl. If there are more than one command file using the same data
|
||||
run.tcl. If is are more than one command file using the same data
|
||||
files, there should be separate command files, run1.tcl, run2.tcl
|
||||
etc. The bug report can refer to these command files by name.
|
||||
|
||||
Command files should not have absolute filenames like
|
||||
"/home/cho/OpenSTA_Request/write_path_spice/dump_spice" in them.
|
||||
"/home/john/OpenSTA_bug/write_path_spice/dump_spice" in them.
|
||||
These obviously are not portable. Use filenames relative to the test
|
||||
case directory.
|
||||
|
||||
The files in the test case should be collected into a directory.
|
||||
The contents of the directory should be collected into a compressed
|
||||
tarfile.
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributors must sign the Contributor License Agreement (doc/CLA.txt)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,10 @@ set(TCL_POSSIBLE_NAMES
|
|||
# tcl lib path guesses.
|
||||
if (NOT TCL_LIB_PATHS)
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
set(TCL_LIB_PATHS /opt/homebrew/Cellar/tcl-tk@8/8.6.16/lib /opt/homebrew/opt/tcl-tk/lib /usr/local/lib)
|
||||
set(TCL_LIB_PATHS
|
||||
/opt/homebrew/Cellar/tcl-tk@8/8.6.17/lib
|
||||
/opt/homebrew/Cellar/tcl-tk@8/8.6.16/lib
|
||||
/opt/homebrew/opt/tcl-tk/lib /usr/local/lib)
|
||||
set(TCL_NO_DEFAULT_PATH TRUE)
|
||||
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(TCL_LIB_PATHS /usr/lib /usr/lib64 /usr/local/lib)
|
||||
|
|
|
|||
|
|
@ -184,6 +184,19 @@ GraphDelayCalc::delayInvalid(Vertex *vertex)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
GraphDelayCalc::levelsChangedBefore()
|
||||
{
|
||||
delaysInvalid();
|
||||
}
|
||||
|
||||
void
|
||||
GraphDelayCalc::levelChangedBefore(Vertex *vertex)
|
||||
{
|
||||
iter_->remove(vertex);
|
||||
delayInvalid(vertex);
|
||||
}
|
||||
|
||||
void
|
||||
GraphDelayCalc::deleteVertexBefore(Vertex *vertex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ The following classes now return const objects.
|
|||
Liberty PgPorts are now LibertyPorts with additional member functions for
|
||||
liberty pg_pins.
|
||||
|
||||
The write_verilog command always sorts the verilog file instances.
|
||||
The -sort argument is ignored.
|
||||
|
||||
Release 2.6.1 2025/03/??
|
||||
-------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -13772,18 +13772,9 @@
|
|||
<text:p text:style-name="P29"><text:alphabetical-index-mark-start text:id="IMark53760024032"/><text:span text:style-name="Command_20_Heading">write_verilog</text:span><text:alphabetical-index-mark-end text:id="IMark53760024032"/></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table109.A1" office:value-type="string">
|
||||
<text:p text:style-name="P264">[-sort<text:span text:style-name="T22">]</text:span></text:p>
|
||||
<text:p text:style-name="P264"><text:span text:style-name="T22">[-include_pwr_gnd]</text:span><text:line-break/>[-remove_cells <text:span text:style-name="T1">lib_cells</text:span><text:span text:style-name="T22">]</text:span><text:line-break/><text:span text:style-name="T1">filename</text:span></text:p>
|
||||
</table:table-cell>
|
||||
</table:table-row>
|
||||
<table:table-row table:style-name="Table109.1">
|
||||
<table:table-cell table:style-name="Table109.A2" office:value-type="string">
|
||||
<text:p text:style-name="P265"><text:span text:style-name="Command_20_Argument"><text:span text:style-name="T317">-sort</text:span></text:span></text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Table109.A2" office:value-type="string">
|
||||
<text:p text:style-name="P29"><text:span text:style-name="Default_20_Paragraph_20_Font">Sort the instances in the netlist.</text:span></text:p>
|
||||
</table:table-cell>
|
||||
</table:table-row>
|
||||
<table:table-row table:style-name="Table109.1">
|
||||
<table:table-cell table:style-name="Table109.A2" office:value-type="string">
|
||||
<text:p text:style-name="P288">-include_pwr_gnd</text:p>
|
||||
|
|
@ -13809,7 +13800,7 @@
|
|||
</table:table-cell>
|
||||
</table:table-row>
|
||||
</table:table>
|
||||
<text:p text:style-name="P290">The <text:span text:style-name="T7">write_verilog</text:span> command writes a Verilog netlist to <text:span text:style-name="T3">filename</text:span>. Use <text:span text:style-name="T317">-sort</text:span> to sort the instances so the results are reproducible across operating systems. Use <text:span text:style-name="T7">-remove_cells</text:span> to remove instances of <text:span text:style-name="T3">lib_cells</text:span> from the netlist.</text:p>
|
||||
<text:p text:style-name="P290">The <text:span text:style-name="T7">write_verilog</text:span> command writes a Verilog netlist to <text:span text:style-name="T3">filename</text:span>. Instances are always sorted so the results are reproducible across operating systems. Use <text:span text:style-name="T7">-remove_cells</text:span> to remove instances of <text:span text:style-name="T3">lib_cells</text:span> from the netlist.</text:p>
|
||||
<text:h text:style-name="Heading_20_1" text:outline-level="1"><text:bookmark-start text:name="__RefHeading___Toc42589_2528141652"/><text:alphabetical-index-mark-start text:id="IMark53760024032"/>Filter Expressions<text:bookmark-end text:name="__RefHeading___Toc42589_2528141652"/><text:alphabetical-index-mark-end text:id="IMark53760024032"/></text:h>
|
||||
<text:p text:style-name="P291">The <text:span text:style-name="Example">get_cells</text:span>, <text:span text:style-name="Example">get_pins</text:span>, <text:span text:style-name="Example">get_ports</text:span> and <text:span text:style-name="Example">get_timing_edges</text:span> functions support filtering the returned objects by property values. Supported filter expressions are shown below.</text:p>
|
||||
<table:table table:name="Table66" table:style-name="Table66">
|
||||
|
|
|
|||
|
|
@ -106,6 +106,8 @@ protected:
|
|||
virtual void incrLevel(Level &level) const = 0;
|
||||
void findNext(Level to_level);
|
||||
void deleteEntries();
|
||||
void checkLevel(Vertex *vertex,
|
||||
Level level);
|
||||
|
||||
BfsIndex bfs_index_;
|
||||
Level level_min_;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ public:
|
|||
DispatchQueue(size_t thread_cnt);
|
||||
~DispatchQueue();
|
||||
void setThreadCount(size_t thread_count);
|
||||
size_t getThreadCount() const;
|
||||
// Dispatch and copy.
|
||||
void dispatch(const fp_t& op);
|
||||
// Dispatch and move.
|
||||
|
|
|
|||
|
|
@ -58,10 +58,12 @@ public:
|
|||
virtual void setObserver(DelayCalcObserver *observer);
|
||||
// Invalidate all delays/slews.
|
||||
virtual void delaysInvalid();
|
||||
virtual void levelsChangedBefore();
|
||||
// Invalidate vertex and downstream delays/slews.
|
||||
virtual void delayInvalid(Vertex *vertex);
|
||||
virtual void delayInvalid(const Pin *pin);
|
||||
virtual void deleteVertexBefore(Vertex *vertex);
|
||||
virtual void levelChangedBefore(Vertex *vertex);
|
||||
// Reset to virgin state.
|
||||
virtual void clear();
|
||||
// Find arc delays and vertex slews thru level.
|
||||
|
|
|
|||
|
|
@ -276,6 +276,7 @@ public:
|
|||
// Network edit before/after methods.
|
||||
virtual void disconnectPinBefore(const Pin *pin,
|
||||
const Network *network) = 0;
|
||||
virtual void deletePinBefore(const Pin *pin) = 0;
|
||||
virtual void loadPinCapacitanceChanged(const Pin *pin) = 0;
|
||||
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ public:
|
|||
};
|
||||
|
||||
virtual PathEnd *copy() const = 0;
|
||||
virtual ~PathEnd();
|
||||
virtual ~PathEnd() {}
|
||||
void deletePath();
|
||||
Path *path() { return path_; }
|
||||
const Path *path() const { return path_; }
|
||||
|
|
|
|||
|
|
@ -418,7 +418,8 @@ public:
|
|||
void checkPrevPaths() const;
|
||||
void deletePaths(Vertex *vertex);
|
||||
void deleteTagGroup(TagGroup *group);
|
||||
bool postpone_latch_outputs_;
|
||||
bool postponeLatchOutputs() const { return postpone_latch_outputs_; }
|
||||
void saveEnumPath(Path *path);
|
||||
|
||||
protected:
|
||||
void init(StaState *sta);
|
||||
|
|
@ -671,8 +672,10 @@ protected:
|
|||
VertexSet *filtered_arrivals_;
|
||||
std::mutex filtered_arrivals_lock_;
|
||||
bool found_downstream_clk_pins_;
|
||||
bool postpone_latch_outputs_;
|
||||
PathGroups *path_groups_;
|
||||
VisitPathEnds *visit_path_ends_;
|
||||
std::vector<Path*> enum_paths_;
|
||||
GatedClk *gated_clk_;
|
||||
CheckCrpr *check_crpr_;
|
||||
Genclks *genclks_;
|
||||
|
|
|
|||
|
|
@ -1365,6 +1365,7 @@ protected:
|
|||
virtual void makeCheckMaxSkews();
|
||||
virtual void makeReportPath();
|
||||
virtual void makePower();
|
||||
virtual void makeClkSkews();
|
||||
virtual void makeObservers();
|
||||
NetworkEdit *networkCmdEdit();
|
||||
|
||||
|
|
|
|||
|
|
@ -42,10 +42,15 @@ public:
|
|||
void operator=(const Unit &unit);
|
||||
float scale() const { return scale_; }
|
||||
void setScale(float scale);
|
||||
const char *scaleAbbreviation() const;
|
||||
const char *suffix() const { return suffix_.c_str(); }
|
||||
// Mkmunpf abbreviation for scale.
|
||||
std::string scaleAbbreviation() const;
|
||||
// 1Mkmunpf or scale
|
||||
std::string scaleString() const;
|
||||
std::string suffix() const { return suffix_; }
|
||||
// scaleString + suffix
|
||||
std::string scaleSuffix() const;
|
||||
// scale abbreviation + suffix
|
||||
const char *scaledSuffix() const { return scaled_suffix_.c_str(); }
|
||||
std::string scaleAbbrevSuffix() const { return scale_abbrev_suffix_; }
|
||||
void setSuffix(const char *suffix);
|
||||
int digits() const { return digits_; }
|
||||
void setDigits(int digits);
|
||||
|
|
@ -57,11 +62,11 @@ public:
|
|||
int digits) const;
|
||||
|
||||
private:
|
||||
void setScaledSuffix();
|
||||
void setScaleAbbrevSuffix();
|
||||
|
||||
float scale_; // multiplier from user units to internal units
|
||||
std::string suffix_; // print suffix
|
||||
std::string scaled_suffix_;
|
||||
std::string scale_abbrev_suffix_;
|
||||
int digits_; // print digits (after decimal pt)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ namespace sta {
|
|||
|
||||
void
|
||||
writeVerilog(const char *filename,
|
||||
bool sort,
|
||||
bool include_pwr_gnd,
|
||||
CellSeq *remove_cells,
|
||||
Network *network);
|
||||
|
|
|
|||
|
|
@ -137,20 +137,20 @@ LibertyWriter::writeHeader()
|
|||
fprintf(stream_, " simulation : false;\n");
|
||||
const Unit *cap_unit = library_->units()->capacitanceUnit();
|
||||
fprintf(stream_, " capacitive_load_unit (1,%s);\n",
|
||||
cap_unit->scaledSuffix());
|
||||
cap_unit->scaleAbbrevSuffix().c_str());
|
||||
fprintf(stream_, " leakage_power_unit : 1pW;\n");
|
||||
const Unit *current_unit = library_->units()->currentUnit();
|
||||
fprintf(stream_, " current_unit : \"1%s\";\n",
|
||||
current_unit->scaledSuffix());
|
||||
current_unit->scaleAbbrevSuffix().c_str());
|
||||
const Unit *res_unit = library_->units()->resistanceUnit();
|
||||
fprintf(stream_, " pulling_resistance_unit : \"1%s\";\n",
|
||||
res_unit->scaledSuffix());
|
||||
res_unit->scaleAbbrevSuffix().c_str());
|
||||
const Unit *time_unit = library_->units()->timeUnit();
|
||||
fprintf(stream_, " time_unit : \"1%s\";\n",
|
||||
time_unit->scaledSuffix());
|
||||
time_unit->scaleAbbrevSuffix().c_str());
|
||||
const Unit *volt_unit = library_->units()->voltageUnit();
|
||||
fprintf(stream_, " voltage_unit : \"1%s\";\n",
|
||||
volt_unit->scaledSuffix());
|
||||
volt_unit->scaleAbbrevSuffix().c_str());
|
||||
fprintf(stream_, " library_features(report_delay_calculation);\n");
|
||||
fprintf(stream_, "\n");
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ Unit::Unit(const char *suffix) :
|
|||
suffix_(suffix),
|
||||
digits_(3)
|
||||
{
|
||||
setScaledSuffix();
|
||||
setScaleAbbrevSuffix();
|
||||
}
|
||||
|
||||
Unit::Unit(float scale,
|
||||
|
|
@ -50,13 +50,13 @@ Unit::Unit(float scale,
|
|||
suffix_(suffix),
|
||||
digits_(digits)
|
||||
{
|
||||
setScaledSuffix();
|
||||
setScaleAbbrevSuffix();
|
||||
}
|
||||
|
||||
void
|
||||
Unit::setScaledSuffix()
|
||||
Unit::setScaleAbbrevSuffix()
|
||||
{
|
||||
scaled_suffix_ = scaleAbbreviation() + suffix_;
|
||||
scale_abbrev_suffix_ = scaleAbbreviation() + suffix_;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -64,7 +64,7 @@ Unit::operator=(const Unit &unit)
|
|||
{
|
||||
scale_ = unit.scale_;
|
||||
suffix_ = unit.suffix_;
|
||||
scaled_suffix_ = unit.scaled_suffix_;
|
||||
scale_abbrev_suffix_ = unit.scale_abbrev_suffix_;
|
||||
digits_ = unit.digits_;
|
||||
}
|
||||
|
||||
|
|
@ -84,10 +84,10 @@ void
|
|||
Unit::setScale(float scale)
|
||||
{
|
||||
scale_ = scale;
|
||||
setScaledSuffix();
|
||||
setScaleAbbrevSuffix();
|
||||
}
|
||||
|
||||
const char *
|
||||
std::string
|
||||
Unit::scaleAbbreviation() const
|
||||
{
|
||||
if (fuzzyEqual(scale_, 1E+6))
|
||||
|
|
@ -110,11 +110,40 @@ Unit::scaleAbbreviation() const
|
|||
return "?";
|
||||
}
|
||||
|
||||
std::string
|
||||
Unit::scaleString() const
|
||||
{
|
||||
if (fuzzyEqual(scale_, 1E+6))
|
||||
return "1M";
|
||||
else if (fuzzyEqual(scale_, 1E+3))
|
||||
return "1k";
|
||||
if (fuzzyEqual(scale_, 1.0))
|
||||
return "1";
|
||||
else if (fuzzyEqual(scale_, 1E-3))
|
||||
return "1m";
|
||||
else if (fuzzyEqual(scale_, 1E-6))
|
||||
return "1u";
|
||||
else if (fuzzyEqual(scale_, 1E-9))
|
||||
return "1n";
|
||||
else if (fuzzyEqual(scale_, 1E-12))
|
||||
return "1p";
|
||||
else if (fuzzyEqual(scale_, 1E-15))
|
||||
return "1f";
|
||||
else
|
||||
return stdstrPrint("%.1e", scale_);
|
||||
}
|
||||
|
||||
std::string
|
||||
Unit::scaleSuffix() const
|
||||
{
|
||||
return scaleString() + suffix_;
|
||||
}
|
||||
|
||||
void
|
||||
Unit::setSuffix(const char *suffix)
|
||||
{
|
||||
suffix_ = suffix;
|
||||
setScaledSuffix();
|
||||
setScaleAbbrevSuffix();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -1286,7 +1286,6 @@ ConcreteNetwork::replaceCell(Instance *inst,
|
|||
if (cport) {
|
||||
rpins[cport->pinIndex()] = cpin;
|
||||
cpin->port_ = cport;
|
||||
cpin->id_ = ConcreteNetwork::nextObjectId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -721,11 +721,13 @@ ConcreteParasiticNetwork::disconnectPin(const Pin *pin,
|
|||
ccapacitor->replaceNode(node, subnode);
|
||||
}
|
||||
|
||||
pin_nodes_.erase(pin);
|
||||
pin_nodes_.erase(pin_node);
|
||||
delete node;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
NetIdPairLess::NetIdPairLess(const Network *network) :
|
||||
net_less_(network)
|
||||
{
|
||||
|
|
@ -893,6 +895,13 @@ ConcreteParasitics::disconnectPinBefore(const Pin *pin,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConcreteParasitics::deletePinBefore(const Pin *pin)
|
||||
{
|
||||
// Actions are the same.
|
||||
disconnectPinBefore(pin, network_);
|
||||
}
|
||||
|
||||
void
|
||||
ConcreteParasitics::loadPinCapacitanceChanged(const Pin *pin)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ public:
|
|||
|
||||
void disconnectPinBefore(const Pin *pin,
|
||||
const Network *network) override;
|
||||
void deletePinBefore(const Pin *pin) override;
|
||||
void loadPinCapacitanceChanged(const Pin *pin) override;
|
||||
|
||||
void deleteReducedParasitics(const Net *net,
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ Power::Power(StaState *sta) :
|
|||
seq_activity_map_(100, SeqPinHash(network_), SeqPinEqual()),
|
||||
activities_valid_(false),
|
||||
bdd_(sta),
|
||||
instance_powers_(InstanceIdLess(network_)),
|
||||
instance_powers_valid_(false),
|
||||
corner_(nullptr)
|
||||
{
|
||||
|
|
@ -694,8 +695,8 @@ Power::evalBddActivity(DdNode *bdd,
|
|||
Cudd_RecursiveDeref(bdd_.cuddMgr(), diff);
|
||||
float var_density = var_activity.density() * diff_duty;
|
||||
density += var_density;
|
||||
debugPrint(debug_, "power_activity", 3, "var %s %.3e * %.3f = %.3e",
|
||||
port->name(),
|
||||
debugPrint(debug_, "power_activity", 3, "%s %.3e * %.3f = %.3e",
|
||||
network_->pathName(pin),
|
||||
var_activity.density(),
|
||||
diff_duty,
|
||||
var_density);
|
||||
|
|
@ -834,24 +835,34 @@ Power::seedRegOutputActivities(const Instance *reg,
|
|||
{
|
||||
const Pin *out_pin = network_->findPin(reg, output);
|
||||
if (!hasUserActivity(out_pin)) {
|
||||
PwrActivity activity = evalActivity(seq->data(), reg);
|
||||
// Register output activity cannnot exceed one transition per clock cycle,
|
||||
// but latch output can.
|
||||
if (seq->isRegister()) {
|
||||
FuncExpr *clk_func = seq->clock();
|
||||
if (clk_func->port()) {
|
||||
const Pin *pin = network_->findPin(reg, clk_func->port());
|
||||
const Clock *clk = findClk(pin);
|
||||
if (clk) {
|
||||
if (activity.density() > 1.0 / clk->period())
|
||||
activity.setDensity(1.0 / clk->period());
|
||||
}
|
||||
PwrActivity in_activity = evalActivity(seq->data(), reg);
|
||||
float in_density = in_activity.density();
|
||||
float in_duty = in_activity.duty();
|
||||
// Default propagates input density/duty thru reg/latch.
|
||||
float out_density = in_density;
|
||||
float out_duty = in_duty;
|
||||
PwrActivity clk_activity = evalActivity(seq->clock(), reg);
|
||||
float clk_density = clk_activity.density();
|
||||
if (in_density > clk_density / 2) {
|
||||
if (seq->isRegister())
|
||||
out_density = 2 * in_duty * (1 - in_duty) * clk_density;
|
||||
else if (seq->isLatch()) {
|
||||
PwrActivity clk_activity = evalActivity(seq->clock(), reg);
|
||||
float clk_duty = clk_activity.duty();
|
||||
FuncExpr *clk_func = seq->clock();
|
||||
bool clk_invert = clk_func
|
||||
&& clk_func->op() == FuncExpr::op_not
|
||||
&& clk_func->left()->op() == FuncExpr::op_port;
|
||||
if (clk_invert)
|
||||
out_density = in_density * (1 - clk_duty);
|
||||
else
|
||||
out_density = in_density * clk_duty;
|
||||
}
|
||||
}
|
||||
if (invert)
|
||||
activity.setDuty(1.0 - activity.duty());
|
||||
activity.setOrigin(PwrActivityOrigin::propagated);
|
||||
setSeqActivity(reg, output, activity);
|
||||
out_duty = 1.0 - out_duty;
|
||||
PwrActivity out_activity(out_density, out_duty, PwrActivityOrigin::propagated);
|
||||
setSeqActivity(reg, output, out_activity);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ private:
|
|||
PwrSeqActivityMap seq_activity_map_;
|
||||
bool activities_valid_;
|
||||
Bdd bdd_;
|
||||
std::map<const Instance*, PowerResult> instance_powers_;
|
||||
std::map<const Instance*, PowerResult, InstanceIdLess> instance_powers_;
|
||||
bool instance_powers_valid_;
|
||||
const Corner *corner_;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,13 +35,14 @@ define_cmd_args "report_power" \
|
|||
[-highest_power_instances count]\
|
||||
[-corner corner]\
|
||||
[-digits digits]\
|
||||
[-format format]\
|
||||
[> filename] [>> filename] }
|
||||
|
||||
proc_redirect report_power {
|
||||
global sta_report_default_digits
|
||||
|
||||
parse_key_args "report_power" args \
|
||||
keys {-instances -highest_power_instances -corner -digits} flags {}
|
||||
keys {-instances -highest_power_instances -corner -digits -format} flags {}
|
||||
|
||||
check_argc_eq0 "report_power" $args
|
||||
|
||||
|
|
@ -56,16 +57,37 @@ proc_redirect report_power {
|
|||
}
|
||||
set corner [parse_corner keys]
|
||||
|
||||
if { [info exists keys(-format)] } {
|
||||
set format $keys(-format)
|
||||
if { $format != "text" && $format != "json" } {
|
||||
sta_error 311 "unknown power report -format $format"
|
||||
}
|
||||
} else {
|
||||
set format "text"
|
||||
}
|
||||
|
||||
if { [info exists keys(-instances)] } {
|
||||
set insts [get_instances_error "-instances" $keys(-instances)]
|
||||
report_power_insts $insts $corner $digits
|
||||
if { $format == "json" } {
|
||||
report_power_insts_json $insts $corner $digits
|
||||
} else {
|
||||
report_power_insts $insts $corner $digits
|
||||
}
|
||||
} elseif { [info exists keys(-highest_power_instances)] } {
|
||||
set count $keys(-highest_power_instances)
|
||||
check_positive_integer "-highest_power_instances" $count
|
||||
set insts [highest_power_instances $count $corner]
|
||||
report_power_insts $insts $corner $digits
|
||||
if { $format == "json" } {
|
||||
report_power_insts_json $insts $corner $digits
|
||||
} else {
|
||||
report_power_insts $insts $corner $digits
|
||||
}
|
||||
} else {
|
||||
report_power_design $corner $digits
|
||||
if { $format == "json" } {
|
||||
report_power_design_json $corner $digits
|
||||
} else {
|
||||
report_power_design $corner $digits
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,6 +126,35 @@ proc report_power_design { corner digits } {
|
|||
report_line "[format %-20s {}][power_col_percent $design_internal $design_total $field_width][power_col_percent $design_switching $design_total $field_width][power_col_percent $design_leakage $design_total $field_width]"
|
||||
}
|
||||
|
||||
proc report_power_design_json { corner digits } {
|
||||
set power_result [design_power $corner]
|
||||
set totals [lrange $power_result 0 3]
|
||||
set sequential [lrange $power_result 4 7]
|
||||
set combinational [lrange $power_result 8 11]
|
||||
set clock [lrange $power_result 12 15]
|
||||
set macro [lrange $power_result 16 19]
|
||||
set pad [lrange $power_result 20 end]
|
||||
|
||||
report_line "\{"
|
||||
report_power_row_json "Sequential" $sequential $digits ","
|
||||
report_power_row_json "Combinational" $combinational $digits ","
|
||||
report_power_row_json "Clock" $clock $digits ","
|
||||
report_power_row_json "Macro" $macro $digits ","
|
||||
report_power_row_json "Pad" $pad $digits ","
|
||||
report_power_row_json "Total" $totals $digits ""
|
||||
report_line "\}"
|
||||
}
|
||||
|
||||
proc report_power_row_json { name row_result digits separator } {
|
||||
lassign $row_result internal switching leakage total
|
||||
report_line " \"$name\": \{"
|
||||
report_line " \"internal\": [format %.${digits}e $internal],"
|
||||
report_line " \"switching\": [format %.${digits}e $switching],"
|
||||
report_line " \"leakage\": [format %.${digits}e $leakage],"
|
||||
report_line " \"total\": [format %.${digits}e $total]"
|
||||
report_line " \}$separator"
|
||||
}
|
||||
|
||||
proc max { x y } {
|
||||
if { $x >= $y } {
|
||||
return $x
|
||||
|
|
@ -206,6 +257,40 @@ proc report_power_insts { insts corner digits } {
|
|||
}
|
||||
}
|
||||
|
||||
proc report_power_insts_json { insts corner digits } {
|
||||
set inst_pwrs {}
|
||||
foreach inst $insts {
|
||||
set power_result [instance_power $inst $corner]
|
||||
lappend inst_pwrs [list $inst $power_result]
|
||||
}
|
||||
set inst_pwrs [lsort -command inst_pwr_cmp $inst_pwrs]
|
||||
|
||||
report_line "\["
|
||||
set first 1
|
||||
foreach inst_pwr $inst_pwrs {
|
||||
set inst [lindex $inst_pwr 0]
|
||||
set power [lindex $inst_pwr 1]
|
||||
if { !$first } {
|
||||
report_line ","
|
||||
}
|
||||
set first 0
|
||||
report_power_inst_json $inst $power $digits
|
||||
}
|
||||
report_line "\]"
|
||||
}
|
||||
|
||||
proc report_power_inst_json { inst power digits } {
|
||||
lassign $power internal switching leakage total
|
||||
set inst_name [get_full_name $inst]
|
||||
report_line "\{"
|
||||
report_line " \"name\": \"$inst_name\","
|
||||
report_line " \"internal\": [format %.${digits}e $internal],"
|
||||
report_line " \"switching\": [format %.${digits}e $switching],"
|
||||
report_line " \"leakage\": [format %.${digits}e $leakage],"
|
||||
report_line " \"total\": [format %.${digits}e $total]"
|
||||
report_line "\}"
|
||||
}
|
||||
|
||||
proc inst_pwr_cmp { inst_pwr1 inst_pwr2 } {
|
||||
set pwr1 [lindex $inst_pwr1 1]
|
||||
set pwr2 [lindex $inst_pwr2 1]
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "VcdParse.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cinttypes>
|
||||
|
||||
|
|
@ -74,14 +75,19 @@ VcdParse::read(const char *filename,
|
|||
// empty body
|
||||
readStmtString();
|
||||
else if (token == "$dumpall")
|
||||
parseVarValues();
|
||||
// Ignore dumpall body.
|
||||
readStmtTokens();
|
||||
else if (token == "$dumpvars")
|
||||
// Initial values.
|
||||
parseVarValues();
|
||||
else if (token[0] == '#') {
|
||||
time_ = stoll(token.substr(1));
|
||||
reader_->setTimeMin(time_);
|
||||
prev_time_ = time_;
|
||||
}
|
||||
else if (token[0] == '$')
|
||||
report_->fileError(800, filename_, stmt_line_, "unhandled vcd command.");
|
||||
else
|
||||
parseVarValues();
|
||||
|
||||
token = getToken();
|
||||
}
|
||||
gzclose(stream_);
|
||||
|
|
@ -205,18 +211,11 @@ void
|
|||
VcdParse::parseVarValues()
|
||||
{
|
||||
string token = getToken();
|
||||
bool first_time = true;
|
||||
while (!token.empty()) {
|
||||
char char0 = toupper(token[0]);
|
||||
if (char0 == '#' && token.size() > 1) {
|
||||
VcdTime time = stoll(token.substr(1));
|
||||
if (first_time) {
|
||||
prev_time_ = time;
|
||||
first_time = false;
|
||||
reader_->setTimeMin(time);
|
||||
}
|
||||
else
|
||||
prev_time_ = time_;
|
||||
prev_time_ = time_;
|
||||
time_ = time;
|
||||
if (time_ > prev_time_)
|
||||
reader_->varMinDeltaTime(time_ - prev_time_);
|
||||
|
|
@ -233,27 +232,15 @@ VcdParse::parseVarValues()
|
|||
reader_->varAppendValue(id, time_, char0);
|
||||
}
|
||||
else if (char0 == 'B') {
|
||||
char char1 = toupper(token[1]);
|
||||
if (char1 == 'X'
|
||||
|| char1 == 'U'
|
||||
|| char1 == 'Z') {
|
||||
string id = getToken();
|
||||
if (!reader_->varIdValid(id))
|
||||
report_->fileError(806, filename_, stmt_line_,
|
||||
"unknown variable %s", id.c_str());
|
||||
// Bus mixed 0/1/X/U not supported.
|
||||
reader_->varAppendValue(id, time_, char1);
|
||||
}
|
||||
string bus_value = token.substr(1);
|
||||
string id = getToken();
|
||||
if (!reader_->varIdValid(id))
|
||||
report_->fileError(807, filename_, stmt_line_,
|
||||
"unknown variable %s", id.c_str());
|
||||
else {
|
||||
string bin = token.substr(1);
|
||||
char *end;
|
||||
int64_t bus_value = strtol(bin.c_str(), &end, 2);
|
||||
string id = getToken();
|
||||
if (!reader_->varIdValid(id))
|
||||
report_->fileError(807, filename_, stmt_line_,
|
||||
"unknown variable %s", id.c_str());
|
||||
else
|
||||
reader_->varAppendBusValue(id, time_, bus_value);
|
||||
// Reverse the bus value to match the bit order in the VCD file.
|
||||
std::reverse(bus_value.begin(), bus_value.end());
|
||||
reader_->varAppendBusValue(id, time_, bus_value);
|
||||
}
|
||||
}
|
||||
token = getToken();
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ public:
|
|||
char value) = 0;
|
||||
virtual void varAppendBusValue(const std::string &id,
|
||||
VcdTime time,
|
||||
int64_t bus_value) = 0;
|
||||
const std::string &bus_value) = 0;
|
||||
};
|
||||
|
||||
class VcdValue
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ public:
|
|||
char value) override;
|
||||
void varAppendBusValue(const string &id,
|
||||
VcdTime time,
|
||||
int64_t bus_value) override;
|
||||
const string &bus_value) override;
|
||||
|
||||
private:
|
||||
void addVarPin(const string &pin_name,
|
||||
|
|
@ -329,14 +329,21 @@ VcdCountReader::varAppendValue(const string &id,
|
|||
void
|
||||
VcdCountReader::varAppendBusValue(const string &id,
|
||||
VcdTime time,
|
||||
int64_t bus_value)
|
||||
const string &bus_value)
|
||||
{
|
||||
const auto &itr = vcd_count_map_.find(id);
|
||||
if (itr != vcd_count_map_.end()) {
|
||||
VcdCounts &vcd_counts = itr->second;
|
||||
for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) {
|
||||
char bit_value = ((bus_value >> bit_idx) & 0x1) ? '1' : '0';
|
||||
char bit_value;
|
||||
if (bus_value.size() == 1)
|
||||
bit_value = bus_value[0];
|
||||
else if (bit_idx < bus_value.size())
|
||||
bit_value = bus_value[bit_idx];
|
||||
else
|
||||
bit_value = '0';
|
||||
VcdCount &vcd_count = vcd_counts[bit_idx];
|
||||
vcd_count.incrCounts(time, bit_value);
|
||||
if (debug_->check("read_vcd", 3)) {
|
||||
for (const Pin *pin : vcd_count.pins()) {
|
||||
debugPrint(debug_, "read_vcd", 3, "%s time %" PRIu64 " value %c",
|
||||
|
|
@ -345,7 +352,6 @@ VcdCountReader::varAppendBusValue(const string &id,
|
|||
bit_value);
|
||||
}
|
||||
}
|
||||
vcd_count.incrCounts(time, bit_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,11 +144,11 @@ proc check_unit { unit key suffix key_var } {
|
|||
set arg_suffix [string range $value end-[expr $suffix_length - 1] end]
|
||||
if { [string match -nocase $arg_suffix $suffix] } {
|
||||
set arg_prefix [string range $value 0 end-$suffix_length]
|
||||
if { [regexp "^(10*)?(\[Mkmunpf\])?$" $arg_prefix ignore mult prefix] } {
|
||||
if { [regexp "^(10*\\\.?0*)?(\[Mkmunpf\])?$" $arg_prefix ignore mult prefix] } {
|
||||
if { $mult == "" } {
|
||||
set mult 1
|
||||
}
|
||||
set scale [unit_prefix_scale $unit $prefix ]
|
||||
set scale [unit_prefix_scale $unit $prefix]
|
||||
check_unit_scale $unit [expr $scale * $mult]
|
||||
} else {
|
||||
sta_error 343 "unknown unit $unit prefix '${arg_prefix}'."
|
||||
|
|
|
|||
|
|
@ -146,7 +146,8 @@ BfsIterator::visit(Level to_level,
|
|||
int visit_count = 0;
|
||||
while (levelLessOrEqual(first_level_, last_level_)
|
||||
&& levelLessOrEqual(first_level_, to_level)) {
|
||||
VertexSeq &level_vertices = queue_[first_level_];
|
||||
Level level = first_level_;
|
||||
VertexSeq &level_vertices = queue_[level];
|
||||
incrLevel(first_level_);
|
||||
// Note that ArrivalVisitor::enqueueRefPinInputDelays may enqueue
|
||||
// vertices at this level so range iteration fails if the vector grows.
|
||||
|
|
@ -154,6 +155,7 @@ BfsIterator::visit(Level to_level,
|
|||
Vertex *vertex = level_vertices.back();
|
||||
level_vertices.pop_back();
|
||||
if (vertex) {
|
||||
checkLevel(vertex, level);
|
||||
vertex->setBfsInQueue(bfs_index_, false);
|
||||
visitor->visit(vertex);
|
||||
visit_count++;
|
||||
|
|
@ -181,12 +183,14 @@ BfsIterator::visitParallel(Level to_level,
|
|||
while (levelLessOrEqual(first_level_, last_level_)
|
||||
&& levelLessOrEqual(first_level_, to_level)) {
|
||||
VertexSeq &level_vertices = queue_[first_level_];
|
||||
Level level = first_level_;
|
||||
incrLevel(first_level_);
|
||||
if (!level_vertices.empty()) {
|
||||
size_t vertex_count = level_vertices.size();
|
||||
if (vertex_count < thread_count) {
|
||||
for (Vertex *vertex : level_vertices) {
|
||||
if (vertex) {
|
||||
checkLevel(vertex, level);
|
||||
vertex->setBfsInQueue(bfs_index_, false);
|
||||
visitor->visit(vertex);
|
||||
}
|
||||
|
|
@ -203,6 +207,7 @@ BfsIterator::visitParallel(Level to_level,
|
|||
for (size_t i = from; i < to; i++) {
|
||||
Vertex *vertex = level_vertices[i];
|
||||
if (vertex) {
|
||||
checkLevel(vertex, level);
|
||||
vertex->setBfsInQueue(bfs_index, false);
|
||||
visitors[k]->visit(vertex);
|
||||
}
|
||||
|
|
@ -259,8 +264,10 @@ BfsIterator::findNext(Level to_level)
|
|||
Vertex *vertex = level_vertices.back();
|
||||
if (vertex == nullptr)
|
||||
level_vertices.pop_back();
|
||||
else
|
||||
else {
|
||||
checkLevel(vertex, first_level_);
|
||||
return;
|
||||
}
|
||||
}
|
||||
incrLevel(first_level_);
|
||||
}
|
||||
|
|
@ -313,6 +320,17 @@ BfsIterator::checkInQueue(Vertex *vertex)
|
|||
vertex->to_string(this).c_str());
|
||||
}
|
||||
|
||||
void
|
||||
BfsIterator::checkLevel(Vertex *vertex,
|
||||
Level level)
|
||||
{
|
||||
if (vertex->level() != level)
|
||||
report_->error(2300, "vertex %s level %d != bfs level %d",
|
||||
vertex->to_string(this).c_str(),
|
||||
vertex->level(),
|
||||
level);
|
||||
}
|
||||
|
||||
void
|
||||
BfsIterator::deleteVertexBefore(Vertex *vertex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -50,40 +50,6 @@ namespace sta {
|
|||
|
||||
using std::abs;
|
||||
|
||||
// Source/target clock skew.
|
||||
class ClkSkew
|
||||
{
|
||||
public:
|
||||
ClkSkew();
|
||||
ClkSkew(Path *src_path,
|
||||
Path *tgt_path,
|
||||
bool include_internal_latency,
|
||||
StaState *sta);
|
||||
ClkSkew(const ClkSkew &clk_skew);
|
||||
void operator=(const ClkSkew &clk_skew);
|
||||
Path *srcPath() { return src_path_; }
|
||||
Path *tgtPath() { return tgt_path_; }
|
||||
float srcLatency(const StaState *sta);
|
||||
float tgtLatency(const StaState *sta);
|
||||
float srcInternalClkLatency(const StaState *sta);
|
||||
float tgtInternalClkLatency(const StaState *sta);
|
||||
Crpr crpr(const StaState *sta);
|
||||
float uncertainty(const StaState *sta);
|
||||
float skew() const { return skew_; }
|
||||
static bool srcTgtPathNameLess(ClkSkew &clk_skew1,
|
||||
ClkSkew &clk_skew2,
|
||||
const StaState *sta);
|
||||
|
||||
private:
|
||||
float clkTreeDelay(Path *clk_path,
|
||||
const StaState *sta);
|
||||
|
||||
Path *src_path_;
|
||||
Path *tgt_path_;
|
||||
bool include_internal_latency_;
|
||||
float skew_;
|
||||
};
|
||||
|
||||
ClkSkew::ClkSkew() :
|
||||
src_path_(nullptr),
|
||||
tgt_path_(nullptr),
|
||||
|
|
@ -206,10 +172,18 @@ ClkSkew::srcTgtPathNameLess(ClkSkew &clk_skew1,
|
|||
|
||||
ClkSkews::ClkSkews(StaState *sta) :
|
||||
StaState(sta),
|
||||
corner_(nullptr),
|
||||
include_internal_latency_(true),
|
||||
fanout_pred_(sta)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ClkSkews::clear()
|
||||
{
|
||||
skews_.clear();
|
||||
}
|
||||
|
||||
void
|
||||
ClkSkews::reportClkSkew(ConstClockSeq &clks,
|
||||
const Corner *corner,
|
||||
|
|
@ -217,8 +191,7 @@ ClkSkews::reportClkSkew(ConstClockSeq &clks,
|
|||
bool include_internal_latency,
|
||||
int digits)
|
||||
{
|
||||
ClkSkewMap skews = findClkSkew(clks, corner, setup_hold,
|
||||
include_internal_latency);
|
||||
findClkSkew(clks, corner, include_internal_latency);
|
||||
|
||||
// Sort the clocks to report in a stable order.
|
||||
ConstClockSeq sorted_clks;
|
||||
|
|
@ -228,9 +201,9 @@ ClkSkews::reportClkSkew(ConstClockSeq &clks,
|
|||
|
||||
for (const Clock *clk : sorted_clks) {
|
||||
report_->reportLine("Clock %s", clk->name());
|
||||
auto skew_itr = skews.find(clk);
|
||||
if (skew_itr != skews.end())
|
||||
reportClkSkew(skew_itr->second, digits);
|
||||
auto skew_itr = skews_.find(clk);
|
||||
if (skew_itr != skews_.end())
|
||||
reportClkSkew(skew_itr->second[setup_hold->index()], digits);
|
||||
else
|
||||
report_->reportLine("No launch/capture paths found.");
|
||||
report_->reportBlankLine();
|
||||
|
|
@ -289,25 +262,30 @@ ClkSkews::findWorstClkSkew(const Corner *corner,
|
|||
ConstClockSeq clks;
|
||||
for (const Clock *clk : *sdc_->clocks())
|
||||
clks.push_back(clk);
|
||||
ClkSkewMap skews = findClkSkew(clks, corner, setup_hold, include_internal_latency);
|
||||
findClkSkew(clks, corner, include_internal_latency);
|
||||
float worst_skew = 0.0;
|
||||
for (const auto& [clk, clk_skew] : skews) {
|
||||
float skew = clk_skew.skew();
|
||||
for (const auto& [clk, clk_skews] : skews_) {
|
||||
float skew = clk_skews[setup_hold->index()].skew();
|
||||
if (abs(skew) > abs(worst_skew))
|
||||
worst_skew = skew;
|
||||
}
|
||||
return worst_skew;
|
||||
}
|
||||
|
||||
ClkSkewMap
|
||||
void
|
||||
ClkSkews::findClkSkew(ConstClockSeq &clks,
|
||||
const Corner *corner,
|
||||
const SetupHold *setup_hold,
|
||||
bool include_internal_latency)
|
||||
{
|
||||
ClkSkewMap skews;
|
||||
if (corner == corner_
|
||||
&& include_internal_latency == include_internal_latency_
|
||||
&& clks == clks_
|
||||
&& !skews_.empty())
|
||||
return;
|
||||
|
||||
skews_.clear();
|
||||
clks_ = clks;
|
||||
corner_ = corner;
|
||||
setup_hold_ = setup_hold;
|
||||
include_internal_latency_ = include_internal_latency;
|
||||
|
||||
clk_set_.clear();
|
||||
|
|
@ -315,7 +293,7 @@ ClkSkews::findClkSkew(ConstClockSeq &clks,
|
|||
clk_set_.insert(clk);
|
||||
|
||||
if (thread_count_ > 1) {
|
||||
std::vector<ClkSkewMap> partial_skews(thread_count_, skews);
|
||||
std::vector<ClkSkewMap> partial_skews(thread_count_);
|
||||
for (Vertex *src_vertex : *graph_->regClkVertices()) {
|
||||
if (hasClkPaths(src_vertex)) {
|
||||
dispatch_queue_->dispatch([this, src_vertex, &partial_skews](int i) {
|
||||
|
|
@ -328,14 +306,30 @@ ClkSkews::findClkSkew(ConstClockSeq &clks,
|
|||
// Reduce skews from each register source.
|
||||
for (size_t i = 0; i < partial_skews.size(); i++) {
|
||||
for (auto& [clk, partial_skew] : partial_skews[i]) {
|
||||
auto ins = skews.insert(std::make_pair(clk, partial_skew));
|
||||
if (!ins.second) {
|
||||
ClkSkew &final_skew = ins.first->second;
|
||||
if (abs(partial_skew.skew()) > abs(final_skew.skew())
|
||||
|| (fuzzyEqual(abs(partial_skew.skew()), abs(final_skew.skew()))
|
||||
// Break ties based on source/target path names.
|
||||
&& ClkSkew::srcTgtPathNameLess(partial_skew, final_skew, this)))
|
||||
final_skew = partial_skew;
|
||||
auto itr = skews_.find(clk);
|
||||
if (itr == skews_.end()) {
|
||||
// Insert new entry using emplace with piecewise_construct
|
||||
// This will default-construct the array, then we copy the elements
|
||||
auto result = skews_.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(clk),
|
||||
std::make_tuple());
|
||||
itr = result.first;
|
||||
// Copy array elements
|
||||
for (int setup_hold_idx : SetupHold::rangeIndex())
|
||||
itr->second[setup_hold_idx] = partial_skew[setup_hold_idx];
|
||||
} else {
|
||||
// Update existing entry
|
||||
for (int setup_hold_idx : SetupHold::rangeIndex()) {
|
||||
ClkSkew &final_skew = itr->second[setup_hold_idx];
|
||||
ClkSkew &partial_skew_val = partial_skew[setup_hold_idx];
|
||||
float partial_skew1 = partial_skew_val.skew();
|
||||
float final_skew1 = final_skew.skew();
|
||||
if (abs(partial_skew1) > abs(final_skew1)
|
||||
|| (fuzzyEqual(abs(partial_skew1), abs(final_skew1))
|
||||
// Break ties based on source/target path names.
|
||||
&& ClkSkew::srcTgtPathNameLess(partial_skew_val, final_skew, this)))
|
||||
final_skew = partial_skew_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -343,10 +337,9 @@ ClkSkews::findClkSkew(ConstClockSeq &clks,
|
|||
else {
|
||||
for (Vertex *src_vertex : *graph_->regClkVertices()) {
|
||||
if (hasClkPaths(src_vertex))
|
||||
findClkSkewFrom(src_vertex, skews);
|
||||
findClkSkewFrom(src_vertex, skews_);
|
||||
}
|
||||
}
|
||||
return skews;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -392,11 +385,8 @@ ClkSkews::findClkSkewFrom(Vertex *src_vertex,
|
|||
while (edge_iter.hasNext()) {
|
||||
Edge *edge = edge_iter.next();
|
||||
const TimingRole *role = edge->role();
|
||||
if (role->isTimingCheck()
|
||||
&& ((setup_hold_ == SetupHold::max()
|
||||
&& role->genericRole() == TimingRole::setup())
|
||||
|| ((setup_hold_ == SetupHold::min()
|
||||
&& role->genericRole() == TimingRole::hold())))) {
|
||||
if (role->genericRole() == TimingRole::setup()
|
||||
|| role->genericRole() == TimingRole::hold()) {
|
||||
Vertex *tgt_vertex = edge->from(graph_);
|
||||
const RiseFall *tgt_rf1 = edge->timingArcSet()->isRisingFallingEdge();
|
||||
const RiseFallBoth *tgt_rf = tgt_rf1
|
||||
|
|
@ -416,16 +406,15 @@ ClkSkews::findClkSkew(Vertex *src_vertex,
|
|||
ClkSkewMap &skews)
|
||||
{
|
||||
Unit *time_unit = units_->timeUnit();
|
||||
const SetupHold *tgt_min_max = setup_hold_->opposite();
|
||||
VertexPathIterator src_iter(src_vertex, this);
|
||||
while (src_iter.hasNext()) {
|
||||
Path *src_path = src_iter.next();
|
||||
const Clock *src_clk = src_path->clock(this);
|
||||
if (src_path->isClock(this)
|
||||
&& src_rf->matches(src_path->transition(this))
|
||||
&& src_path->minMax(this) == setup_hold_
|
||||
&& clk_set_.find(src_clk) != clk_set_.end()) {
|
||||
Corner *src_corner = src_path->pathAnalysisPt(this)->corner();
|
||||
const MinMax *tgt_min_max = src_path->minMax(this)->opposite();
|
||||
if (corner_ == nullptr
|
||||
|| src_corner == corner_) {
|
||||
VertexPathIterator tgt_iter(tgt_vertex, this);
|
||||
|
|
@ -438,7 +427,8 @@ ClkSkews::findClkSkew(Vertex *src_vertex,
|
|||
&& tgt_path->minMax(this) == tgt_min_max
|
||||
&& tgt_path->pathAnalysisPt(this)->corner() == src_corner) {
|
||||
ClkSkew probe(src_path, tgt_path, include_internal_latency_, this);
|
||||
ClkSkew &clk_skew = skews[src_clk];
|
||||
const SetupHold *setup_hold = src_path->minMax(this);
|
||||
ClkSkew &clk_skew = skews[src_clk][setup_hold->index()];
|
||||
debugPrint(debug_, "clk_skew", 2,
|
||||
"%s %s %s -> %s %s %s crpr = %s skew = %s",
|
||||
network_->pathName(src_path->pin(this)),
|
||||
|
|
|
|||
|
|
@ -36,10 +36,43 @@
|
|||
|
||||
namespace sta {
|
||||
|
||||
class ClkSkew;
|
||||
class SearchPred;
|
||||
|
||||
typedef std::map<const Clock*, ClkSkew> ClkSkewMap;
|
||||
// Source/target clock skew.
|
||||
class ClkSkew
|
||||
{
|
||||
public:
|
||||
ClkSkew();
|
||||
ClkSkew(Path *src_path,
|
||||
Path *tgt_path,
|
||||
bool include_internal_latency,
|
||||
StaState *sta);
|
||||
ClkSkew(const ClkSkew &clk_skew);
|
||||
void operator=(const ClkSkew &clk_skew);
|
||||
Path *srcPath() { return src_path_; }
|
||||
Path *tgtPath() { return tgt_path_; }
|
||||
float srcLatency(const StaState *sta);
|
||||
float tgtLatency(const StaState *sta);
|
||||
float srcInternalClkLatency(const StaState *sta);
|
||||
float tgtInternalClkLatency(const StaState *sta);
|
||||
Crpr crpr(const StaState *sta);
|
||||
float uncertainty(const StaState *sta);
|
||||
float skew() const { return skew_; }
|
||||
static bool srcTgtPathNameLess(ClkSkew &clk_skew1,
|
||||
ClkSkew &clk_skew2,
|
||||
const StaState *sta);
|
||||
|
||||
private:
|
||||
float clkTreeDelay(Path *clk_path,
|
||||
const StaState *sta);
|
||||
|
||||
Path *src_path_;
|
||||
Path *tgt_path_;
|
||||
bool include_internal_latency_;
|
||||
float skew_;
|
||||
};
|
||||
|
||||
typedef std::map<const Clock*, ClkSkew[SetupHold::index_count]> ClkSkewMap;
|
||||
|
||||
class FanOutSrchPred : public SearchPred1
|
||||
{
|
||||
|
|
@ -53,6 +86,7 @@ class ClkSkews : public StaState
|
|||
{
|
||||
public:
|
||||
ClkSkews(StaState *sta);
|
||||
void clear();
|
||||
// Report clk skews for clks.
|
||||
void reportClkSkew(ConstClockSeq &clks,
|
||||
const Corner *corner,
|
||||
|
|
@ -65,10 +99,9 @@ public:
|
|||
bool include_internal_latency);
|
||||
|
||||
protected:
|
||||
ClkSkewMap findClkSkew(ConstClockSeq &clks,
|
||||
const Corner *corner,
|
||||
const SetupHold *setup_hold,
|
||||
bool include_internal_latency);
|
||||
void findClkSkew(ConstClockSeq &clks,
|
||||
const Corner *corner,
|
||||
bool include_internal_latency);
|
||||
bool hasClkPaths(Vertex *vertex);
|
||||
void findClkSkewFrom(Vertex *src_vertex,
|
||||
ClkSkewMap &skews);
|
||||
|
|
@ -88,11 +121,12 @@ protected:
|
|||
void reportClkSkew(ClkSkew &clk_skew,
|
||||
int digits);
|
||||
|
||||
ConstClockSeq clks_;
|
||||
ConstClockSet clk_set_;
|
||||
const Corner *corner_;
|
||||
const SetupHold *setup_hold_;
|
||||
bool include_internal_latency_;
|
||||
FanOutSrchPred fanout_pred_;
|
||||
ClkSkewMap skews_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -54,12 +54,6 @@ PathEnd::PathEnd(Path *path) :
|
|||
{
|
||||
}
|
||||
|
||||
PathEnd::~PathEnd()
|
||||
{
|
||||
if (path_->isEnum())
|
||||
delete path_;
|
||||
}
|
||||
|
||||
void
|
||||
PathEnd::setPath(Path *path)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -97,7 +97,11 @@ DiversionGreater::operator()(Diversion *div1,
|
|||
static void
|
||||
deleteDiversionPathEnd(Diversion *div)
|
||||
{
|
||||
delete div->pathEnd();
|
||||
PathEnd *div_end = div->pathEnd();
|
||||
Path *div_path = div_end->path();
|
||||
if (div_path->isEnum())
|
||||
delete div_path;
|
||||
delete div_end;
|
||||
delete div;
|
||||
}
|
||||
|
||||
|
|
@ -197,6 +201,7 @@ PathEnum::findNext()
|
|||
makeDiversions(path_end, div->divPath());
|
||||
// Caller owns the path end now, so don't delete it.
|
||||
next_ = path_end;
|
||||
//search_->saveEnumPath(path_end->path());
|
||||
delete div;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -409,11 +409,22 @@ Search::deletePaths()
|
|||
Vertex *vertex = vertex_iter.next();
|
||||
deletePaths(vertex);
|
||||
}
|
||||
|
||||
for (Path *path : enum_paths_)
|
||||
delete path;
|
||||
enum_paths_.clear();
|
||||
|
||||
filtered_arrivals_->clear();
|
||||
arrivals_exist_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Search::saveEnumPath(Path *path)
|
||||
{
|
||||
enum_paths_.push_back(path);
|
||||
}
|
||||
|
||||
// Delete with incremental tns/wns update.
|
||||
void
|
||||
Search::deletePathsIncr(Vertex *vertex)
|
||||
|
|
@ -2304,7 +2315,7 @@ PathVisitor::visitFromPath(const Pin *from_pin,
|
|||
if (min_max == MinMax::max()
|
||||
&& clk) {
|
||||
bool postponed = false;
|
||||
if (search_->postpone_latch_outputs_) {
|
||||
if (search_->postponeLatchOutputs()) {
|
||||
const Path *from_clk_path = from_clk_info->crprClkPath(this);
|
||||
if (from_clk_path) {
|
||||
Vertex *d_clk_vertex = from_clk_path->vertex(this);
|
||||
|
|
|
|||
|
|
@ -209,16 +209,19 @@ StaSimObserver::fanoutEdgesChangeAfter(Vertex *vertex)
|
|||
class StaLevelizeObserver : public LevelizeObserver
|
||||
{
|
||||
public:
|
||||
StaLevelizeObserver(Search *search);
|
||||
StaLevelizeObserver(Search *search, GraphDelayCalc *graph_delay_calc);
|
||||
void levelsChangedBefore() override;
|
||||
void levelChangedBefore(Vertex *vertex) override;
|
||||
|
||||
private:
|
||||
Search *search_;
|
||||
GraphDelayCalc *graph_delay_calc_;
|
||||
};
|
||||
|
||||
StaLevelizeObserver::StaLevelizeObserver(Search *search) :
|
||||
search_(search)
|
||||
StaLevelizeObserver::StaLevelizeObserver(Search *search,
|
||||
GraphDelayCalc *graph_delay_calc) :
|
||||
search_(search),
|
||||
graph_delay_calc_(graph_delay_calc)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -226,12 +229,14 @@ void
|
|||
StaLevelizeObserver::levelsChangedBefore()
|
||||
{
|
||||
search_->levelsChangedBefore();
|
||||
graph_delay_calc_->levelsChangedBefore();
|
||||
}
|
||||
|
||||
void
|
||||
StaLevelizeObserver::levelChangedBefore(Vertex *vertex)
|
||||
{
|
||||
search_->levelChangedBefore(vertex);
|
||||
graph_delay_calc_->levelChangedBefore(vertex);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
@ -310,6 +315,8 @@ Sta::makeComponents()
|
|||
makeSdcNetwork();
|
||||
makeReportPath();
|
||||
makePower();
|
||||
makeClkSkews();
|
||||
|
||||
setCmdNamespace1(CmdNamespace::sdc);
|
||||
setThreadCount1(defaultThreadCount());
|
||||
updateComponentsState();
|
||||
|
|
@ -324,7 +331,7 @@ Sta::makeObservers()
|
|||
{
|
||||
graph_delay_calc_->setObserver(new StaDelayCalcObserver(search_));
|
||||
sim_->setObserver(new StaSimObserver(graph_delay_calc_, levelize_, search_));
|
||||
levelize_->setObserver(new StaLevelizeObserver(search_));
|
||||
levelize_->setObserver(new StaLevelizeObserver(search_, graph_delay_calc_));
|
||||
}
|
||||
|
||||
int
|
||||
|
|
@ -371,8 +378,7 @@ Sta::updateComponentsState()
|
|||
if (check_timing_)
|
||||
check_timing_->copyState(this);
|
||||
clk_network_->copyState(this);
|
||||
if (clk_skews_)
|
||||
clk_skews_->copyState(this);
|
||||
clk_skews_->copyState(this);
|
||||
if (power_)
|
||||
power_->copyState(this);
|
||||
}
|
||||
|
|
@ -590,6 +596,7 @@ Sta::clear()
|
|||
check_min_pulse_widths_->clear();
|
||||
if (check_min_periods_)
|
||||
check_min_periods_->clear();
|
||||
clk_skews_->clear();
|
||||
delete graph_;
|
||||
graph_ = nullptr;
|
||||
current_instance_ = nullptr;
|
||||
|
|
@ -611,6 +618,7 @@ Sta::networkChanged()
|
|||
check_min_pulse_widths_->clear();
|
||||
if (check_min_periods_)
|
||||
check_min_periods_->clear();
|
||||
clk_skews_->clear();
|
||||
delete graph_;
|
||||
graph_ = nullptr;
|
||||
graph_sdc_annotated_ = false;
|
||||
|
|
@ -2481,6 +2489,7 @@ Sta::findPathEnds(ExceptionFrom *from,
|
|||
bool clk_gating_hold)
|
||||
{
|
||||
searchPreamble();
|
||||
clk_skews_->clear();
|
||||
return search_->findPathEnds(from, thrus, to, unconstrained,
|
||||
corner, min_max, group_path_count,
|
||||
endpoint_path_count,
|
||||
|
|
@ -2628,8 +2637,9 @@ float
|
|||
Sta::findWorstClkSkew(const SetupHold *setup_hold,
|
||||
bool include_internal_latency)
|
||||
{
|
||||
|
||||
clkSkewPreamble();
|
||||
return clk_skews_->findWorstClkSkew(cmd_corner_, setup_hold,
|
||||
return clk_skews_->findWorstClkSkew(nullptr, setup_hold,
|
||||
include_internal_latency);
|
||||
}
|
||||
|
||||
|
|
@ -2637,8 +2647,12 @@ void
|
|||
Sta::clkSkewPreamble()
|
||||
{
|
||||
ensureClkArrivals();
|
||||
if (clk_skews_ == nullptr)
|
||||
clk_skews_ = new ClkSkews(this);
|
||||
}
|
||||
|
||||
void
|
||||
Sta::makeClkSkews()
|
||||
{
|
||||
clk_skews_ = new ClkSkews(this);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
@ -4367,6 +4381,7 @@ Sta::replaceCellBefore(const Instance *inst,
|
|||
}
|
||||
}
|
||||
delete pin_iter;
|
||||
clk_skews_->clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4431,6 +4446,7 @@ Sta::connectPinAfter(const Pin *pin)
|
|||
}
|
||||
sdc_->connectPinAfter(pin);
|
||||
sim_->connectPinAfter(pin);
|
||||
clk_skews_->clear();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -4521,6 +4537,7 @@ Sta::disconnectPinBefore(const Pin *pin)
|
|||
}
|
||||
}
|
||||
}
|
||||
clk_skews_->clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4671,6 +4688,7 @@ Sta::deletePinBefore(const Pin *pin)
|
|||
sim_->deletePinBefore(pin);
|
||||
clk_network_->deletePinBefore(pin);
|
||||
power_->deletePinBefore(pin);
|
||||
clk_skews_->clear();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ define_cmd_args "report_units" {}
|
|||
proc report_units { args } {
|
||||
check_argc_eq0 "report_units" $args
|
||||
foreach unit {"time" "capacitance" "resistance" "voltage" "current" "power" "distance"} {
|
||||
report_line " $unit 1[unit_scaled_suffix $unit]"
|
||||
report_line " $unit [unit_scale_suffix $unit]"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -130,9 +130,9 @@ proc write_units_json { jsonfile } {
|
|||
set f [open $jsonfile w]
|
||||
puts $f "{"
|
||||
foreach unit {"time" "capacitance" "resistance" "voltage" "current" "power"} {
|
||||
puts $f " \"$unit\": \"[unit_scaled_suffix $unit]\","
|
||||
puts $f " \"$unit\": \"[unit_scale_suffix $unit]\","
|
||||
}
|
||||
puts $f " \"distance\": \"[unit_scaled_suffix distance]\""
|
||||
puts $f " \"distance\": \"[unit_scale_suffix distance]\""
|
||||
puts $f "}"
|
||||
close $f
|
||||
}
|
||||
|
|
@ -167,13 +167,13 @@ proc set_unit_values { unit key suffix key_var } {
|
|||
set arg_suffix [string range $value end-[expr $suffix_length - 1] end]
|
||||
if { [string match -nocase $arg_suffix $suffix] } {
|
||||
set arg_prefix [string range $value 0 end-$suffix_length]
|
||||
if { [regexp "^(10*)?(\[Mkmunpf\])?$" $arg_prefix ignore mult prefix] } {
|
||||
if { [regexp "^(10*\\\.?0*)?(\[Mkmunpf\])?$" $arg_prefix ignore mult prefix] } {
|
||||
#puts "$arg_prefix '$mult' '$prefix'"
|
||||
if { $mult == "" } {
|
||||
set mult 1
|
||||
}
|
||||
set scale [unit_prefix_scale $unit $prefix ]
|
||||
set_cmd_unit_scale $unit $scale
|
||||
set_cmd_unit_scale $unit [expr $scale * $mult]
|
||||
} else {
|
||||
sta_error 166 "unknown unit $unit prefix '${arg_prefix}'."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
%exception {
|
||||
try { $function }
|
||||
try { $action }
|
||||
catch (std::bad_alloc &) {
|
||||
fprintf(stderr, "Error: out of memory.\n");
|
||||
exit(1);
|
||||
|
|
|
|||
11
tcl/Util.tcl
11
tcl/Util.tcl
|
|
@ -185,6 +185,17 @@ proc define_hidden_cmd_args { cmd arglist } {
|
|||
namespace export $cmd
|
||||
}
|
||||
|
||||
# "Optional Upvar"
|
||||
# If $other_var is not empty, the upvar is executed.
|
||||
# Otherwise, $my_var is set to empty.
|
||||
proc upvar_opt { level other_var my_var } {
|
||||
if { $other_var != "" } {
|
||||
uplevel 1 "upvar $level $other_var $my_var"
|
||||
} else {
|
||||
uplevel 1 "set $my_var \"\""
|
||||
}
|
||||
}
|
||||
|
||||
################################################################
|
||||
|
||||
proc sta_warn { msg_id msg } {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"Sequential": {
|
||||
"internal": 8.01e-06,
|
||||
"switching": 4.99e-08,
|
||||
"leakage": 1.45e-09,
|
||||
"total": 8.06e-06
|
||||
},
|
||||
"Combinational": {
|
||||
"internal": 2.03e-07,
|
||||
"switching": 5.32e-08,
|
||||
"leakage": 1.59e-10,
|
||||
"total": 2.56e-07
|
||||
},
|
||||
"Clock": {
|
||||
"internal": 0.00e+00,
|
||||
"switching": 0.00e+00,
|
||||
"leakage": 0.00e+00,
|
||||
"total": 0.00e+00
|
||||
},
|
||||
"Macro": {
|
||||
"internal": 0.00e+00,
|
||||
"switching": 0.00e+00,
|
||||
"leakage": 0.00e+00,
|
||||
"total": 0.00e+00
|
||||
},
|
||||
"Pad": {
|
||||
"internal": 0.00e+00,
|
||||
"switching": 0.00e+00,
|
||||
"leakage": 0.00e+00,
|
||||
"total": 0.00e+00
|
||||
},
|
||||
"Total": {
|
||||
"internal": 8.22e-06,
|
||||
"switching": 1.03e-07,
|
||||
"leakage": 1.61e-09,
|
||||
"total": 8.32e-06
|
||||
}
|
||||
}
|
||||
[
|
||||
{
|
||||
"name": "u2",
|
||||
"internal": 1.05e-07,
|
||||
"switching": 2.74e-08,
|
||||
"leakage": 9.07e-11,
|
||||
"total": 1.32e-07
|
||||
}
|
||||
,
|
||||
{
|
||||
"name": "u1",
|
||||
"internal": 9.83e-08,
|
||||
"switching": 2.58e-08,
|
||||
"leakage": 6.82e-11,
|
||||
"total": 1.24e-07
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# report_power reg1_asap7
|
||||
read_liberty asap7_small.lib.gz
|
||||
read_verilog reg1_asap7.v
|
||||
link_design top
|
||||
create_clock -name clk -period 500 {clk1 clk2 clk3}
|
||||
set_propagated_clock {clk1 clk2 clk3}
|
||||
report_power -format json
|
||||
report_power -format json -instances {u1 u2}
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
# prima reg1 asap7
|
||||
read_liberty asap7_invbuf.lib.gz
|
||||
read_liberty asap7_seq.lib.gz
|
||||
read_liberty asap7_simple.lib.gz
|
||||
read_liberty asap7_small.lib.gz
|
||||
read_verilog reg1_asap7.v
|
||||
link_design top
|
||||
create_clock -name clk -period 500 {clk1 clk2 clk3}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ exec tclsh $0 ${1+"$@"}
|
|||
|
||||
# Directory containing tests.
|
||||
set test_dir [file dirname [file normalize [info script]]]
|
||||
set sta_dir [file normalize [file join $test_dir ".."]]
|
||||
source [file join $test_dir regression_vars.tcl]
|
||||
source [file join $test_dir regression.tcl]
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
|
||||
# Application program to run tests on.
|
||||
set app "sta"
|
||||
set sta_dir [file dirname $test_dir]
|
||||
set app_path [file join $sta_dir "build" $app]
|
||||
# Application options.
|
||||
set app_options "-no_init -no_splash -exit"
|
||||
|
|
@ -52,7 +51,7 @@ if [info exists env(DIFF_OPTIONS)] {
|
|||
}
|
||||
|
||||
set valgrind_suppress [file join $test_dir valgrind.suppress]
|
||||
set valgrind_options "--num-callers=20 --leak-check=full --freelist-vol=100000000 --leak-resolution=high --suppressions=$valgrind_suppress"
|
||||
set valgrind_options "--num-callers=20 --leak-check=full --show-leak-kinds=all --leak-resolution=high --suppressions=$valgrind_suppress"
|
||||
if { [exec "uname"] == "Darwin" } {
|
||||
append valgrind_options " --dsymutil=yes"
|
||||
}
|
||||
|
|
@ -71,31 +70,6 @@ proc record_test { test cmd_dir } {
|
|||
return $test
|
||||
}
|
||||
|
||||
# Record tests in the $STA/test directory.
|
||||
proc record_sta_tests { tests } {
|
||||
global test_dir
|
||||
foreach test $tests {
|
||||
# Prune commented tests from the list.
|
||||
if { [string index $test 0] != "#" } {
|
||||
record_test $test $test_dir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Record tests in the $STA/examples directory.
|
||||
proc record_example_tests { tests } {
|
||||
global test_dir test_groups
|
||||
set example_dir [file join $test_dir ".." "examples"]
|
||||
foreach test $tests {
|
||||
# Prune commented tests from the list.
|
||||
if { [string index $test 0] != "#" } {
|
||||
record_test $test $example_dir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
################################################################
|
||||
|
||||
proc define_test_group { name tests } {
|
||||
global test_groups
|
||||
set test_groups($name) $tests
|
||||
|
|
@ -124,6 +98,38 @@ proc list_delete { list delete } {
|
|||
|
||||
################################################################
|
||||
|
||||
# Record tests in $STA/test.
|
||||
proc record_public_tests { tests } {
|
||||
global sta_dir cmd_dirs test_groups test_dir
|
||||
set public_dir [file join $sta_dir "test"]
|
||||
foreach test $tests {
|
||||
if { [string index $test 0] != "#" } {
|
||||
record_test $test $public_dir
|
||||
# sync pvt/test okfiles to sta/test
|
||||
set public_ok [file join $public_dir $test.ok]
|
||||
if { [file exists $public_ok] } {
|
||||
file copy -force $public_ok [file join $test_dir $test.ok]
|
||||
}
|
||||
}
|
||||
}
|
||||
define_test_group public $tests
|
||||
}
|
||||
|
||||
# Record tests in the $STA/examples directory.
|
||||
proc record_example_tests { tests } {
|
||||
global test_dir test_groups
|
||||
set example_dir [file join $test_dir ".." "examples"]
|
||||
foreach test $tests {
|
||||
# Prune commented tests from the list.
|
||||
if { [string index $test 0] != "#" } {
|
||||
record_test $test $example_dir
|
||||
}
|
||||
}
|
||||
define_test_group examples $tests
|
||||
}
|
||||
|
||||
################################################################
|
||||
|
||||
# Regression test lists.
|
||||
|
||||
# Record tests in sta/examples
|
||||
|
|
@ -137,7 +143,7 @@ record_example_tests {
|
|||
spef_parasitics
|
||||
}
|
||||
|
||||
record_sta_tests {
|
||||
record_public_tests {
|
||||
disconnect_mcp_pin
|
||||
get_filter
|
||||
get_is_buffer
|
||||
|
|
@ -153,6 +159,7 @@ record_sta_tests {
|
|||
liberty_latch3
|
||||
package_require
|
||||
path_group_names
|
||||
power_json
|
||||
prima3
|
||||
report_checks_sorted
|
||||
report_checks_src_attr
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ exec tclsh $0 ${1+"$@"}
|
|||
|
||||
# Directory containing tests.
|
||||
set test_dir [file dirname [info script]]
|
||||
set sta_dir [file normalize [file join $test_dir ".."]]
|
||||
source [file join $test_dir regression_vars.tcl]
|
||||
source [file join $test_dir regression.tcl]
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@ DispatchQueue::setThreadCount(size_t thread_count)
|
|||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
DispatchQueue::getThreadCount() const
|
||||
{
|
||||
return threads_.size();
|
||||
}
|
||||
|
||||
void
|
||||
DispatchQueue::finishTasks()
|
||||
{
|
||||
|
|
|
|||
22
util/Util.i
22
util/Util.i
|
|
@ -23,6 +23,7 @@
|
|||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
%module util
|
||||
%include <std_string.i>
|
||||
|
||||
%{
|
||||
|
||||
|
|
@ -415,7 +416,7 @@ set_cmd_unit_suffix(const char *unit_name,
|
|||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
std::string
|
||||
unit_scale_abbreviation (const char *unit_name)
|
||||
{
|
||||
Unit *unit = Sta::sta()->units()->find(unit_name);
|
||||
|
|
@ -425,7 +426,7 @@ unit_scale_abbreviation (const char *unit_name)
|
|||
return "";
|
||||
}
|
||||
|
||||
const char *
|
||||
std::string
|
||||
unit_suffix(const char *unit_name)
|
||||
{
|
||||
Unit *unit = Sta::sta()->units()->find(unit_name);
|
||||
|
|
@ -435,12 +436,23 @@ unit_suffix(const char *unit_name)
|
|||
return "";
|
||||
}
|
||||
|
||||
const char *
|
||||
unit_scaled_suffix(const char *unit_name)
|
||||
std::string
|
||||
unit_scale_suffix(const char *unit_name)
|
||||
{
|
||||
Unit *unit = Sta::sta()->units()->find(unit_name);
|
||||
if (unit) {
|
||||
return unit->scaleSuffix();
|
||||
}
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string
|
||||
unit_scale_abbrev_suffix(const char *unit_name)
|
||||
{
|
||||
Unit *unit = Sta::sta()->units()->find(unit_name);
|
||||
if (unit)
|
||||
return unit->scaledSuffix();
|
||||
return unit->scaleAbbrevSuffix();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,14 +39,13 @@ read_verilog_cmd(const char *filename)
|
|||
|
||||
void
|
||||
write_verilog_cmd(const char *filename,
|
||||
bool sort,
|
||||
bool include_pwr_gnd,
|
||||
CellSeq *remove_cells)
|
||||
{
|
||||
// This does NOT want the SDC (cmd) network because it wants
|
||||
// to see the sta internal names.
|
||||
Network *network = Sta::sta()->network();
|
||||
writeVerilog(filename, sort, include_pwr_gnd, remove_cells, network);
|
||||
writeVerilog(filename, include_pwr_gnd, remove_cells, network);
|
||||
delete remove_cells;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,22 +31,25 @@ proc_redirect read_verilog {
|
|||
read_verilog_cmd [file nativename [lindex $args 0]]
|
||||
}
|
||||
|
||||
define_cmd_args "write_verilog" {[-sort] [-include_pwr_gnd]\
|
||||
define_cmd_args "write_verilog" {[-include_pwr_gnd]\
|
||||
[-remove_cells cells] filename}
|
||||
|
||||
proc write_verilog { args } {
|
||||
# -sort deprecated 12/12/2025
|
||||
parse_key_args "write_verilog" args keys {-remove_cells} \
|
||||
flags {-sort -include_pwr_gnd}
|
||||
|
||||
if { [info exists flags(-sort)] } {
|
||||
sta_warn 1338 "The -sort flag is ignored."
|
||||
}
|
||||
set remove_cells {}
|
||||
if { [info exists keys(-remove_cells)] } {
|
||||
set remove_cells [parse_cell_arg $keys(-remove_cells)]
|
||||
}
|
||||
set sort [info exists flags(-sort)]
|
||||
set include_pwr_gnd [info exists flags(-include_pwr_gnd)]
|
||||
check_argc_eq1 "write_verilog" $args
|
||||
set filename [file nativename [lindex $args 0]]
|
||||
write_verilog_cmd $filename $sort $include_pwr_gnd $remove_cells
|
||||
write_verilog_cmd $filename $include_pwr_gnd $remove_cells
|
||||
}
|
||||
|
||||
# sta namespace end
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ class VerilogWriter
|
|||
{
|
||||
public:
|
||||
VerilogWriter(const char *filename,
|
||||
bool sort,
|
||||
bool include_pwr_gnd,
|
||||
CellSeq *remove_cells,
|
||||
FILE *stream,
|
||||
|
|
@ -81,7 +80,6 @@ protected:
|
|||
const Port *port);
|
||||
|
||||
const char *filename_;
|
||||
bool sort_;
|
||||
bool include_pwr_gnd_;
|
||||
CellSet remove_cells_;
|
||||
FILE *stream_;
|
||||
|
|
@ -91,7 +89,6 @@ protected:
|
|||
|
||||
void
|
||||
writeVerilog(const char *filename,
|
||||
bool sort,
|
||||
bool include_pwr_gnd,
|
||||
CellSeq *remove_cells,
|
||||
Network *network)
|
||||
|
|
@ -99,7 +96,7 @@ writeVerilog(const char *filename,
|
|||
if (network->topInstance()) {
|
||||
FILE *stream = fopen(filename, "w");
|
||||
if (stream) {
|
||||
VerilogWriter writer(filename, sort, include_pwr_gnd,
|
||||
VerilogWriter writer(filename, include_pwr_gnd,
|
||||
remove_cells, stream, network);
|
||||
writer.writeModules();
|
||||
fclose(stream);
|
||||
|
|
@ -110,13 +107,11 @@ writeVerilog(const char *filename,
|
|||
}
|
||||
|
||||
VerilogWriter::VerilogWriter(const char *filename,
|
||||
bool sort,
|
||||
bool include_pwr_gnd,
|
||||
CellSeq *remove_cells,
|
||||
FILE *stream,
|
||||
Network *network) :
|
||||
filename_(filename),
|
||||
sort_(sort),
|
||||
include_pwr_gnd_(include_pwr_gnd),
|
||||
remove_cells_(network),
|
||||
stream_(stream),
|
||||
|
|
@ -146,13 +141,12 @@ VerilogWriter::findHierChildren()
|
|||
CellSet cells(network_);
|
||||
findHierChildren(network_->topInstance(), children, cells);
|
||||
|
||||
if (sort_)
|
||||
sort(children, [this](const Instance *inst1,
|
||||
const Instance *inst2) {
|
||||
const char *cell_name1 = network_->cellName(inst1);
|
||||
const char *cell_name2 = network_->cellName(inst2);
|
||||
return stringLess(cell_name1, cell_name2);
|
||||
});
|
||||
sort(children, [this](const Instance *inst1,
|
||||
const Instance *inst2) {
|
||||
const char *cell_name1 = network_->cellName(inst1);
|
||||
const char *cell_name2 = network_->cellName(inst2);
|
||||
return stringLess(cell_name1, cell_name2);
|
||||
});
|
||||
|
||||
return children;
|
||||
}
|
||||
|
|
@ -327,11 +321,10 @@ VerilogWriter::writeChildren(const Instance *inst)
|
|||
}
|
||||
delete child_iter;
|
||||
|
||||
if (sort_)
|
||||
sort(children, [this](const Instance *inst1,
|
||||
const Instance *inst2) {
|
||||
return stringLess(network_->name(inst1), network_->name(inst2));
|
||||
});
|
||||
sort(children, [this](const Instance *inst1,
|
||||
const Instance *inst2) {
|
||||
return stringLess(network_->name(inst1), network_->name(inst2));
|
||||
});
|
||||
|
||||
for (auto child : children)
|
||||
writeChild(child);
|
||||
|
|
|
|||
Loading…
Reference in New Issue