diff --git a/examples/timing_cell_dff.lib b/examples/timing_cell_dff.lib new file mode 100644 index 00000000..4d49e744 --- /dev/null +++ b/examples/timing_cell_dff.lib @@ -0,0 +1,192 @@ +library (timing_cell_dff) { + comment : ""; + delay_model : table_lookup; + simulation : false; + capacitive_load_unit (1,fF); + leakage_power_unit : 1pW; + current_unit : "1mA"; + 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 : 125.0; + nom_voltage : 0.95; + + lu_table_template(template_1) { + variable_1 : total_output_net_capacitance; + index_1("0.36562, 1.89781, 3.79562, 7.59125, 15.18250, 30.36500, 60.73000"); + } + + cell ("timing_cell_dff") { + area : 11.438 + is_macro_cell : true; + pin("in") { + direction : input; + capacitance : 0.9346; + timing() { + related_pin : "clk"; + timing_type : hold_rising; + rise_constraint(scalar) { + values("-0.02757"); + } + fall_constraint(scalar) { + values("-0.07072"); + } + paths() { + slack : 0.02757; + fall_data_required() { + time: 0.00300; + vertex("r1", "DFF_X1", "r1/CK", "clk", "^", 0.00000, 0.00000, 1.64207, 0); + } + fall_data_arrival() { + time: 0.07373; + vertex("", "timing_cell_dff", "in", "", "v", 0.00000, 0.00000, 0.83524, 1); + vertex("u1", "BUF_X1", "u1/A", "in", "v", 0.00000, 0.00000, 0.83524, 0); + vertex("u1", "BUF_X1", "u1/Z", "w1", "v", 0.07373, 0.01504, 1.03013, 1); + vertex("r1", "DFF_X1", "r1/D", "w1", "v", 0.07373, 0.01504, 1.03013, 0); + } + rise_data_required() { + time: 0.01713; + vertex("r1", "DFF_X1", "r1/CK", "clk", "^", 0.00000, 0.00000, 1.81915, 0); + } + rise_data_arrival() { + time: 0.04470; + vertex("", "timing_cell_dff", "in", "", "^", 0.00000, 0.00000, 0.93456, 1); + vertex("u1", "BUF_X1", "u1/A", "in", "^", 0.00000, 0.00000, 0.93456, 0); + vertex("u1", "BUF_X1", "u1/Z", "w1", "^", 0.04470, 0.01908, 1.10913, 1); + vertex("r1", "DFF_X1", "r1/D", "w1", "^", 0.04470, 0.01908, 1.10913, 0); + } + } + } + timing() { + related_pin : "clk"; + timing_type : setup_rising; + rise_constraint(scalar) { + values("0.11670"); + } + fall_constraint(scalar) { + values("0.23045"); + } + paths() { + slack : 9.76955; + fall_data_required() { + time: 9.84328; + vertex("r1", "DFF_X1", "r1/CK", "clk", "^", 0.00000, 0.00000, 1.64207, 0); + } + fall_data_arrival() { + time: 0.07373; + vertex("", "timing_cell_dff", "in", "", "v", 0.00000, 0.00000, 0.83524, 1); + vertex("u1", "BUF_X1", "u1/A", "in", "v", 0.00000, 0.00000, 0.83524, 0); + vertex("u1", "BUF_X1", "u1/Z", "w1", "v", 0.07373, 0.01504, 1.03013, 1); + vertex("r1", "DFF_X1", "r1/D", "w1", "v", 0.07373, 0.01504, 1.03013, 0); + } + rise_data_required() { + time: 9.92800; + vertex("r1", "DFF_X1", "r1/CK", "clk", "^", 0.00000, 0.00000, 1.81915, 0); + } + rise_data_arrival() { + time: 0.04470; + vertex("", "timing_cell_dff", "in", "", "^", 0.00000, 0.00000, 0.93456, 1); + vertex("u1", "BUF_X1", "u1/A", "in", "^", 0.00000, 0.00000, 0.93456, 0); + vertex("u1", "BUF_X1", "u1/Z", "w1", "^", 0.04470, 0.01908, 1.10913, 1); + vertex("r1", "DFF_X1", "r1/D", "w1", "^", 0.04470, 0.01908, 1.10913, 0); + } + } + } + } + pin("clk") { + direction : input; + clock : true; + capacitance : 1.8192; + timing() { + timing_sense : positive_unate; + timing_type : min_clock_tree_path; + cell_rise(scalar) { + values("0.00000"); + } + cell_fall(scalar) { + values("0.00000"); + } + } + timing() { + timing_sense : positive_unate; + timing_type : max_clock_tree_path; + cell_rise(scalar) { + values("0.00000"); + } + cell_fall(scalar) { + values("0.00000"); + } + } + } + pin("out") { + direction : output; + capacitance : 0.0000; + timing() { + related_pin : "clk"; + timing_type : rising_edge; + cell_rise(template_1) { + index_1("0.36562, 1.89781, 3.79562, 7.59125, 15.18250, 30.36500, 60.73000"); + values("0.29101,0.30130,0.31279,0.33950,0.39956,0.52522,0.77978"); + } + rise_transition(template_1) { + index_1("0.36562, 1.89781, 3.79562, 7.59125, 15.18250, 30.36500, 60.73000"); + values("0.01964,0.02937,0.04223,0.07118,0.13205,0.25458,0.49990"); + } + cell_fall(template_1) { + index_1("0.36562, 1.89781, 3.79562, 7.59125, 15.18250, 30.36500, 60.73000"); + values("0.23051,0.23926,0.24731,0.26050,0.28251,0.32097,0.39267"); + } + fall_transition(template_1) { + index_1("0.36562, 1.89781, 3.79562, 7.59125, 15.18250, 30.36500, 60.73000"); + values("0.01522,0.01891,0.02276,0.03000,0.04411,0.07298,0.13352"); + } + paths() { + slack : 0.22842; + fall_clocked_output() { + time: 0.22842; + vertex("r2", "DFF_X1", "r2/CK", "clk", "^", 0.00000, 0.00000, 1.64207, 0); + vertex("r2", "DFF_X1", "r2/Q", "out", "v", 0.22842, 0.01434, 0.00000, 1); + vertex("", "timing_cell_dff", "out", "", "v", 0.22842, 0.01434, 0.00000, 0); + } + rise_clocked_output() { + time: 0.28855; + vertex("r2", "DFF_X1", "r2/CK", "clk", "^", 0.00000, 0.00000, 1.81915, 0); + vertex("r2", "DFF_X1", "r2/Q", "out", "^", 0.28855, 0.01732, 0.00000, 1); + vertex("", "timing_cell_dff", "out", "", "^", 0.28855, 0.01732, 0.00000, 0); + } + } + } + } + worst_slack_path() { + slack : 0.09420; + rise_data_arrival() { + time: 0.11136; + vertex("r1", "DFF_X1", "r1/Q", "w2", "^", 0.00000, 0.02325, 0.93456, 1); + vertex("u2", "BUF_X1", "u2/A", "w2", "^", 0.00000, 0.02325, 0.93456, 0); + vertex("u2", "BUF_X1", "u2/Z", "w3", "^", 0.05639, 0.01783, 0.93456, 1); + vertex("u3", "BUF_X1", "u3/A", "w3", "^", 0.05639, 0.01783, 0.93456, 0); + vertex("u3", "BUF_X1", "u3/Z", "w4", "^", 0.11136, 0.01913, 1.10913, 1); + vertex("r2", "DFF_X1", "r2/D", "w4", "^", 0.11136, 0.01913, 1.10913, 0); + } + rise_data_required() { + time: 0.01716; + vertex("r2", "DFF_X1", "r2/CK", "clk", "^", 0.00000, 0.00000, 1.81915, 0); + } + } + } + +} diff --git a/examples/timing_cell_dff.tcl b/examples/timing_cell_dff.tcl new file mode 100644 index 00000000..0981d1b0 --- /dev/null +++ b/examples/timing_cell_dff.tcl @@ -0,0 +1,8 @@ +# timing cell dff example +read_liberty nangate45_slow.lib.gz +read_verilog timing_cell_dff.v +link_design timing_cell_dff +create_clock -name clk -period 10 {clk} +set_input_delay -clock clk 0 {in} +write_timing_model -paths timing_cell_dff.lib +report_checks -path_delay min_rise -from r1/Q \ No newline at end of file diff --git a/examples/timing_cell_dff.v b/examples/timing_cell_dff.v new file mode 100644 index 00000000..9a901819 --- /dev/null +++ b/examples/timing_cell_dff.v @@ -0,0 +1,16 @@ +module timing_cell_dff (in, clk, out); + + input in, clk; + output out; + wire w1, w2, w3, w4; + + BUF_X1 u1 (.A(in), .Z(w1)); + + DFF_X1 r1 (.D(w1), .CK(clk), .Q(w2)); + + BUF_X1 u2 (.A(w2), .Z(w3)); + BUF_X1 u3 (.A(w3), .Z(w4)); + + DFF_X1 r2 (.D(w4), .CK(clk), .Q(out)); + +endmodule diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 1ecb02b9..1001a800 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -36,6 +36,7 @@ #include "Transition.hh" #include "Delay.hh" #include "LibertyClass.hh" +#include "TimingArc.hh" namespace sta { @@ -561,6 +562,13 @@ public: const char *userFunctionClass() const; void setUserFunctionClass(const char *user_function_class); + void setWorstSlackTimingPath(const InputRegisterTimingPath& timing_path, + const MinMax *min_max, + const RiseFall *rise_fall); + const InputRegisterTimingPath &getWorstSlackTimingPath(const MinMax *min_max, + const RiseFall *rise_fall) const; + bool hasWorstSlackTimingPaths() const; + protected: void addPort(ConcretePort *port); void setHasInternalPorts(bool has_internal); @@ -652,6 +660,8 @@ protected: std::mutex waveform_lock_; std::string footprint_; std::string user_function_class_; + float worst_slack_{std::numeric_limits::max()}; + std::array, 2> worst_slack_timing_paths_; private: friend class LibertyLibrary; diff --git a/include/sta/LibertyWriter.hh b/include/sta/LibertyWriter.hh index 1b142af5..61bb5269 100644 --- a/include/sta/LibertyWriter.hh +++ b/include/sta/LibertyWriter.hh @@ -33,6 +33,7 @@ class StaState; void writeLiberty(LibertyLibrary *lib, const char *filename, - StaState *sta); + StaState *sta, + bool write_timing_paths); } // namespace diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index a93843d5..0a2b8f18 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -1248,7 +1248,8 @@ public: const char *cell_name, const char *filename, const Corner *corner, - const bool scalar); + const bool scalar, + const bool paths); // Find equivalent cells in equiv_libs. // Optionally add mappings for cells in map_libs. diff --git a/include/sta/TimingArc.hh b/include/sta/TimingArc.hh index 3884cbd3..09eafd37 100644 --- a/include/sta/TimingArc.hh +++ b/include/sta/TimingArc.hh @@ -25,11 +25,14 @@ #pragma once #include +#include +#include #include "Vector.hh" #include "Transition.hh" #include "Delay.hh" #include "LibertyClass.hh" +#include "TimingRole.hh" namespace sta { @@ -93,6 +96,58 @@ timingTypeScaleFactorType(TimingType type); //////////////////////////////////////////////////////////////// +struct TimingPathVertex +{ + std::string instance; + std::string cell; + std::string pin; + std::string net; + std::string transition; + float arrival; + float slew; + float capacitance; + bool is_driver; +}; + +struct TimingPath +{ + std::string name{}; + std::vector vertices{}; + float time{0.0f}; + const RiseFall* rise_fall; + + struct Names + { + static constexpr std::array DATA_ARRIVAL{"rise_data_arrival", "fall_data_arrival"}; + static constexpr std::array DATA_REQUIRED{"rise_data_required", "fall_data_required"}; + static constexpr std::array CLOCKED_OUTPUT{"rise_clocked_output", "fall_clocked_output"}; + static constexpr std::array COMBINATIONAL{"rise_combinational", "fall_combinational"}; + }; + + inline static const std::unordered_map> ROLE_PATH_MAPPINGS = + { + {TimingRole::regClkToQ(), Names::CLOCKED_OUTPUT}, + {TimingRole::combinational(), Names::COMBINATIONAL}, + {TimingRole::setup(), Names::DATA_ARRIVAL}, + {TimingRole::hold(), Names::DATA_ARRIVAL} + }; +}; + +struct InputRegisterTimingPath +{ + float slack{std::numeric_limits::max()}; + TimingPath data_arrival_path{}; + TimingPath data_required_path{}; +}; + +struct CombinationalTimingPath +{ + float slack{std::numeric_limits::max()}; + TimingPath combinational_delay_path{}; +}; + +//////////////////////////////////////////////////////////////// + class TimingArcAttrs { public: @@ -120,6 +175,11 @@ public: TimingModel *model); float ocvArcDepth() const { return ocv_arc_depth_; } void setOcvArcDepth(float depth); + void setSlack(float slack); + void mergeSlack(float slack); + void addTimingPath(TimingPath timing_path); + float slack() const { return slack_; } + const std::unordered_map& timingPaths() const { return timing_paths_; } protected: TimingType timing_type_; @@ -132,6 +192,8 @@ protected: const char *mode_value_; float ocv_arc_depth_; TimingModel *models_[RiseFall::index_count]; + float slack_{std::numeric_limits::max()}; + std::unordered_map timing_paths_; }; // A timing arc set is a group of related timing arcs between from/to @@ -184,6 +246,10 @@ public: const char *sdfCondEnd() const { return attrs_->sdfCondEnd(); } const char *modeName() const { return attrs_->modeName(); } const char *modeValue() const { return attrs_->modeValue(); } + float slack() const { return attrs_->slack(); } + bool hasTimingPaths() const { return !attrs_->timingPaths().empty(); } + const std::unordered_map& timingPaths() const { return attrs_->timingPaths(); } + // Timing arc set index in cell. TimingArcIndex index() const { return index_; } bool isDisabledConstraint() const { return is_disabled_constraint_; } diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 2dd0cd89..ed0997a9 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -2062,6 +2062,36 @@ LibertyCell::setUserFunctionClass(const char *user_function_class) user_function_class_ = user_function_class; } +void +LibertyCell::setWorstSlackTimingPath(const InputRegisterTimingPath& timing_path, + const MinMax *min_max, + const RiseFall *rise_fall) +{ + worst_slack_ = std::min(timing_path.slack, worst_slack_); + worst_slack_timing_paths_.at(min_max->index()).at(rise_fall->index()) = timing_path; +} + +const InputRegisterTimingPath & +LibertyCell::getWorstSlackTimingPath(const MinMax *min_max, + const RiseFall *rise_fall) const +{ + return worst_slack_timing_paths_.at(min_max->index()).at(rise_fall->index()); +} + +bool +LibertyCell::hasWorstSlackTimingPaths() const +{ + for (const auto& min_max_paths : worst_slack_timing_paths_) { + for (const auto& rise_fall_paths : min_max_paths) { + if (!rise_fall_paths.data_arrival_path.vertices.empty()) { + return true; + } + } + } + + return false; +} + //////////////////////////////////////////////////////////////// LibertyCellPortIterator::LibertyCellPortIterator(const LibertyCell *cell) : diff --git a/liberty/Liberty.i b/liberty/Liberty.i index 562de105..b92ba3c8 100644 --- a/liberty/Liberty.i +++ b/liberty/Liberty.i @@ -127,9 +127,10 @@ read_liberty_cmd(char *filename, void write_liberty_cmd(LibertyLibrary *library, - char *filename) + char *filename, + bool write_timing_paths) { - writeLiberty(library, filename, Sta::sta()); + writeLiberty(library, filename, Sta::sta(), write_timing_paths); } void diff --git a/liberty/Liberty.tcl b/liberty/Liberty.tcl index d4ac84a1..e707af16 100644 --- a/liberty/Liberty.tcl +++ b/liberty/Liberty.tcl @@ -47,7 +47,8 @@ proc write_liberty { args } { set library [get_liberty_error "library" [lindex $args 0]] set filename [file nativename [lindex $args 1]] - write_liberty_cmd $library $filename + set write_timing_paths [info exists flags(-paths)] + write_liberty_cmd $library $filename $write_timing_paths } ################################################################ diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 9f066161..cc1292e5 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -421,6 +421,63 @@ LibertyReader::defineVisitors() defineAttrVisitor("value", &LibertyReader::visitValue); defineAttrVisitor("values", &LibertyReader::visitValues); + // Custom Liberty attrs/groups for handling timing paths + defineAttrVisitor("slack", &LibertyReader::visitSlack); + defineGroupVisitor( + TimingPath::Names::DATA_ARRIVAL.at(RiseFall::riseIndex()), + &LibertyReader::beginRiseTimingPath, + &LibertyReader::endTimingPath); + defineGroupVisitor( + TimingPath::Names::DATA_ARRIVAL.at(RiseFall::fallIndex()), + &LibertyReader::beginFallTimingPath, + &LibertyReader::endTimingPath); + defineGroupVisitor( + TimingPath::Names::DATA_REQUIRED.at(RiseFall::riseIndex()), + &LibertyReader::beginRiseTimingPath, + &LibertyReader::endTimingPath); + defineGroupVisitor( + TimingPath::Names::DATA_REQUIRED.at(RiseFall::fallIndex()), + &LibertyReader::beginFallTimingPath, + &LibertyReader::endTimingPath); + defineGroupVisitor( + TimingPath::Names::CLOCKED_OUTPUT.at(RiseFall::riseIndex()), + &LibertyReader::beginRiseTimingPath, + &LibertyReader::endTimingPath); + defineGroupVisitor( + TimingPath::Names::CLOCKED_OUTPUT.at(RiseFall::fallIndex()), + &LibertyReader::beginFallTimingPath, + &LibertyReader::endTimingPath); + defineGroupVisitor( + TimingPath::Names::COMBINATIONAL.at(RiseFall::riseIndex()), + &LibertyReader::beginRiseTimingPath, + &LibertyReader::endTimingPath); + defineGroupVisitor( + TimingPath::Names::COMBINATIONAL.at(RiseFall::fallIndex()), + &LibertyReader::beginFallTimingPath, + &LibertyReader::endTimingPath); + defineAttrVisitor("time", &LibertyReader::visitTimingPathTime); + defineAttrVisitor("vertex", &LibertyReader::visitTimingPathVertex); + defineGroupVisitor( + "worst_slack_paths", + &LibertyReader::beginRegisterToRegisterTimingPaths, + &LibertyReader::endRegisterToRegisterTimingPaths); + defineGroupVisitor( + "min_rise", + &LibertyReader::beginRegisterToRegisterMinRiseTimingPath, + &LibertyReader::endRegisterToRegisterTimingPath); + defineGroupVisitor( + "min_fall", + &LibertyReader::beginRegisterToRegisterMinFallTimingPath, + &LibertyReader::endRegisterToRegisterTimingPath); + defineGroupVisitor( + "max_rise", + &LibertyReader::beginRegisterToRegisterMaxRiseTimingPath, + &LibertyReader::endRegisterToRegisterTimingPath); + defineGroupVisitor( + "max_fall", + &LibertyReader::beginRegisterToRegisterMaxFallTimingPath, + &LibertyReader::endRegisterToRegisterTimingPath); + defineGroupVisitor("lut", &LibertyReader::beginLut,&LibertyReader::endLut); defineGroupVisitor("test_cell", &LibertyReader::beginTestCell, @@ -4531,6 +4588,115 @@ LibertyReader::beginFallConstraint(LibertyGroup *group) beginTimingTableModel(group, RiseFall::fall(), ScaleFactorType::unknown); } +void +LibertyReader::visitSlack(LibertyAttr *attr) +{ + float slack = library_->units()->timeUnit()->userToSta(attr->firstValue()->floatValue()); + if (timing_) { + timing_->attrs()->setSlack(slack); + } else if (traversing_cell_worst_timing_paths_) { + register_to_register_timing_path_.slack = slack; + } +} + +void +LibertyReader::beginRiseTimingPath(LibertyGroup *group) +{ + timing_path_ = TimingPath{}; + timing_path_.name = group->type(); + timing_path_.rise_fall = RiseFall::rise(); +} + +void +LibertyReader::beginFallTimingPath(LibertyGroup *group) +{ + timing_path_ = TimingPath{}; + timing_path_.name = group->type(); + timing_path_.rise_fall = RiseFall::fall(); +} + +void +LibertyReader::visitTimingPathTime(LibertyAttr *attr) +{ + timing_path_.time = library_->units()->timeUnit()->userToSta(attr->firstValue()->floatValue()); +} + +void +LibertyReader::visitTimingPathVertex(LibertyAttr *attr) +{ + TimingPathVertex vertex{}; + LibertyAttrValueSeq* values = attr->values(); + vertex.instance = values->at(0)->stringValue(); + vertex.cell = values->at(1)->stringValue(); + vertex.pin = values->at(2)->stringValue(); + vertex.net = values->at(3)->stringValue(); + vertex.transition = values->at(4)->stringValue(); + vertex.arrival = library_->units()->timeUnit()->userToSta(values->at(5)->floatValue()); + vertex.slew = library_->units()->timeUnit()->userToSta(values->at(6)->floatValue()); + vertex.capacitance = library_->units()->capacitanceUnit()->userToSta(values->at(7)->floatValue()); + vertex.is_driver = values->at(8)->floatValue() > 0.5f; + timing_path_.vertices.emplace_back(vertex); +} + +void +LibertyReader::endTimingPath(LibertyGroup *) +{ + if (timing_) { + timing_->attrs()->addTimingPath(std::move(timing_path_)); + } else if (traversing_cell_worst_timing_paths_) { + if (timing_path_.name.rfind("data_arrival") != std::string::npos) { + register_to_register_timing_path_.data_arrival_path = std::move(timing_path_); + } else { + register_to_register_timing_path_.data_required_path = std::move(timing_path_); + } + } +} + +void +LibertyReader::beginRegisterToRegisterTimingPaths(LibertyGroup *) +{ + traversing_cell_worst_timing_paths_ = true; +} + +void +LibertyReader::endRegisterToRegisterTimingPaths(LibertyGroup *) +{ + traversing_cell_worst_timing_paths_ = false; +} + +void LibertyReader::beginRegisterToRegisterMinRiseTimingPath(LibertyGroup *) +{ + register_to_register_timing_path_ = InputRegisterTimingPath{}; + timing_path_min_max_ = MinMax::min(); + timing_path_rise_fall_ = RiseFall::rise(); +} + +void LibertyReader::beginRegisterToRegisterMinFallTimingPath(LibertyGroup *) +{ + register_to_register_timing_path_ = InputRegisterTimingPath{}; + timing_path_min_max_ = MinMax::min(); + timing_path_rise_fall_ = RiseFall::fall(); +} + +void LibertyReader::beginRegisterToRegisterMaxRiseTimingPath(LibertyGroup *) +{ + register_to_register_timing_path_ = InputRegisterTimingPath{}; + timing_path_min_max_ = MinMax::max(); + timing_path_rise_fall_ = RiseFall::rise(); +} + +void LibertyReader::beginRegisterToRegisterMaxFallTimingPath(LibertyGroup *) +{ + register_to_register_timing_path_ = InputRegisterTimingPath{}; + timing_path_min_max_ = MinMax::max(); + timing_path_rise_fall_ = RiseFall::fall(); +} + +void LibertyReader::endRegisterToRegisterTimingPath(LibertyGroup *) +{ + cell_->setWorstSlackTimingPath(register_to_register_timing_path_, timing_path_min_max_, timing_path_rise_fall_); +} + void LibertyReader::endRiseFallConstraint(LibertyGroup *group) { diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 28751eac..9ea78069 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -365,6 +365,19 @@ public: virtual void beginRiseConstraint(LibertyGroup *group); virtual void endRiseFallConstraint(LibertyGroup *group); virtual void beginFallConstraint(LibertyGroup *group); + virtual void visitSlack(LibertyAttr *attr); + virtual void beginRiseTimingPath(LibertyGroup *group); + virtual void beginFallTimingPath(LibertyGroup *group); + virtual void endTimingPath(LibertyGroup *group); + virtual void beginRegisterToRegisterTimingPaths(LibertyGroup *group); + virtual void endRegisterToRegisterTimingPaths(LibertyGroup *group); + virtual void beginRegisterToRegisterMinRiseTimingPath(LibertyGroup *group); + virtual void beginRegisterToRegisterMinFallTimingPath(LibertyGroup *group); + virtual void beginRegisterToRegisterMaxRiseTimingPath(LibertyGroup *group); + virtual void beginRegisterToRegisterMaxFallTimingPath(LibertyGroup *group); + virtual void endRegisterToRegisterTimingPath(LibertyGroup *group); + virtual void visitTimingPathTime(LibertyAttr *attr); + virtual void visitTimingPathVertex(LibertyAttr *attr); virtual void beginRiseTransitionDegredation(LibertyGroup *group); virtual void beginFallTransitionDegredation(LibertyGroup *group); @@ -641,6 +654,11 @@ protected: SequentialGroupSeq cell_sequentials_; StatetableGroup *statetable_; TimingGroup *timing_; + TimingPath timing_path_; + InputRegisterTimingPath register_to_register_timing_path_{}; + bool traversing_cell_worst_timing_paths_{false}; + const MinMax *timing_path_min_max_{nullptr}; + const RiseFall *timing_path_rise_fall_{nullptr}; InternalPowerGroup *internal_power_; LeakagePowerGroup *leakage_power_; LeakagePowerGroupSeq leakage_powers_; diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc index e3d31312..b2aebf12 100644 --- a/liberty/LibertyWriter.cc +++ b/liberty/LibertyWriter.cc @@ -35,6 +35,7 @@ #include "TimingArc.hh" #include "TimingModel.hh" #include "TableModel.hh" +#include "Sta.hh" #include "StaState.hh" namespace sta { @@ -47,7 +48,8 @@ class LibertyWriter LibertyWriter(const LibertyLibrary *lib, const char *filename, FILE *stream, - Report *report); + Report *report, + bool write_timing_paths); void writeLibrary(); protected: @@ -72,6 +74,7 @@ class LibertyWriter int index); void writeTableAxis10(const TableAxis *axis, int index); + void writeTimingPath(int indent, const TimingPath &timing_path); const char *asString(bool value); const char *asString(const PortDirection *dir); @@ -85,16 +88,18 @@ class LibertyWriter Report *report_; const Unit *time_unit_; const Unit *cap_unit_; + bool write_timing_paths_; }; void writeLiberty(LibertyLibrary *lib, const char *filename, - StaState *sta) + StaState *sta, + bool write_timing_paths) { FILE *stream = fopen(filename, "w"); if (stream) { - LibertyWriter writer(lib, filename, stream, sta->report()); + LibertyWriter writer(lib, filename, stream, sta->report(), write_timing_paths); writer.writeLibrary(); fclose(stream); } @@ -105,13 +110,15 @@ writeLiberty(LibertyLibrary *lib, LibertyWriter::LibertyWriter(const LibertyLibrary *lib, const char *filename, FILE *stream, - Report *report) : + Report *report, + bool write_timing_paths) : library_(lib), filename_(filename), stream_(stream), report_(report), time_unit_(lib->units()->timeUnit()), - cap_unit_(lib->units()->capacitanceUnit()) + cap_unit_(lib->units()->capacitanceUnit()), + write_timing_paths_(write_timing_paths) { } @@ -321,10 +328,47 @@ LibertyWriter::writeCell(const LibertyCell *cell) } } + if (write_timing_paths_ && cell->hasWorstSlackTimingPaths()) { + fprintf(stream_, " worst_slack_paths() {\n"); + for (const MinMax *min_max : MinMax::range()) { + for (const RiseFall *rise_fall : RiseFall::range()) { + fprintf(stream_, " %s_%s() {\n", min_max->to_string().c_str(), rise_fall->name()); + auto& timing_path = cell->getWorstSlackTimingPath(min_max, rise_fall); + fprintf(stream_, " slack : %s;\n", time_unit_->asString(timing_path.slack, 5)); + writeTimingPath(8, timing_path.data_arrival_path); + writeTimingPath(8, timing_path.data_required_path); + fprintf(stream_, " }\n"); + } + } + fprintf(stream_, " }\n"); + } + fprintf(stream_, " }\n"); fprintf(stream_, "\n"); } +void +LibertyWriter::writeTimingPath(int indent, const TimingPath &timing_path) +{ + fprintf(stream_, "%*s%s() {\n", indent, " ", timing_path.name.c_str()); + fprintf(stream_, "%*s time: %s;\n", indent, " ", time_unit_->asString(timing_path.time, 5)); + for (auto& vertex : timing_path.vertices) { + fprintf(stream_, "%*s vertex(\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", %s, %s, %s, %d);\n", + indent, + " ", + vertex.instance.c_str(), + vertex.cell.c_str(), + vertex.pin.c_str(), + vertex.net.c_str(), + vertex.transition.c_str(), + time_unit_->asString(vertex.arrival, 5), + time_unit_->asString(vertex.slew, 5), + cap_unit_->asString(vertex.capacitance, 5), + vertex.is_driver); + } + fprintf(stream_, "%*s}\n", indent, " "); +} + void LibertyWriter::writeBusPort(const LibertyPort *port) { @@ -435,6 +479,31 @@ LibertyWriter::writeTimingArcSet(const TimingArcSet *arc_set) } } + // Custom Liberty attrs/groups for handling timing paths + if (write_timing_paths_ && arc_set->hasTimingPaths()) { + fprintf(stream_, " paths() {\n"); + fprintf(stream_, " slack : %s;\n", time_unit_->asString(arc_set->slack(), 5)); + + for (auto& [_, timing_path] : arc_set->timingPaths()) { + fprintf(stream_, " %s() {\n", timing_path.name.c_str()); + fprintf(stream_, " time: %s;\n", time_unit_->asString(timing_path.time, 5)); + for (auto& vertex : timing_path.vertices) { + fprintf(stream_, " vertex(\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", %s, %s, %s, %d);\n", + vertex.instance.c_str(), + vertex.cell.c_str(), + vertex.pin.c_str(), + vertex.net.c_str(), + vertex.transition.c_str(), + time_unit_->asString(vertex.arrival, 5), + time_unit_->asString(vertex.slew, 5), + cap_unit_->asString(vertex.capacitance, 5), + vertex.is_driver); + } + fprintf(stream_, " }\n"); + } + fprintf(stream_, " }\n"); + } + fprintf(stream_, " }\n"); } diff --git a/liberty/TimingArc.cc b/liberty/TimingArc.cc index b03b5cc6..d4e29a85 100644 --- a/liberty/TimingArc.cc +++ b/liberty/TimingArc.cc @@ -162,6 +162,24 @@ TimingArcAttrs::setOcvArcDepth(float depth) ocv_arc_depth_ = depth; } +void +TimingArcAttrs::setSlack(float slack) +{ + slack_ = slack; +} + +void +TimingArcAttrs::mergeSlack(float slack) +{ + slack_ = std::min(slack, slack_); +} + +void TimingArcAttrs::addTimingPath(TimingPath timing_path) +{ + std::string name = timing_path.name; + timing_paths_[std::move(name)] = std::move(timing_path); +} + float TimingArc::driveResistance() const { diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index 7529cfb3..e9601318 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -49,6 +49,10 @@ #include "VisitPathEnds.hh" #include "ArcDelayCalc.hh" #include "ClkLatency.hh" +#include "PathExpanded.hh" +#include "PathAnalysisPt.hh" +#include "Latches.hh" +#include "ClkInfo.hh" namespace sta { @@ -63,9 +67,10 @@ makeTimingModel(const char *lib_name, const char *filename, const Corner *corner, const bool scalar, + const bool write_timing_paths, Sta *sta) { - MakeTimingModel maker(lib_name, cell_name, filename, corner, scalar, sta); + MakeTimingModel maker(lib_name, cell_name, filename, corner, scalar, write_timing_paths, sta); return maker.makeTimingModel(); } @@ -74,6 +79,7 @@ MakeTimingModel::MakeTimingModel(const char *lib_name, const char *filename, const Corner *corner, const bool scalar, + const bool write_timing_paths, Sta *sta) : StaState(sta), lib_name_(lib_name), @@ -81,6 +87,7 @@ MakeTimingModel::MakeTimingModel(const char *lib_name, filename_(filename), corner_(corner), scalar_(scalar), + write_timing_paths_(write_timing_paths), cell_(nullptr), min_max_(MinMax::max()), lib_builder_(new LibertyBuilder), @@ -110,6 +117,7 @@ MakeTimingModel::makeTimingModel() findTimingFromInputs(); findClkedOutputPaths(); + findWorstSlackInternalPath(); findClkTreeDelays(); cell_->finish(false, report_, debug_); @@ -242,26 +250,125 @@ MakeTimingModel::checkClock(Clock *clk) //////////////////////////////////////////////////////////////// +TimingPathVertex extractTimingPathVertexDescription(StaState *sta_state, + const RiseFall *rise_fall, + DcalcAnalysisPt *dcalc_ap, + const Path *path_element, + bool is_clock_propagated) +{ + Vertex *vertex = path_element->vertex(sta_state); + Pin *pin = vertex->pin(); + + bool is_clk = path_element->isClock(sta_state->search()); + + Instance *inst = sta_state->network()->instance(pin); + + TimingPathVertex timing_path_vertex{}; + + timing_path_vertex.instance = sta_state->network()->pathName(inst); + if (auto net = sta_state->network()->net(pin)) { + timing_path_vertex.net = sta_state->sdcNetwork()->pathName(net); + } + timing_path_vertex.pin = sta_state->cmdNetwork()->pathName(pin); + timing_path_vertex.cell = sta_state->network()->cellName(inst); + + timing_path_vertex.arrival = path_element->arrival(); + if (is_clk && !is_clock_propagated) { + timing_path_vertex.arrival = 0.0f; + } + + timing_path_vertex.slew = path_element->slew(sta_state); + + timing_path_vertex.capacitance = sta_state->graphDelayCalc()->loadCap(pin, rise_fall, dcalc_ap); + + const RiseFall* vertex_rise_fall = path_element->transition(sta_state); + timing_path_vertex.transition = vertex_rise_fall->shortName(); + + timing_path_vertex.is_driver = sta_state->network()->isDriver(pin); + + return timing_path_vertex; +} + +std::vector extractTimingPathVertices(const Path *path, const RiseFall *rise_fall, bool skip_clk_vertex) +{ + StaState* sta_state = Sta::sta(); + + PathExpanded expanded(path, sta_state); + std::size_t path_first_index = skip_clk_vertex ? 1 : 0; + std::size_t path_last_index = expanded.size() - 1; + + bool is_clock_propagated = path->clkInfo(sta_state->search())->isPropagated(); + const Path *clk_path = expanded.clkPath(); + Vertex *clk_start = clk_path ? clk_path->vertex(sta_state) : nullptr; + + DcalcAnalysisPt *dcalc_ap = path->pathAnalysisPt(sta_state)->dcalcAnalysisPt(); + + std::vector vertices; + vertices.reserve(path_last_index - path_first_index + 1); + for (std::size_t i = path_first_index; i <= path_last_index; ++i) { + const Path *path_element = expanded.path(i); + + bool is_clk_start = path_element->vertex(sta_state) == clk_start; + bool is_clk = path_element->isClock(sta_state->search()); + + if (is_clk && !is_clock_propagated && !is_clk_start) { + continue; + } + + TimingPathVertex timing_path_vertex = extractTimingPathVertexDescription(sta_state, rise_fall, dcalc_ap, path_element, is_clock_propagated); + vertices.emplace_back(timing_path_vertex); + } + + return vertices; +} + +std::vector extractDataRequiredTimingPathVertices(const Path *path, const RiseFall *rise_fall) +{ + StaState* sta_state = Sta::sta(); + + PathExpanded expanded(path, sta_state); + std::size_t path_last_index = expanded.size() - 1; + + bool is_clock_propagated = path->clkInfo(sta_state->search())->isPropagated(); + std::size_t path_first_index = is_clock_propagated ? 1 : path_last_index; + + DcalcAnalysisPt *dcalc_ap = path->pathAnalysisPt(sta_state)->dcalcAnalysisPt(); + + std::vector vertices; + vertices.reserve(path_last_index - path_first_index + 1); + for (std::size_t i = path_first_index; i <= path_last_index; ++i) { + const Path *path_element = expanded.path(i); + TimingPathVertex timing_path_vertex = extractTimingPathVertexDescription(sta_state, rise_fall, dcalc_ap, path_element, is_clock_propagated); + vertices.emplace_back(timing_path_vertex); + } + + return vertices; +} + class MakeEndTimingArcs : public PathEndVisitor { public: - MakeEndTimingArcs(Sta *sta); + MakeEndTimingArcs(Sta *sta, bool export_paths); MakeEndTimingArcs(const MakeEndTimingArcs&) = default; virtual ~MakeEndTimingArcs() {} virtual PathEndVisitor *copy() const; virtual void visit(PathEnd *path_end); void setInputRf(const RiseFall *input_rf); const ClockEdgeDelays &margins() const { return margins_; } + const InputRegisterTimingPaths& extractedTimingPaths() const { return timing_paths_; } private: const RiseFall *input_rf_; ClockEdgeDelays margins_; + InputRegisterTimingPaths timing_paths_; Sta *sta_; + bool write_timing_paths_; }; -MakeEndTimingArcs::MakeEndTimingArcs(Sta *sta) : +MakeEndTimingArcs::MakeEndTimingArcs(Sta *sta, bool export_paths) : input_rf_(nullptr), - sta_(sta) + sta_(sta), + write_timing_paths_(export_paths) { } @@ -277,6 +384,32 @@ MakeEndTimingArcs::setInputRf(const RiseFall *input_rf) input_rf_ = input_rf; } +InputRegisterTimingPath +extractInputRegisterTimingPath(PathEnd *path_end, const RiseFall *input_rf) +{ + StaState* sta_state = Sta::sta(); + + InputRegisterTimingPath input_register_timing_path{}; + + const Path *path = path_end->path(); + static constexpr bool INCLUDE_CLOCK_VERTEX = false; + input_register_timing_path.data_arrival_path.vertices = extractTimingPathVertices(path, input_rf, INCLUDE_CLOCK_VERTEX); + input_register_timing_path.data_arrival_path.time = path_end->dataArrivalTime(sta_state); + input_register_timing_path.data_arrival_path.rise_fall = input_rf; + input_register_timing_path.data_arrival_path.name = TimingPath::Names::DATA_ARRIVAL.at(input_rf->index()); + + const Path *clock_path = path_end->targetClkPath(); + input_register_timing_path.data_required_path.vertices = extractDataRequiredTimingPathVertices(clock_path, input_rf); + input_register_timing_path.data_required_path.time = path_end->requiredTime(sta_state); + input_register_timing_path.data_required_path.rise_fall = input_rf; + input_register_timing_path.data_required_path.name = TimingPath::Names::DATA_REQUIRED.at(input_rf->index()); + + const EarlyLate *early_late = nullptr; + input_register_timing_path.slack = delayAsFloat(path_end->slack(sta_state), early_late, sta_state); + + return input_register_timing_path; +} + void MakeEndTimingArcs::visit(PathEnd *path_end) { @@ -305,6 +438,13 @@ MakeEndTimingArcs::visit(PathEnd *path_end) if (debug->check("make_timing_model", 3)) sta_->reportPathEnd(path_end); + if (write_timing_paths_) { + InputRegisterTimingPath timing_path = extractInputRegisterTimingPath(path_end, input_rf_); + if (timing_path.slack < timing_paths_[min_max->index()][input_rf_->index()].slack) { + timing_paths_[min_max->index()][input_rf_->index()] = std::move(timing_path); + } + } + RiseFallMinMax &margins = margins_[tgt_clk_edge]; float max_margin; bool max_exists; @@ -341,8 +481,9 @@ MakeTimingModel::findTimingFromInput(Port *input_port) Instance *top_inst = network_->topInstance(); Pin *input_pin = network_->findPin(top_inst, input_port); if (!sta_->isClockSrc(input_pin)) { - MakeEndTimingArcs end_visitor(sta_); + MakeEndTimingArcs end_visitor(sta_, write_timing_paths_); OutputPinDelays output_delays; + CombinationalTimingPaths timing_paths; for (const RiseFall *input_rf : RiseFall::range()) { const RiseFallBoth *input_rf1 = input_rf->asRiseFallBoth(); sta_->setInputDelay(input_pin, input_rf1, @@ -361,7 +502,7 @@ MakeTimingModel::findTimingFromInput(Port *input_port) VisitPathEnds visit_ends(sta_); for (Vertex *end : endpoints) visit_ends.visitPathEnds(end, corner_, MinMaxAll::all(), true, &end_visitor); - findOutputDelays(input_rf, output_delays); + findOutputDelays(input_rf, output_delays, timing_paths); search_->deleteFilteredArrivals(); sta_->removeInputDelay(input_pin, input_rf1, @@ -369,14 +510,15 @@ MakeTimingModel::findTimingFromInput(Port *input_port) sdc_->defaultArrivalClockEdge()->transition(), MinMaxAll::all()); } - makeSetupHoldTimingArcs(input_pin, end_visitor.margins()); - makeInputOutputTimingArcs(input_pin, output_delays); + makeSetupHoldTimingArcs(input_pin, end_visitor.margins(), end_visitor.extractedTimingPaths()); + makeInputOutputTimingArcs(input_pin, output_delays, timing_paths); } } void MakeTimingModel::findOutputDelays(const RiseFall *input_rf, - OutputPinDelays &output_pin_delays) + OutputPinDelays &output_pin_delays, + CombinationalTimingPaths &combinational_timing_paths) { InstancePinIterator *output_iter = network_->pinIterator(network_->topInstance()); while (output_iter->hasNext()) { @@ -394,6 +536,20 @@ MakeTimingModel::findOutputDelays(const RiseFall *input_rf, delays.delays.mergeValue(output_rf, min_max, delayAsFloat(delay, min_max, sta_)); delays.rf_path_exists[input_rf->index()][output_rf->index()] = true; + + CombinationalTimingPath &timing_path = combinational_timing_paths[output_pin][input_rf->index()]; + + Clock* clock = sdc_->clocks()->front(); + float slack = clock->period() - delay; + if (write_timing_paths_ && slack < timing_path.slack) { + timing_path.slack = slack; + timing_path.combinational_delay_path.name = TimingPath::Names::COMBINATIONAL.at(output_rf->index()); + timing_path.combinational_delay_path.rise_fall = input_rf; + + static constexpr bool INCLUDE_CLOCK_VERTEX = false; + timing_path.combinational_delay_path.vertices = extractTimingPathVertices(path, input_rf, INCLUDE_CLOCK_VERTEX); + timing_path.combinational_delay_path.time = path->arrival(); + } } } } @@ -403,7 +559,8 @@ MakeTimingModel::findOutputDelays(const RiseFall *input_rf, void MakeTimingModel::makeSetupHoldTimingArcs(const Pin *input_pin, - const ClockEdgeDelays &clk_margins) + const ClockEdgeDelays &clk_margins, + const InputRegisterTimingPaths& timing_paths) { for (const auto& [clk_edge, margins] : clk_margins) { for (const MinMax *min_max : MinMax::range()) { @@ -427,6 +584,13 @@ MakeTimingModel::makeSetupHoldTimingArcs(const Pin *input_pin, if (attrs == nullptr) attrs = std::make_shared(); attrs->setModel(input_rf, check_model); + + if (write_timing_paths_) { + const InputRegisterTimingPath& timing_path = timing_paths[min_max->index()][input_rf->index()]; + attrs->mergeSlack(timing_path.slack); + attrs->addTimingPath(timing_path.data_arrival_path); + attrs->addTimingPath(timing_path.data_required_path); + } } } if (attrs) { @@ -450,7 +614,8 @@ MakeTimingModel::makeSetupHoldTimingArcs(const Pin *input_pin, void MakeTimingModel::makeInputOutputTimingArcs(const Pin *input_pin, - OutputPinDelays &output_pin_delays) + OutputPinDelays &output_pin_delays, + CombinationalTimingPaths &combinational_timing_paths) { for (const auto& [output_pin, output_delays] : output_pin_delays) { TimingArcAttrsPtr attrs = nullptr; @@ -473,9 +638,14 @@ MakeTimingModel::makeInputOutputTimingArcs(const Pin *input_pin, if (attrs == nullptr) attrs = std::make_shared(); attrs->setModel(output_rf, gate_model); + if (write_timing_paths_) { + attrs->mergeSlack(combinational_timing_paths.at(output_pin)[output_rf->index()].slack); + attrs->addTimingPath(combinational_timing_paths.at(output_pin)[output_rf->index()].combinational_delay_path); + } } } if (attrs) { + LibertyPort *output_port = modelPort(output_pin); LibertyPort *input_port = modelPort(input_pin); if (output_port && input_port) { @@ -495,9 +665,10 @@ MakeTimingModel::findClkedOutputPaths() { InstancePinIterator *output_iter = network_->pinIterator(network_->topInstance()); while (output_iter->hasNext()) { - Pin *output_pin = output_iter->next(); + Pin *output_pin = output_iter->next(); if (network_->direction(output_pin)->isOutput()) { ClockEdgeDelays clk_delays; + std::unordered_map timing_paths; LibertyPort *output_port = modelPort(output_pin); Vertex *output_vertex = graph_->pinLoadVertex(output_pin); VertexPathIterator path_iter(output_vertex, this); @@ -511,6 +682,21 @@ MakeTimingModel::findClkedOutputPaths() RiseFallMinMax &delays = clk_delays[clk_edge]; delays.mergeValue(output_rf, min_max, delayAsFloat(delay, min_max, sta_)); + + CombinationalTimingPath timing_path{}; + + Arrival arrival = path->arrival(); + Required required = path->required(); + timing_path.slack = arrival - required; + if (write_timing_paths_ && (timing_paths.count(output_rf) == 0 || timing_path.slack < timing_paths.at(output_rf).slack)) { + timing_path.combinational_delay_path.name = TimingPath::Names::CLOCKED_OUTPUT.at(output_rf->index()); + + static constexpr bool SKIP_CLOCK_VERTEX = true; + timing_path.combinational_delay_path.vertices = extractTimingPathVertices(path, output_rf, SKIP_CLOCK_VERTEX); + timing_path.combinational_delay_path.time = delay; + timing_path.combinational_delay_path.rise_fall = output_rf; + timing_paths[output_rf] = std::move(timing_path); + } } } for (const auto& [clk_edge, delays] : clk_delays) { @@ -532,7 +718,12 @@ MakeTimingModel::findClkedOutputPaths() if (attrs == nullptr) attrs = std::make_shared(); attrs->setModel(output_rf, gate_model); + if (write_timing_paths_) { + attrs->mergeSlack(timing_paths.at(output_rf).slack); + attrs->addTimingPath(timing_paths.at(output_rf).combinational_delay_path); + } } + if (attrs) { lib_builder_->makeFromTransitionArcs(cell_, clk_port, output_port, nullptr, @@ -549,6 +740,161 @@ MakeTimingModel::findClkedOutputPaths() //////////////////////////////////////////////////////////////// +class FindRegTimingArcs : public PathEndVisitor +{ +public: + FindRegTimingArcs(Sta *sta); + FindRegTimingArcs(const FindRegTimingArcs&) = default; + virtual ~FindRegTimingArcs() {} + virtual PathEndVisitor *copy() const; + virtual void visit(PathEnd *path_end); + void setInputRf(const RiseFall *input_rf); + void mergeSlack(const InputRegisterTimingPath &timing_path, + const MinMax *min_max, + const RiseFall *rise_fall); + float slack() const { return slack_; } + const InputRegisterTimingPath &timingPath(const MinMax *min_max, + const RiseFall *rise_fall) const; + +private: + const RiseFall *input_rf_; + Sta *sta_; + float slack_{std::numeric_limits::max()}; + std::array, 2> timing_paths_; +}; + +FindRegTimingArcs::FindRegTimingArcs(Sta *sta) : + input_rf_(nullptr), + sta_(sta) +{ +} + +PathEndVisitor * +FindRegTimingArcs::copy() const +{ + return new FindRegTimingArcs(*this); +} + +void +FindRegTimingArcs::setInputRf(const RiseFall *input_rf) +{ + input_rf_ = input_rf; +} + +void +FindRegTimingArcs::visit(PathEnd *path_end) +{ + InputRegisterTimingPath timing_path = extractInputRegisterTimingPath(path_end, input_rf_); + mergeSlack(timing_path, path_end->minMax(sta_), input_rf_); +} + +void +FindRegTimingArcs::mergeSlack(const InputRegisterTimingPath &timing_path, + const MinMax *min_max, + const RiseFall *rise_fall) +{ + InputRegisterTimingPath &existing_timing_path = timing_paths_.at(min_max->index()).at(rise_fall->index()); + if (timing_path.slack < existing_timing_path.slack) { + slack_ = std::min(timing_path.slack, slack_); + existing_timing_path = std::move(timing_path); + } +} + +const InputRegisterTimingPath & +FindRegTimingArcs::timingPath(const MinMax *min_max, const RiseFall *rise_fall) const +{ + return timing_paths_.at(min_max->index()).at(rise_fall->index()); +} + +bool isRegisterInput(const TimingArcSet *timing_arc_set) +{ + return timing_arc_set && timing_arc_set->role() == TimingRole::regClkToQ(); +} + +void +MakeTimingModel::findWorstSlackInternalPath() +{ + if (!write_timing_paths_) { + return; + } + + search_->deleteFilteredArrivals(); + + FindRegTimingArcs end_visitor(sta_); + Instance *top_inst = network_->topInstance(); + LeafInstanceIterator *instance_iterator = network_->leafInstanceIterator(top_inst); + while (instance_iterator->hasNext()) { + Instance *instance = instance_iterator->next(); + InstancePinIterator *instance_pin_iterator = network_->pinIterator(instance); + + std::vector input_pins{}; + std::vector output_pins{}; + + while (instance_pin_iterator->hasNext()) { + Pin *instance_pin = instance_pin_iterator->next(); + const char *instance_pin_name = network_->name(instance_pin); + if (network_->direction(instance_pin)->isInput()) { + Vertex *vertex = graph_->vertex(network_->vertexId(instance_pin)); + VertexOutEdgeIterator out_edge_iter(vertex, graph_); + while (out_edge_iter.hasNext()) { + Edge *out_edge = out_edge_iter.next(); + if (isRegisterInput(out_edge->timingArcSet())) { + input_pins.emplace_back(instance_pin); + break; + } + } + } else if (network_->direction(instance_pin)->isOutput()) { + output_pins.emplace_back(instance_pin); + } + } + + if (input_pins.empty()) { + continue; + } + + for (auto& register_input_pin : input_pins) { + for (auto& register_output_pin : output_pins) { + const char *input_pin_name = network_->name(register_input_pin); + const char *output_pin_name = network_->name(register_output_pin); + + for (const RiseFall *input_rf : RiseFall::range()) { + const RiseFallBoth *input_rf1 = input_rf->asRiseFallBoth(); + sta_->setInputDelay(register_input_pin, input_rf1, + sdc_->defaultArrivalClock(), + sdc_->defaultArrivalClockEdge()->transition(), + nullptr, false, false, MinMaxAll::all(), true, 0.0); + + PinSet *from_pins = new PinSet(network_); + from_pins->insert(register_input_pin); + ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr, RiseFallBoth::rise()); + + ExceptionTo *to = sta_->makeExceptionTo(nullptr, nullptr, nullptr, input_rf1, input_rf1); + + search_->findFilteredArrivals(from, nullptr, to, false, false); + + end_visitor.setInputRf(input_rf); + VertexSeq endpoints = search_->filteredEndpoints(); + VisitPathEnds visit_ends(sta_); + for (Vertex *end : endpoints) { + visit_ends.visitPathEnds(end, corner_, MinMaxAll::all(), true, &end_visitor); + } + search_->deleteFilteredArrivals(); + } + } + } + } + + for (const MinMax *min_max : MinMax::range()) { + for (const RiseFall *rise_fall : RiseFall::range()) { + cell_->setWorstSlackTimingPath(end_visitor.timingPath(min_max, rise_fall), min_max, rise_fall); + } + } + + delete instance_iterator; +} + +//////////////////////////////////////////////////////////////// + void MakeTimingModel::findClkTreeDelays() { diff --git a/search/MakeTimingModel.hh b/search/MakeTimingModel.hh index 4db2c87c..95d5ed23 100644 --- a/search/MakeTimingModel.hh +++ b/search/MakeTimingModel.hh @@ -36,6 +36,7 @@ makeTimingModel(const char *lib_name, const char *filename, const Corner *corner, const bool scalar, + const bool paths, Sta *sta); } // namespace diff --git a/search/MakeTimingModelPvt.hh b/search/MakeTimingModelPvt.hh index 2b976a0f..c5495d3c 100644 --- a/search/MakeTimingModelPvt.hh +++ b/search/MakeTimingModelPvt.hh @@ -25,12 +25,15 @@ #pragma once #include +#include +#include #include "LibertyClass.hh" #include "SdcClass.hh" #include "SearchClass.hh" #include "StaState.hh" #include "RiseFallMinMax.hh" +#include "TimingArc.hh" namespace sta { @@ -50,6 +53,8 @@ public: typedef std::map ClockEdgeDelays; typedef std::map OutputPinDelays; +typedef std::unordered_map> CombinationalTimingPaths; +typedef std::array, 2> InputRegisterTimingPaths; class MakeTimingModel : public StaState { @@ -59,6 +64,7 @@ public: const char *filename, const Corner *corner, const bool scalar, + const bool paths, Sta *sta); ~MakeTimingModel(); LibertyLibrary *makeTimingModel(); @@ -72,17 +78,21 @@ private: void findTimingFromInputs(); void findTimingFromInput(Port *input_port); void findClkedOutputPaths(); + void findWorstSlackInternalPath(); void findClkTreeDelays(); void makeClkTreePaths(LibertyPort *lib_port, const MinMax *min_max, TimingSense sense, const ClkDelays &delays); void findOutputDelays(const RiseFall *input_rf, - OutputPinDelays &output_pin_delays); + OutputPinDelays &output_pin_delays, + CombinationalTimingPaths &combinational_timing_paths); void makeSetupHoldTimingArcs(const Pin *input_pin, - const ClockEdgeDelays &clk_margins); + const ClockEdgeDelays &clk_margins, + const InputRegisterTimingPaths& timing_paths); void makeInputOutputTimingArcs(const Pin *input_pin, - OutputPinDelays &output_pin_delays); + OutputPinDelays &output_pin_delays, + CombinationalTimingPaths &combinational_timing_paths); TimingModel *makeScalarCheckModel(float value, ScaleFactorType scale_factor_type, const RiseFall *rf); @@ -108,6 +118,7 @@ private: const char *filename_; const Corner *corner_; const bool scalar_; + const bool write_timing_paths_; LibertyLibrary *library_; LibertyCell *cell_; const MinMax *min_max_; diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 5f8d5fe0..b5fc5227 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -550,7 +550,7 @@ ReportPath::reportFull(const PathEndLatchCheck *end) const // Based on reportSrcPath. reportPathHeader(); reportPath3(end->path(), expanded, false, false, 0.0, - end->sourceClkOffset(this)); + end->sourceClkOffset(this), end->checkArc()); } else reportSrcPath(end, expanded); @@ -643,7 +643,8 @@ ReportPath::reportBorrowing(const PathEndLatchCheck *end, auto width_msg = stdstrPrint("%s pulse width", tgt_clk_name.c_str()); reportLineTotal(width_msg.c_str(), tgt_clk_width, early_late); } - ArcDelay margin = end->margin(this); + float required_diff = calculateRequired(end) - end->requiredTime(this); + ArcDelay margin = end->margin(this) - required_diff; reportLineTotalMinus("library setup time", margin, early_late); reportDashLineTotal(); if (!delayZero(crpr_diff)) @@ -717,7 +718,7 @@ ReportPath::reportFull(const PathEndPathDelay *end) const // Based on reportSrcPath. reportPathHeader(); reportPath3(end->path(), expanded, false, false, 0.0, - end->sourceClkOffset(this)); + end->sourceClkOffset(this), end->checkArc()); } else reportSrcPath(end, expanded); @@ -939,7 +940,7 @@ ReportPath::reportFull(const PathEndDataCheck *end) const clk_expanded.size() - 1, data_clk_path->clkInfo(search_)->isPropagated(), false, // Delay to startpoint is already included. - prev, offset); + prev, offset, end->checkArc()); } reportRequired(end, checkRoleReason(end)); reportSlack(end); @@ -1110,7 +1111,7 @@ ReportPath::reportJson(const PathEnd *end, stringAppend(result, " \"source_clock_edge\": \"%s\",\n", src_clk_edge->transition()->name()); } - reportJson(expanded, "source_path", 2, !end->isUnconstrained(), result); + reportJson(expanded, "source_path", 2, !end->isUnconstrained(), result, end->checkArc(), false); const ClockEdge *tgt_clk_edge = end->targetClkEdge(this); if (tgt_clk_edge) { @@ -1120,11 +1121,11 @@ ReportPath::reportJson(const PathEnd *end, tgt_clk_edge->transition()->name()); } if (tgt_clk_path) - reportJson(end->targetClkPath(), "target_clock_path", 2, true, result); + reportJson(end->targetClkPath(), "target_clock_path", 2, true, result, end->checkArc(), true); if (end->checkRole(this)) { stringAppend(result, " \"data_arrival_time\": %.3e,\n", - delayAsFloat(end->dataArrivalTimeOffset(this))); + delayAsFloat(calculateSrcPathArrival(end))); const MultiCyclePath *mcp = end->multiCyclePath(); if (mcp) @@ -1139,9 +1140,9 @@ ReportPath::reportJson(const PathEnd *end, stringAppend(result, " \"crpr\": %.3e,\n", delayAsFloat(end->checkCrpr(this))); stringAppend(result, " \"margin\": %.3e,\n", - delayAsFloat(end->margin(this))); + delayAsFloat(calculateMargin(end))); stringAppend(result, " \"required_time\": %.3e,\n", - delayAsFloat(end->requiredTimeOffset(this))); + delayAsFloat(calculateRequired(end))); stringAppend(result, " \"slack\": %.3e\n", delayAsFloat(end->slack(this))); } @@ -1156,7 +1157,7 @@ ReportPath::reportJson(const Path *path) const { string result; result += "{\n"; - reportJson(path, "path", 0, false, result); + reportJson(path, "path", 0, false, result, nullptr, false); result += "}\n"; report_->reportLineString(result); } @@ -1166,10 +1167,12 @@ ReportPath::reportJson(const Path *path, const char *path_name, int indent, bool trailing_comma, - string &result) const + string &result, + const TimingArc *end_check_arc, + bool is_clk_path) const { PathExpanded expanded(path, this); - reportJson(expanded, path_name, indent, trailing_comma, result); + reportJson(expanded, path_name, indent, trailing_comma, result, end_check_arc, is_clk_path); } void @@ -1177,10 +1180,16 @@ ReportPath::reportJson(const PathExpanded &expanded, const char *path_name, int indent, bool trailing_comma, - string &result) const + string &result, + const TimingArc *end_check_arc, + bool is_clk_path) const { + auto instances_timing_arcs = extractInstancesTimingArcs(expanded, end_check_arc); + stringAppend(result, "%*s\"%s\": [\n", indent, "", path_name); - for (size_t i = 0; i < expanded.size(); i++) { + std::size_t path_last_index = expanded.size(); + float prev_time = 0.0f; + for (size_t i = 0; i < path_last_index; i++) { const Path *path = expanded.path(i); const Pin *pin = path->vertex(this)->pin(); const Net *net = network_->net(pin); @@ -1189,6 +1198,22 @@ ReportPath::reportJson(const PathExpanded &expanded, DcalcAnalysisPt *dcalc_ap = path->pathAnalysisPt(this)->dcalcAnalysisPt(); bool is_driver = network_->isDriver(pin); + if (instances_timing_arcs.find(inst) != instances_timing_arcs.end()) { + const char* from_instance_name = network_->pathName(inst); + reportTimingPathJson(from_instance_name, instances_timing_arcs.at(inst), indent, i == path_last_index - 1, result, is_clk_path, prev_time); + + // Paths only contain cell input/outpin pin, so we can just skip input pin + // and calculate prev time from the output pin + ++i; + if (i < path_last_index) { + path = expanded.path(i); + prev_time = path->arrival(); + } + continue; + } + + prev_time = path->arrival(); + stringAppend(result, "%*s {\n", indent, ""); if (inst) { @@ -1255,6 +1280,39 @@ ReportPath::reportJson(const PathExpanded &expanded, trailing_comma ? "," : ""); } +void ReportPath::reportTimingPathJson(const char* instance_name, const TimingArc* timing_arc, int indent, bool last_path, std::string &result, bool is_clk_path, float prev_time) const +{ + const auto& timing_paths = timing_arc->set()->timingPaths(); + const TimingPath* timing_path = nullptr; + if (is_clk_path) { + timing_path = &timing_paths.at(TimingPath::Names::DATA_REQUIRED.at(timing_arc->toEdge()->asRiseFall()->index())); + } + else { + timing_path = &timing_paths.at(TimingPath::ROLE_PATH_MAPPINGS.at(timing_arc->role()).at(timing_arc->toEdge()->asRiseFall()->index())); + } + + for (std::size_t index = 0; index < timing_path->vertices.size(); ++index) { + const auto& vertex = timing_path->vertices[index]; + std::string description = std::string{instance_name} + '/' + vertex.pin; + + stringAppend(result, "%*s {\n", indent, ""); + + if (!vertex.instance.empty()) { + stringAppend(result, "%*s \"instance\": \"%s\",\n", indent, "", vertex.instance.c_str()); + } + + if (!vertex.cell.empty()) { + stringAppend(result, "%*s \"cell\": \"%s\",\n", indent, "", vertex.cell.c_str()); + } + + stringAppend(result, "%*s \"pin\": \"%s\",\n", indent, "", description.c_str()); + stringAppend(result, "%*s \"net\": \"%s\",\n", indent, "", vertex.net.c_str()); + stringAppend(result, "%*s \"arrival\": %.3e,\n", indent, "", delayAsFloat(prev_time + vertex.arrival)); + stringAppend(result, "%*s \"slew\": %.3e\n", indent, "", delayAsFloat(vertex.slew)); + stringAppend(result, "%*s }%s\n", indent, "", (!last_path || index < timing_path->vertices.size() - 1) ? "," : ""); + } +} + //////////////////////////////////////////////////////////////// void @@ -1657,7 +1715,7 @@ ReportPath::reportSkewClkPath(const char *arrival_msg, insertion, latency); reportClkSrcLatency(insertion, clk_time, early_late); PathExpanded clk_expanded(clk_path, this); - reportPath2(clk_path, clk_expanded, false, 0.0); + reportPath2(clk_path, clk_expanded, false, 0.0, nullptr); } } else { @@ -2021,11 +2079,37 @@ ReportPath::reportSrcPathArrival(const PathEnd *end, { reportBlankLine(); reportSrcPath(end, expanded); - reportLine("data arrival time", end->dataArrivalTimeOffset(this), - end->pathEarlyLate(this)); + float data_arrival_time = calculateSrcPathArrival(end); + reportLine("data arrival time", data_arrival_time, end->pathEarlyLate(this)); reportBlankLine(); } +float ReportPath::calculateSrcPathArrival(const PathEnd *end) const +{ + float data_arrival_time = end->dataArrivalTimeOffset(this); + const TimingArc *end_check_arc = end->checkArc(); + if (!end_check_arc) { + return data_arrival_time; + } + + const auto& timing_paths = end_check_arc->set()->timingPaths(); + if (timing_paths.empty()) { + return data_arrival_time; + } + + if (end_check_arc->role() == TimingRole::setup()) { + const RiseFall* path_rf = end->path()->transition(this); + return data_arrival_time + timing_paths.at(TimingPath::Names::DATA_ARRIVAL.at(path_rf->index())).time; + } + + return data_arrival_time; +} + +float ReportPath::calculateMargin(const PathEnd *end) const +{ + return end->margin(this) - calculateRequired(end) + end->requiredTime(this); +} + void ReportPath::reportSrcPath(const PathEnd *end, const PathExpanded &expanded) const @@ -2036,7 +2120,7 @@ ReportPath::reportSrcPath(const PathEnd *end, Arrival src_clk_latency = end->sourceClkLatency(this); const Path *path = end->path(); reportSrcClkAndPath(path, expanded, src_clk_offset, src_clk_insertion, - src_clk_latency, end->isPathDelay()); + src_clk_latency, end->isPathDelay(), end->checkArc()); } void @@ -2045,7 +2129,8 @@ ReportPath::reportSrcClkAndPath(const Path *path, float time_offset, Arrival clk_insertion, Arrival clk_latency, - bool is_path_delay) const + bool is_path_delay, + const TimingArc *end_check_arc) const { const ClockEdge *clk_edge = path->clkEdge(this); const MinMax *min_max = path->minMax(this); @@ -2061,7 +2146,7 @@ ReportPath::reportSrcClkAndPath(const Path *path, clk_end_time, clk_end_time, early_late); reportLine(clkNetworkDelayIdealProp(false), 0.0, clk_end_time, early_late); } - reportPath1(path, expanded, false, time_offset); + reportPath1(path, expanded, false, time_offset, end_check_arc); } else { bool path_from_input = false; @@ -2116,21 +2201,21 @@ ReportPath::reportSrcClkAndPath(const Path *path, ClkInfo *clk_info = path->tag(search_)->clkInfo(); if (clk_info->isPropagated()) reportClkSrcLatency(clk_insertion, clk_time, early_late); - reportPath1(path, expanded, true, time_offset); + reportPath1(path, expanded, true, time_offset, end_check_arc); } else if (is_prop && reportClkPath() && !(path_from_input && !input_has_ref_path)) { reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, early_late); reportClkSrcLatency(clk_insertion, clk_time, early_late); - reportPath1(path, expanded, false, time_offset); + reportPath1(path, expanded, false, time_offset, end_check_arc); } else if (clk_used_as_data) { reportClkLine(clk, clk_name.c_str(), clk_end_rf, clk_time, early_late); if (delayGreater(clk_insertion, 0.0, this)) reportClkSrcLatency(clk_insertion, clk_time, early_late); if (reportClkPath()) - reportPath1(path, expanded, true, time_offset); + reportPath1(path, expanded, true, time_offset, end_check_arc); else { Arrival clk_arrival = clk_end_time; Arrival end_arrival = path->arrival() + time_offset; @@ -2154,12 +2239,12 @@ ReportPath::reportSrcClkAndPath(const Path *path, reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, clk_arrival, early_late); } - reportPath1(path, expanded, false, time_offset); + reportPath1(path, expanded, false, time_offset, end_check_arc); } } } else - reportPath1(path, expanded, false, time_offset); + reportPath1(path, expanded, false, time_offset, end_check_arc); } void @@ -2227,7 +2312,7 @@ ReportPath::reportTgtClk(const PathEnd *end, float insertion_offset = tgtClkInsertionOffet(clk_path, early_late, path_ap); reportPath5(clk_path, clk_expanded, 0, clk_expanded.size() - 1, is_prop, - reportClkPath(), delay_zero, time_offset + insertion_offset); + reportClkPath(), delay_zero, time_offset + insertion_offset, end->checkArc()); } else { // Output departure. @@ -2375,7 +2460,7 @@ ReportPath::reportGenClkSrcAndPath(const Path *path, if (path) { PathExpanded expanded(path, this); reportPath4(path, expanded, skip_first_path, false, clk_used_as_data, - path_time_offset); + path_time_offset, nullptr); } } @@ -2417,7 +2502,7 @@ ReportPath::reportGenClkSrcPath1(const Clock *clk, } PathExpanded src_expanded(src_path, this); reportPath4(src_path, src_expanded, skip_first_path, false, - clk_used_as_data, gclk_time); + clk_used_as_data, gclk_time, nullptr); if (!clk->isPropagated()) reportLine("clock network delay (ideal)", 0.0, src_path->arrival(), min_max); @@ -2473,10 +2558,11 @@ void ReportPath::reportRequired(const PathEnd *end, string margin_msg) const { - Required req_time = end->requiredTimeOffset(this); + Required req_time = calculateRequired(end); const EarlyLate *early_late = end->clkEarlyLate(this); float macro_clk_tree_delay = end->macroClkTreeDelay(this); - ArcDelay margin = end->margin(this); + float required_diff = req_time - end->requiredTime(this); + ArcDelay margin = end->margin(this) - required_diff; if (end->minMax(this) == MinMax::min()) { margin = -margin; macro_clk_tree_delay = -macro_clk_tree_delay; @@ -2489,13 +2575,35 @@ ReportPath::reportRequired(const PathEnd *end, reportDashLine(); } +Required ReportPath::calculateRequired(const PathEnd *end) const +{ + Required req_time = end->requiredTimeOffset(this); + + const TimingArc *end_check_arc = end->checkArc(); + if (!end_check_arc) { + return req_time; + } + + const auto& timing_paths = end_check_arc->set()->timingPaths(); + if (timing_paths.empty()) { + return req_time; + } + + if (end_check_arc->role() == TimingRole::setup()) { + const RiseFall* path_rf = end->path()->transition(this); + return timing_paths.at(TimingPath::Names::DATA_REQUIRED.at(path_rf->index())).time; + } + + return req_time; +} + void ReportPath::reportSlack(const PathEnd *end) const { const EarlyLate *early_late = end->pathEarlyLate(this); - reportLine("data required time", end->requiredTimeOffset(this), + reportLine("data required time", calculateRequired(end), early_late->opposite()); - reportLineNegative("data arrival time", end->dataArrivalTimeOffset(this), early_late); + reportLineNegative("data arrival time", calculateSrcPathArrival(end), early_late); reportDashLine(); reportSlack(end->slack(this)); } @@ -2566,7 +2674,7 @@ ReportPath::reportPath(const PathEnd *end, reportPathHeader(); // Source clk offset for path delays removes clock phase time. float src_clk_offset = end->sourceClkOffset(this); - reportPath1(end->path(), expanded, pathFromClkPin(expanded), src_clk_offset); + reportPath1(end->path(), expanded, pathFromClkPin(expanded), src_clk_offset, end->checkArc()); } void @@ -2595,7 +2703,7 @@ ReportPath::reportPathFull(const Path *path) const { reportPathHeader(); PathExpanded expanded(path, this); - reportSrcClkAndPath(path, expanded, 0.0, delay_zero, delay_zero, false); + reportSrcClkAndPath(path, expanded, 0.0, delay_zero, delay_zero, false, nullptr); } //////////////////////////////////////////////////////////////// @@ -2604,7 +2712,8 @@ void ReportPath::reportPath1(const Path *path, const PathExpanded &expanded, bool clk_used_as_data, - float time_offset) const + float time_offset, + const TimingArc *end_check_arc) const { const Path *d_path, *q_path; Edge *d_q_edge; @@ -2621,7 +2730,7 @@ ReportPath::reportPath1(const Path *path, PathExpanded enable_expanded(latch_enable_path, this); // Report the path to the latch enable. reportPath2(latch_enable_path, enable_expanded, false, - time_offset); + time_offset, end_check_arc); } Arrival time = latch_enable_time + latch_time_given; Arrival incr = latch_time_given; @@ -2635,18 +2744,19 @@ ReportPath::reportPath1(const Path *path, bool report_clk_path = path->isClock(search_) || reportClkPath(); reportPath5(path, expanded, 1, expanded.size() - 1, propagated_clk, report_clk_path, - latch_enable_time + latch_time_given, time_offset); + latch_enable_time + latch_time_given, time_offset, end_check_arc); } } else - reportPath2(path, expanded, clk_used_as_data, time_offset); + reportPath2(path, expanded, clk_used_as_data, time_offset, end_check_arc); } void ReportPath::reportPath2(const Path *path, const PathExpanded &expanded, bool clk_used_as_data, - float time_offset) const + float time_offset, + const TimingArc *end_check_arc) const { // Report the clock path if the end is a clock or we wouldn't have // anything to report. @@ -2654,7 +2764,7 @@ ReportPath::reportPath2(const Path *path, || (reportClkPath() && path->clkInfo(search_)->isPropagated()); reportPath3(path, expanded, clk_used_as_data, report_clk_path, - delay_zero, time_offset); + delay_zero, time_offset, end_check_arc); } void @@ -2663,13 +2773,14 @@ ReportPath::reportPath3(const Path *path, bool clk_used_as_data, bool report_clk_path, Arrival prev_time, - float time_offset) const + float time_offset, + const TimingArc *end_check_arc) const { bool propagated_clk = clk_used_as_data || path->clkInfo(search_)->isPropagated(); size_t path_last_index = expanded.size() - 1; reportPath5(path, expanded, 0, path_last_index, propagated_clk, - report_clk_path, prev_time, time_offset); + report_clk_path, prev_time, time_offset, end_check_arc); } void @@ -2678,7 +2789,8 @@ ReportPath::reportPath4(const Path *path, bool skip_first_path, bool skip_last_path, bool clk_used_as_data, - float time_offset) const + float time_offset, + const TimingArc *end_check_arc) const { size_t path_first_index = 0; Arrival prev_time(0.0); @@ -2698,7 +2810,7 @@ ReportPath::reportPath4(const Path *path, bool report_clk_path = path->isClock(search_) || (reportClkPath() && propagated_clk); reportPath5(path, expanded, path_first_index, path_last_index, - propagated_clk, report_clk_path, prev_time, time_offset); + propagated_clk, report_clk_path, prev_time, time_offset, end_check_arc); } void @@ -2709,24 +2821,44 @@ ReportPath::reportPath5(const Path *path, bool propagated_clk, bool report_clk_path, Arrival prev_time, - float time_offset) const + float time_offset, + const TimingArc *end_check_arc) const { const MinMax *min_max = path->minMax(this); DcalcAnalysisPt *dcalc_ap = path->pathAnalysisPt(this)->dcalcAnalysisPt(); DcalcAPIndex ap_index = dcalc_ap->index(); const Path *clk_path = expanded.clkPath(); Vertex *clk_start = clk_path ? clk_path->vertex(this) : nullptr; + + auto instances_timing_arcs = extractInstancesTimingArcs(expanded, end_check_arc); + for (size_t i = path_first_index; i <= path_last_index; i++) { const Path *path1 = expanded.path(i); - const TimingArc *prev_arc = path1->prevArc(this); Vertex *vertex = path1->vertex(this); Pin *pin = vertex->pin(); + Instance *inst = network_->instance(pin); Arrival time = path1->arrival() + time_offset; + + if (instances_timing_arcs.find(inst) != instances_timing_arcs.end()) { + const char* from_instance_name = network_->pathName(inst); + reportTimingPath(from_instance_name, instances_timing_arcs.at(inst), min_max, prev_time); + + // Paths only contain cell input/outpin pin, so we can just skip input pin + // and calculate prev time from the output pin + ++i; + if (i < path_last_index) { + path1 = expanded.path(i); + prev_time = path1->arrival() + time_offset; + } + continue; + } + + const TimingArc *prev_arc = path1->prevArc(this); Delay incr = 0.0; const char *line_case = nullptr; bool is_clk_start = path1->vertex(this) == clk_start; bool is_clk = path1->isClock(search_); - Instance *inst = network_->instance(pin); + string src_attr = ""; if (inst) src_attr = network_->getAttribute(inst, "src"); @@ -2739,7 +2871,7 @@ ReportPath::reportPath5(const Path *path, Slew slew = graph_->slew(vertex, rf, ap_index); if (prev_arc == nullptr) { // First path. - reportInputExternalDelay(path1, time_offset); + reportInputExternalDelay(path1, time_offset, end_check_arc); size_t next_index = i + 1; const Path *next_path = expanded.path(next_index); if (network_->isTopLevelPort(pin) @@ -2845,6 +2977,71 @@ ReportPath::reportPath5(const Path *path, } } +std::unordered_map ReportPath::extractInstancesTimingArcs(const PathExpanded &path_expanded, const TimingArc *end_check_arc) const +{ + std::unordered_map instances_timing_arcs{}; + + std::size_t starting_path_index = path_expanded.startIndex(); + std::size_t ending_path_index = path_expanded.size(); + Instance *inst = nullptr; + for (std::size_t index = starting_path_index; index < ending_path_index; ++index) { + const Path *path = path_expanded.path(index); + Vertex *vertex = path->vertex(this); + Pin *pin = vertex->pin(); + inst = network_->instance(pin); + const TimingArc *timing_arc = path->prevArc(this); + if (hasTimingPaths(timing_arc)) { + instances_timing_arcs[inst] = timing_arc; + } + } + + if (hasTimingPaths(end_check_arc)) { + instances_timing_arcs[inst] = end_check_arc; + } + + return instances_timing_arcs; +} + +bool ReportPath::hasTimingPaths(const TimingArc *timing_arc) const +{ + if (!timing_arc) { + return false; + } + + const TimingArcSet *timing_arc_set = timing_arc->set(); + return timing_arc_set && timing_arc_set->hasTimingPaths(); +} + +void ReportPath::reportTimingPath(const char *instance_name, const TimingArc *timing_arc, const MinMax *min_max, float base_arrival) const +{ + const auto &timing_paths = timing_arc->set()->timingPaths(); + const auto &timing_path = timing_paths.at(TimingPath::ROLE_PATH_MAPPINGS.at(timing_arc->role()).at(timing_arc->toEdge()->asRiseFall()->index())); + float previous_arrival = 0.0f; + for (std::size_t index = 0; index < timing_path.vertices.size(); ++index) { + const auto& vertex = timing_path.vertices[index]; + std::string description = stdstrPrint("%s/%s (%s)", instance_name, vertex.pin.c_str(), vertex.cell.c_str()); + + float increase = vertex.arrival - previous_arrival; + float time = base_arrival + vertex.arrival; + + const RiseFall *rise_fall = RiseFall::find(vertex.transition.c_str()); + + if (vertex.is_driver) { + reportLine(description.c_str(), vertex.capacitance, vertex.slew, field_blank_, increase, time, false, min_max, rise_fall, "", "normal"); + } + else { + reportLine(description.c_str(), field_blank_, vertex.slew, field_blank_, increase, time, false, min_max, rise_fall, "", "normal"); + } + + if (report_net_) { + const std::string net_description = stdstrPrint("%s (net)", vertex.net.c_str()); + reportLine(net_description.c_str(), field_blank_, field_blank_, field_blank_, field_blank_, field_blank_, false, min_max, nullptr, "", ""); + } + + previous_arrival = vertex.arrival; + } +} + void ReportPath::reportHierPinsThru(const Path *path) const { @@ -2971,7 +3168,8 @@ ReportPath::hasExtInputDriver(const Pin *pin, void ReportPath::reportInputExternalDelay(const Path *first_path, - float time_offset) const + float time_offset, + const TimingArc *end_check_arc) const { const Pin *first_pin = first_path->pin(graph_); if (!pathFromClkPin(first_path, first_pin)) { @@ -2987,7 +3185,7 @@ ReportPath::reportInputExternalDelay(const Path *first_path, if (!ref_path.isNull() && reportClkPath()) { PathExpanded ref_expanded(&ref_path, this); reportPath3(&ref_path, ref_expanded, false, true, - delay_zero, 0.0); + delay_zero, 0.0, end_check_arc); } } float input_arrival = diff --git a/search/ReportPath.hh b/search/ReportPath.hh index be229016..996c0e1b 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -100,12 +100,16 @@ public: const char *path_name, int indent, bool trailing_comma, - std::string &result) const; + std::string &result, + const TimingArc *end_check_arc, + bool is_clk_path) const; void reportJson(const PathExpanded &expanded, const char *path_name, int indent, bool trailing_comma, - std::string &result) const; + std::string &result, + const TimingArc *end_check_arc, + bool is_clk_path) const; void reportEndHeader() const; void reportEndLine(const PathEnd *end) const; @@ -238,7 +242,8 @@ protected: float time_offset, Arrival clk_insertion, Arrival clk_latency, - bool is_path_delay) const; + bool is_path_delay, + const TimingArc *end_check_arc) const; bool reportGenClkSrcPath(const Path *clk_path, const Clock *clk, const RiseFall *clk_rf, @@ -284,6 +289,7 @@ protected: const MinMax *min_max) const ; void reportRequired(const PathEnd *end, std::string margin_msg) const ; + Required calculateRequired(const PathEnd *end) const; void reportSlack(const PathEnd *end) const ; void reportSlack(Slack slack) const ; void reportSpaceSlack(const PathEnd *end, @@ -292,6 +298,8 @@ protected: std::string &line) const ; void reportSrcPathArrival(const PathEnd *end, const PathExpanded &expanded) const ; + float calculateSrcPathArrival(const PathEnd *end) const; + float calculateMargin(const PathEnd *end) const; void reportPath(const PathEnd *end, const PathExpanded &expanded) const; void reportPathFull(const Path *path) const; @@ -299,23 +307,27 @@ protected: void reportPath1(const Path *path, const PathExpanded &expanded, bool clk_used_as_data, - float time_offset) const; + float time_offset, + const TimingArc *end_check_arc) const; void reportPath2(const Path *path, const PathExpanded &expanded, bool clk_used_as_data, - float time_offset) const; + float time_offset, + const TimingArc *end_check_arc) const; void reportPath3(const Path *path, const PathExpanded &expanded, bool clk_used_as_data, bool report_clk_path, Arrival prev_time, - float time_offset) const; + float time_offset, + const TimingArc *end_check_arc) const; void reportPath4(const Path *path, const PathExpanded &expanded, bool clk_used_as_data, bool skip_first_path, bool skip_last_path, - float time_offset) const; + float time_offset, + const TimingArc *end_check_arc) const; void reportPath5(const Path *path, const PathExpanded &expanded, size_t path_first_index, @@ -323,10 +335,12 @@ protected: bool propagated_clk, bool report_clk_path, Arrival prev_time, - float time_offset) const; + float time_offset, + const TimingArc *end_check_arc) const; void reportHierPinsThru(const Path *path) const; void reportInputExternalDelay(const Path *path, - float time_offset) const; + float time_offset, + const TimingArc *end_check_arc) const; void reportLine(const char *what, Delay total, const EarlyLate *early_late) const; @@ -461,6 +475,10 @@ protected: Delay delayIncr(Delay time, Delay prev, const MinMax *min_max) const; + std::unordered_map extractInstancesTimingArcs(const PathExpanded &path_expanded, const TimingArc *end_check_arc) const; + bool hasTimingPaths(const TimingArc *timing_arc) const; + void reportTimingPath(const char *instance_name, const TimingArc *timing_arc, const MinMax *min_max, float prev_arrival) const; + void reportTimingPathJson(const char *instance_name, const TimingArc *timing_arc, int indent, bool last_path, std::string &result, bool is_clk_path, float prev_time) const; // Path options. ReportPathFormat format_; diff --git a/search/Search.i b/search/Search.i index 54fd88e1..52b7a346 100644 --- a/search/Search.i +++ b/search/Search.i @@ -819,9 +819,10 @@ write_timing_model_cmd(const char *lib_name, const char *cell_name, const char *filename, const Corner *corner, - const bool scalar) + const bool scalar, + const bool paths) { - Sta::sta()->writeTimingModel(lib_name, cell_name, filename, corner, scalar); + Sta::sta()->writeTimingModel(lib_name, cell_name, filename, corner, scalar, paths); } //////////////////////////////////////////////////////////////// diff --git a/search/Search.tcl b/search/Search.tcl index d77d29a1..e39106ed 100644 --- a/search/Search.tcl +++ b/search/Search.tcl @@ -1066,15 +1066,17 @@ define_cmd_args "write_timing_model" {[-scalar] \ [-corner corner] \ [-library_name lib_name]\ [-cell_name cell_name]\ + [-paths]\ filename} proc write_timing_model { args } { parse_key_args "write_timing_model" args \ - keys {-library_name -cell_name -corner} flags {-scalar} + keys {-library_name -cell_name -corner} flags {-scalar -paths} check_argc_eq1 "write_timing_model" $args set filename [file nativename [lindex $args 0]] set scalar [info exists flags(-scalar)] + set write_timing_paths [info exists flags(-paths)] if { [info exists keys(-cell_name)] } { set cell_name $keys(-cell_name) } else { @@ -1086,7 +1088,7 @@ proc write_timing_model { args } { set lib_name $cell_name } set corner [parse_corner keys] - write_timing_model_cmd $lib_name $cell_name $filename $corner $scalar + write_timing_model_cmd $lib_name $cell_name $filename $corner $scalar $write_timing_paths } ################################################################ diff --git a/search/Sta.cc b/search/Sta.cc index cfd8ea8d..8cea3c3d 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -5701,13 +5701,14 @@ Sta::writeTimingModel(const char *lib_name, const char *cell_name, const char *filename, const Corner *corner, - const bool scalar) + const bool scalar, + const bool write_timing_paths) { ensureLibLinked(); ensureGraph(); LibertyLibrary *library = makeTimingModel(lib_name, cell_name, filename, - corner, scalar, this); - writeLiberty(library, filename, this); + corner, scalar, write_timing_paths, this); + writeLiberty(library, filename, this, write_timing_paths); } //////////////////////////////////////////////////////////////// diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index afbaa559..50843491 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -163,6 +163,14 @@ record_sta_tests { suppress_msg verilog_attribute write_timing_model_scalar + timing_cell_dff + timing_cell_comb + timing_cell_complex + timing_cell_complex_json + timing_cell_cdc + timing_cell_complex_cdc + timing_paths_propagated_clock + timing_paths_non_propagated_clock } define_test_group fast [group_tests all] diff --git a/test/timing_cell_cdc.ok b/test/timing_cell_cdc.ok new file mode 100644 index 00000000..722e4d19 --- /dev/null +++ b/test/timing_cell_cdc.ok @@ -0,0 +1,344 @@ +library (timing_cell_cdc) { + comment : ""; + delay_model : table_lookup; + simulation : false; + capacitive_load_unit (1,fF); + leakage_power_unit : 1pW; + current_unit : "1mA"; + pulling_resistance_unit : "1kohm"; + time_unit : "1ps"; + 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 : 10; + slew_lower_threshold_pct_fall : 10; + slew_upper_threshold_pct_rise : 90; + slew_upper_threshold_pct_fall : 90; + slew_derate_from_library : 1.0; + + + nom_process : 1.0; + nom_temperature : 25.0; + nom_voltage : 0.70; + + lu_table_template(template_1) { + variable_1 : total_output_net_capacitance; + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + } + + cell ("timing_cell_cdc") { + area : 0.729 + is_macro_cell : true; + pin("in") { + direction : input; + capacitance : 0.5587; + timing() { + related_pin : "clk_a"; + timing_type : hold_rising; + rise_constraint(scalar) { + values("-3.16857"); + } + fall_constraint(scalar) { + values("9.34610"); + } + paths() { + slack : -9.34610; + fall_data_required() { + time: 9.34610; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk_a", "^", 0.00000, 0.00000, 0.47543, 0); + } + fall_data_arrival() { + time: 0.00000; + vertex("", "timing_cell_cdc", "in", "", "v", 0.00000, 0.00000, 0.48550, 1); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/D", "in", "v", 0.00000, 0.00000, 0.48550, 0); + } + rise_data_required() { + time: -3.16857; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk_a", "^", 0.00000, 0.00000, 0.47554, 0); + } + rise_data_arrival() { + time: 0.00000; + vertex("", "timing_cell_cdc", "in", "", "^", 0.00000, 0.00000, 0.49848, 1); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/D", "in", "^", 0.00000, 0.00000, 0.49848, 0); + } + } + } + timing() { + related_pin : "clk_b"; + timing_type : hold_rising; + rise_constraint(scalar) { + values("-3.16857"); + } + fall_constraint(scalar) { + values("9.34610"); + } + paths() { + slack : -9.34610; + fall_data_required() { + time: 9.34610; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk_a", "^", 0.00000, 0.00000, 0.47543, 0); + } + fall_data_arrival() { + time: 0.00000; + vertex("", "timing_cell_cdc", "in", "", "v", 0.00000, 0.00000, 0.48550, 1); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/D", "in", "v", 0.00000, 0.00000, 0.48550, 0); + } + rise_data_required() { + time: -3.16857; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk_a", "^", 0.00000, 0.00000, 0.47554, 0); + } + rise_data_arrival() { + time: 0.00000; + vertex("", "timing_cell_cdc", "in", "", "^", 0.00000, 0.00000, 0.49848, 1); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/D", "in", "^", 0.00000, 0.00000, 0.49848, 0); + } + } + } + timing() { + related_pin : "clk_a"; + timing_type : setup_rising; + rise_constraint(scalar) { + values("11.41230"); + } + fall_constraint(scalar) { + values("3.06735"); + } + paths() { + slack : 488.58768; + fall_data_required() { + time: 496.93268; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk_a", "^", 0.00000, 0.00000, 0.35251, 0); + } + fall_data_arrival() { + time: 0.00000; + vertex("", "timing_cell_cdc", "in", "", "v", 0.00000, 0.00000, 0.55560, 1); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/D", "in", "v", 0.00000, 0.00000, 0.55560, 0); + } + rise_data_required() { + time: 488.58768; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk_a", "^", 0.00000, 0.00000, 0.35582, 0); + } + rise_data_arrival() { + time: 0.00000; + vertex("", "timing_cell_cdc", "in", "", "^", 0.00000, 0.00000, 0.55869, 1); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/D", "in", "^", 0.00000, 0.00000, 0.55869, 0); + } + } + } + timing() { + related_pin : "clk_b"; + timing_type : setup_rising; + rise_constraint(scalar) { + values("11.41230"); + } + fall_constraint(scalar) { + values("3.06735"); + } + paths() { + slack : 488.58768; + fall_data_required() { + time: 496.93268; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk_a", "^", 0.00000, 0.00000, 0.35251, 0); + } + fall_data_arrival() { + time: 0.00000; + vertex("", "timing_cell_cdc", "in", "", "v", 0.00000, 0.00000, 0.55560, 1); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/D", "in", "v", 0.00000, 0.00000, 0.55560, 0); + } + rise_data_required() { + time: 488.58768; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk_a", "^", 0.00000, 0.00000, 0.35582, 0); + } + rise_data_arrival() { + time: 0.00000; + vertex("", "timing_cell_cdc", "in", "", "^", 0.00000, 0.00000, 0.55869, 1); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/D", "in", "^", 0.00000, 0.00000, 0.55869, 0); + } + } + } + } + pin("clk_a") { + direction : input; + clock : true; + capacitance : 0.4755; + timing() { + timing_sense : positive_unate; + timing_type : min_clock_tree_path; + cell_rise(scalar) { + values("0.00000"); + } + cell_fall(scalar) { + values("0.00000"); + } + } + timing() { + timing_sense : positive_unate; + timing_type : max_clock_tree_path; + cell_rise(scalar) { + values("0.00000"); + } + cell_fall(scalar) { + values("0.00000"); + } + } + } + pin("clk_b") { + direction : input; + clock : true; + capacitance : 0.4755; + timing() { + timing_sense : positive_unate; + timing_type : min_clock_tree_path; + cell_rise(scalar) { + values("0.00000"); + } + cell_fall(scalar) { + values("0.00000"); + } + } + timing() { + timing_sense : positive_unate; + timing_type : max_clock_tree_path; + cell_rise(scalar) { + values("0.00000"); + } + cell_fall(scalar) { + values("0.00000"); + } + } + } + pin("out") { + direction : output; + capacitance : 0.0000; + timing() { + related_pin : "clk_a"; + timing_type : rising_edge; + cell_rise(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("66.16869,68.40050,71.97030,78.11000,89.73939,112.75500,158.58101"); + } + rise_transition(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("10.66370,13.37680,18.80680,30.41660,54.95930,105.42699,207.52800"); + } + cell_fall(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("61.68679,63.66789,66.83360,72.15240,81.86710,100.74999,138.30800"); + } + fall_transition(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("8.76410,11.02540,15.42310,24.41700,43.13271,81.68120,159.89299"); + } + paths() { + slack : 59.70571; + fall_clocked_output() { + time: 59.70571; + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/CLK", "clk_b", "^", 0.00000, 0.00000, 0.35251, 0); + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/Q", "out", "v", 59.70571, 6.50280, 0.00000, 1); + vertex("", "timing_cell_cdc", "out", "", "v", 59.70571, 6.50280, 0.00000, 0); + } + rise_clocked_output() { + time: 63.93688; + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/CLK", "clk_b", "^", 0.00000, 0.00000, 0.35582, 0); + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/Q", "out", "^", 63.93688, 7.95060, 0.00000, 1); + vertex("", "timing_cell_cdc", "out", "", "^", 63.93688, 7.95060, 0.00000, 0); + } + } + } + timing() { + related_pin : "clk_b"; + timing_type : rising_edge; + cell_rise(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("66.16869,68.40050,71.97030,78.11000,89.73939,112.75500,158.58101"); + } + rise_transition(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("10.66370,13.37680,18.80680,30.41660,54.95930,105.42699,207.52800"); + } + cell_fall(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("61.68679,63.66789,66.83360,72.15240,81.86710,100.74999,138.30800"); + } + fall_transition(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("8.76410,11.02540,15.42310,24.41700,43.13271,81.68120,159.89299"); + } + paths() { + slack : 59.70571; + fall_clocked_output() { + time: 59.70571; + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/CLK", "clk_b", "^", 0.00000, 0.00000, 0.35251, 0); + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/Q", "out", "v", 59.70571, 6.50280, 0.00000, 1); + vertex("", "timing_cell_cdc", "out", "", "v", 59.70571, 6.50280, 0.00000, 0); + } + rise_clocked_output() { + time: 63.93688; + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/CLK", "clk_b", "^", 0.00000, 0.00000, 0.35582, 0); + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/Q", "out", "^", 63.93688, 7.95060, 0.00000, 1); + vertex("", "timing_cell_cdc", "out", "", "^", 63.93688, 7.95060, 0.00000, 0); + } + } + } + } + worst_slack_paths() { + min_rise() { + slack : 69.42394; + rise_data_arrival() { + time: 64.70945; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk_a", "^", 0.00000, 0.00000, 0.35582, 0); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/Q", "w1", "^", 64.70945, 8.88979, 0.49848, 1); + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/D", "w1", "^", 64.70945, 8.88979, 0.49848, 0); + } + rise_data_required() { + time: -4.71449; + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/CLK", "clk_b", "^", 0.00000, 0.00000, 0.47554, 0); + } + } + min_fall() { + slack : 51.65410; + fall_data_arrival() { + time: 60.37365; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk_a", "^", 0.00000, 0.00000, 0.35251, 0); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/Q", "w1", "v", 60.37365, 7.26521, 0.48550, 1); + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/D", "w1", "v", 60.37365, 7.26521, 0.48550, 0); + } + fall_data_required() { + time: 8.71955; + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/CLK", "clk_b", "^", 0.00000, 0.00000, 0.47543, 0); + } + } + max_rise() { + slack : 422.26083; + rise_data_arrival() { + time: 64.80278; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk_a", "^", 0.00000, 0.00000, 0.47554, 0); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/Q", "w1", "^", 64.80278, 9.00323, 0.55869, 1); + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/D", "w1", "^", 64.80278, 9.00323, 0.55869, 0); + } + rise_data_required() { + time: 487.06360; + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/CLK", "clk_b", "^", 0.00000, 0.00000, 0.35582, 0); + } + } + max_fall() { + slack : 435.13882; + fall_data_arrival() { + time: 60.47008; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk_a", "^", 0.00000, 0.00000, 0.47543, 0); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/Q", "w1", "v", 60.47008, 7.37528, 0.55560, 1); + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/D", "w1", "v", 60.47008, 7.37528, 0.55560, 0); + } + fall_data_required() { + time: 495.60889; + vertex("r2", "DFFHQx4_ASAP7_75t_R", "r2/CLK", "clk_b", "^", 0.00000, 0.00000, 0.35251, 0); + } + } + } + } + +} diff --git a/test/timing_cell_cdc.tcl b/test/timing_cell_cdc.tcl new file mode 100644 index 00000000..9cddb8ee --- /dev/null +++ b/test/timing_cell_cdc.tcl @@ -0,0 +1,7 @@ +# timing cell dff example +read_liberty asap7_small.lib.gz +read_verilog timing_cell_cdc.v +link_design timing_cell_cdc +create_clock -name clk -period 500 {clk_a clk_b} +set_input_delay -clock clk 0 {in} +write_timing_model -paths results/timing_cell_cdc.log diff --git a/test/timing_cell_cdc.v b/test/timing_cell_cdc.v new file mode 100644 index 00000000..c338d0b8 --- /dev/null +++ b/test/timing_cell_cdc.v @@ -0,0 +1,8 @@ +module timing_cell_cdc (input in, input clk_a, input clk_b, output out); + + wire w1; + + DFFHQx4_ASAP7_75t_R r1 (.D(in), .CLK(clk_a), .Q(w1)); + DFFHQx4_ASAP7_75t_R r2 (.D(w1), .CLK(clk_b), .Q(out)); + +endmodule diff --git a/test/timing_cell_comb.ok b/test/timing_cell_comb.ok new file mode 100644 index 00000000..2b70e106 --- /dev/null +++ b/test/timing_cell_comb.ok @@ -0,0 +1,89 @@ +library (timing_cell_comb) { + comment : ""; + delay_model : table_lookup; + simulation : false; + capacitive_load_unit (1,fF); + leakage_power_unit : 1pW; + current_unit : "1mA"; + pulling_resistance_unit : "1kohm"; + time_unit : "1ps"; + 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 : 10; + slew_lower_threshold_pct_fall : 10; + slew_upper_threshold_pct_rise : 90; + slew_upper_threshold_pct_fall : 90; + slew_derate_from_library : 1.0; + + + nom_process : 1.0; + nom_temperature : 25.0; + nom_voltage : 0.70; + + lu_table_template(template_1) { + variable_1 : total_output_net_capacitance; + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + } + + cell ("timing_cell_comb") { + area : 0.073 + is_macro_cell : true; + pin("in") { + direction : input; + capacitance : 0.5343; + } + pin("clk") { + direction : input; + clock : true; + capacitance : 0.0000; + } + pin("out") { + direction : output; + capacitance : 0.0000; + timing() { + related_pin : "in"; + timing_sense : positive_unate; + timing_type : combinational; + cell_rise(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("15.34000,18.76610,24.70210,36.34740,59.22760,104.82500,195.93398"); + } + rise_transition(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("10.71910,16.68890,28.89130,53.74130,103.98500,204.87100,406.85599"); + } + cell_fall(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("16.78370,19.81070,25.07290,34.75059,53.55110,91.08410,165.90601"); + } + fall_transition(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("9.67198,14.34710,23.62830,42.45401,80.76170,158.09500,313.27496"); + } + paths() { + slack : 486.24326; + fall_combinational() { + time: 13.75670; + vertex("", "timing_cell_comb", "in", "", "v", 0.00000, 0.00000, 0.39404, 1); + vertex("u2", "BUFx2_ASAP7_75t_R", "u2/A", "in", "v", 0.00000, 0.00000, 0.39404, 0); + vertex("u2", "BUFx2_ASAP7_75t_R", "u2/Y", "out", "v", 13.75670, 4.99686, 0.00000, 1); + vertex("", "timing_cell_comb", "out", "", "v", 13.75670, 4.99686, 0.00000, 0); + } + rise_combinational() { + time: 11.91390; + vertex("", "timing_cell_comb", "in", "", "^", 0.00000, 0.00000, 0.39934, 1); + vertex("u2", "BUFx2_ASAP7_75t_R", "u2/A", "in", "^", 0.00000, 0.00000, 0.39934, 0); + vertex("u2", "BUFx2_ASAP7_75t_R", "u2/Y", "out", "^", 11.91390, 4.74930, 0.00000, 1); + vertex("", "timing_cell_comb", "out", "", "^", 11.91390, 4.74930, 0.00000, 0); + } + } + } + } + } + +} diff --git a/test/timing_cell_comb.tcl b/test/timing_cell_comb.tcl new file mode 100644 index 00000000..6f398a37 --- /dev/null +++ b/test/timing_cell_comb.tcl @@ -0,0 +1,7 @@ +# timing cell comb example +read_liberty asap7_small.lib.gz +read_verilog timing_cell_comb.v +link_design timing_cell_comb +create_clock -name clk -period 500 {clk} +set_input_delay -clock clk 0 {in} +write_timing_model -paths results/timing_cell_comb.log diff --git a/test/timing_cell_comb.v b/test/timing_cell_comb.v new file mode 100644 index 00000000..f91b9c65 --- /dev/null +++ b/test/timing_cell_comb.v @@ -0,0 +1,7 @@ +module timing_cell_comb (in, clk, out); + input in, clk; + output out; + + BUFx2_ASAP7_75t_R u2 (.A(in), .Y(out)); + +endmodule diff --git a/test/timing_cell_complex.ok b/test/timing_cell_complex.ok new file mode 100644 index 00000000..8828b987 --- /dev/null +++ b/test/timing_cell_complex.ok @@ -0,0 +1,39 @@ +Startpoint: tt1 (rising edge-triggered flip-flop clocked by clk) +Endpoint: tt3 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path Type: max + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ tt1/r1/CLK (DFFHQx4_ASAP7_75t_R) + 64.56 64.56 ^ tt1/r1/Q (DFFHQx4_ASAP7_75t_R) + 0.00 64.56 ^ tt1/u2/A (BUFx2_ASAP7_75t_R) + 15.17 79.72 ^ tt1/u2/Y (BUFx2_ASAP7_75t_R) + 0.00 79.72 ^ tt1/out (timing_cell_dff) + 0.00 81.25 ^ tt2/in (timing_cell_comb) + 0.00 81.25 ^ tt2/u2/A (BUFx2_ASAP7_75t_R) + 11.91 93.17 ^ tt2/u2/Y (BUFx2_ASAP7_75t_R) + 0.00 93.17 ^ tt2/out (timing_cell_comb) + 0.00 94.44 ^ u1/A (BUFx2_ASAP7_75t_R) + 15.75 110.19 ^ u1/Y (BUFx2_ASAP7_75t_R) + 0.00 110.19 ^ tt3/in (timing_cell_dff) + 0.00 110.19 ^ tt3/u1/A (BUFx2_ASAP7_75t_R) + 13.24 123.43 ^ tt3/u1/Y (BUFx2_ASAP7_75t_R) + 0.00 123.43 ^ tt3/r1/D (DFFHQx4_ASAP7_75t_R) + 123.43 data arrival time + + 500.00 500.00 clock clk (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 500.00 ^ tt3/clk (timing_cell_dff) + -12.61 487.39 library setup time + 487.39 data required time +--------------------------------------------------------- + 487.39 data required time + -123.43 data arrival time +--------------------------------------------------------- + 363.96 slack (MET) + + diff --git a/test/timing_cell_complex.tcl b/test/timing_cell_complex.tcl new file mode 100644 index 00000000..65e2e939 --- /dev/null +++ b/test/timing_cell_complex.tcl @@ -0,0 +1,9 @@ +# timing cell complex example +read_liberty asap7_small.lib.gz +read_liberty timing_cell_dff.ok +read_liberty timing_cell_comb.ok +read_verilog timing_cell_complex.v +link_design timing_cell_complex +create_clock -name clk -period 500 {clk} +set_input_delay -clock clk 0 {in} +report_checks -fields input diff --git a/test/timing_cell_complex.v b/test/timing_cell_complex.v new file mode 100644 index 00000000..9f0572d8 --- /dev/null +++ b/test/timing_cell_complex.v @@ -0,0 +1,15 @@ +module timing_cell_complex (in, clk, out); + + input in, clk; + output out; + wire w1, w2, w3; + + timing_cell_dff tt1(.in(in), .clk(clk), .out(w1)); + + timing_cell_comb tt2(.in(w1), .out(w2)); + + BUFx2_ASAP7_75t_R u1 (.A(w2), .Y(w3)); + + timing_cell_dff tt3(.in(w3), .clk(clk), .out(out)); + +endmodule diff --git a/test/timing_cell_complex_cdc.ok b/test/timing_cell_complex_cdc.ok new file mode 100644 index 00000000..f13d197b --- /dev/null +++ b/test/timing_cell_complex_cdc.ok @@ -0,0 +1,27 @@ +Startpoint: r1 (rising edge-triggered flip-flop clocked by clk) +Endpoint: r2 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path Type: max + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r1/CLK (DFFHQx4_ASAP7_75t_R) + 64.80 64.80 ^ r1/Q (DFFHQx4_ASAP7_75t_R) + 0.00 64.80 ^ r2/D (DFFHQx4_ASAP7_75t_R) + 64.80 data arrival time + + 500.00 500.00 clock clk (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 500.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R) + -12.94 487.06 library setup time + 487.06 data required time +--------------------------------------------------------- + 487.06 data required time + -64.80 data arrival time +--------------------------------------------------------- + 422.26 slack (MET) + + diff --git a/test/timing_cell_complex_cdc.tcl b/test/timing_cell_complex_cdc.tcl new file mode 100644 index 00000000..dc2eaf4b --- /dev/null +++ b/test/timing_cell_complex_cdc.tcl @@ -0,0 +1,8 @@ +# timing cell complex example +read_liberty asap7_small.lib.gz +read_liberty timing_cell_cdc.ok +read_verilog timing_cell_cdc.v +link_design timing_cell_cdc +create_clock -name clk -period 500 {clk_a clk_b} +set_input_delay -clock clk 0 {in} +report_checks diff --git a/test/timing_cell_complex_cdc.v b/test/timing_cell_complex_cdc.v new file mode 100644 index 00000000..efe1052e --- /dev/null +++ b/test/timing_cell_complex_cdc.v @@ -0,0 +1,5 @@ +module timing_cell_complex (input in, input clk_a, input clk_b, output out); + + timing_cell_cdc tt1(.in(in), .clk_a(clk_a), .clk_b(clk_b), .out(out)); + +endmodule diff --git a/test/timing_cell_complex_json.ok b/test/timing_cell_complex_json.ok new file mode 100644 index 00000000..d6b2b7b9 --- /dev/null +++ b/test/timing_cell_complex_json.ok @@ -0,0 +1,168 @@ +{"checks": [ +{ + "type": "check", + "path_group": "clk", + "path_type": "max", + "startpoint": "tt1/out", + "endpoint": "tt3/in", + "source_clock": "clk", + "source_clock_edge": "rise", + "source_path": [ + { + "instance": "", + "cell": "timing_cell_complex", + "verilog_src": "", + "pin": "clk", + "arrival": 0.000e+00, + "capacitance": 9.510e-16, + "slew": 0.000e+00 + }, + { + "instance": "r1", + "cell": "DFFHQx4_ASAP7_75t_R", + "pin": "tt1/r1/CLK", + "net": "clk", + "arrival": 0.000e+00, + "slew": 0.000e+00 + }, + { + "instance": "r1", + "cell": "DFFHQx4_ASAP7_75t_R", + "pin": "tt1/r1/Q", + "net": "w2", + "arrival": 6.456e-11, + "slew": 8.703e-12 + }, + { + "instance": "u2", + "cell": "BUFx2_ASAP7_75t_R", + "pin": "tt1/u2/A", + "net": "w2", + "arrival": 6.456e-11, + "slew": 8.703e-12 + }, + { + "instance": "u2", + "cell": "BUFx2_ASAP7_75t_R", + "pin": "tt1/u2/Y", + "net": "out", + "arrival": 7.972e-11, + "slew": 4.737e-12 + }, + { + "cell": "timing_cell_dff", + "pin": "tt1/out", + "net": "", + "arrival": 7.972e-11, + "slew": 4.737e-12 + }, + { + "cell": "timing_cell_comb", + "pin": "tt2/in", + "net": "", + "arrival": 8.125e-11, + "slew": 0.000e+00 + }, + { + "instance": "u2", + "cell": "BUFx2_ASAP7_75t_R", + "pin": "tt2/u2/A", + "net": "in", + "arrival": 8.125e-11, + "slew": 0.000e+00 + }, + { + "instance": "u2", + "cell": "BUFx2_ASAP7_75t_R", + "pin": "tt2/u2/Y", + "net": "out", + "arrival": 9.317e-11, + "slew": 4.749e-12 + }, + { + "cell": "timing_cell_comb", + "pin": "tt2/out", + "net": "", + "arrival": 9.317e-11, + "slew": 4.749e-12 + }, + { + "instance": "u1", + "cell": "BUFx2_ASAP7_75t_R", + "verilog_src": "", + "pin": "u1/A", + "net": "w2", + "arrival": 9.444e-11, + "slew": 6.964e-12 + }, + { + "instance": "u1", + "cell": "BUFx2_ASAP7_75t_R", + "verilog_src": "", + "pin": "u1/Y", + "net": "w3", + "arrival": 1.102e-10, + "capacitance": 5.343e-16, + "slew": 6.958e-12 + }, + { + "cell": "timing_cell_dff", + "pin": "tt3/in", + "net": "", + "arrival": 1.102e-10, + "slew": 0.000e+00 + }, + { + "instance": "u1", + "cell": "BUFx2_ASAP7_75t_R", + "pin": "tt3/u1/A", + "net": "in", + "arrival": 1.102e-10, + "slew": 0.000e+00 + }, + { + "instance": "u1", + "cell": "BUFx2_ASAP7_75t_R", + "pin": "tt3/u1/Y", + "net": "w1", + "arrival": 1.234e-10, + "slew": 7.065e-12 + }, + { + "instance": "r1", + "cell": "DFFHQx4_ASAP7_75t_R", + "pin": "tt3/r1/D", + "net": "w1", + "arrival": 1.234e-10, + "slew": 7.065e-12 + } + ], + "target_clock": "clk", + "target_clock_edge": "rise", + "target_clock_path": [ + { + "instance": "", + "cell": "timing_cell_complex", + "verilog_src": "", + "pin": "clk", + "arrival": 0.000e+00, + "capacitance": 9.510e-16, + "slew": 0.000e+00 + }, + { + "instance": "r1", + "cell": "DFFHQx4_ASAP7_75t_R", + "pin": "tt3/r1/CLK", + "net": "clk", + "arrival": 0.000e+00, + "slew": 0.000e+00 + } + ], + "data_arrival_time": 1.234e-10, + "crpr": 0.000e+00, + "margin": 1.261e-11, + "required_time": 4.874e-10, + "slack": 3.640e-10 +} +] +} diff --git a/test/timing_cell_complex_json.tcl b/test/timing_cell_complex_json.tcl new file mode 100644 index 00000000..8fd6b079 --- /dev/null +++ b/test/timing_cell_complex_json.tcl @@ -0,0 +1,9 @@ +# timing cell complex example +read_liberty asap7_small.lib.gz +read_liberty timing_cell_dff.ok +read_liberty timing_cell_comb.ok +read_verilog timing_cell_complex.v +link_design timing_cell_complex +create_clock -name clk -period 500 {clk} +set_input_delay -clock clk 0 {in} +report_checks -format json diff --git a/test/timing_cell_dff.ok b/test/timing_cell_dff.ok new file mode 100644 index 00000000..06ed1329 --- /dev/null +++ b/test/timing_cell_dff.ok @@ -0,0 +1,180 @@ +library (timing_cell_dff) { + comment : ""; + delay_model : table_lookup; + simulation : false; + capacitive_load_unit (1,fF); + leakage_power_unit : 1pW; + current_unit : "1mA"; + pulling_resistance_unit : "1kohm"; + time_unit : "1ps"; + 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 : 10; + slew_lower_threshold_pct_fall : 10; + slew_upper_threshold_pct_rise : 90; + slew_upper_threshold_pct_fall : 90; + slew_derate_from_library : 1.0; + + + nom_process : 1.0; + nom_temperature : 25.0; + nom_voltage : 0.70; + + lu_table_template(template_1) { + variable_1 : total_output_net_capacitance; + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + } + + cell ("timing_cell_dff") { + area : 0.510 + is_macro_cell : true; + pin("in") { + direction : input; + capacitance : 0.5343; + timing() { + related_pin : "clk"; + timing_type : hold_rising; + rise_constraint(scalar) { + values("-17.45373"); + } + fall_constraint(scalar) { + values("-5.99804"); + } + paths() { + slack : 5.99804; + fall_data_required() { + time: 8.77923; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk", "^", 0.00000, 0.00000, 0.47543, 0); + } + fall_data_arrival() { + time: 14.77727; + vertex("", "timing_cell_dff", "in", "", "v", 0.00000, 0.00000, 0.39404, 1); + vertex("u1", "BUFx2_ASAP7_75t_R", "u1/A", "in", "v", 0.00000, 0.00000, 0.39404, 0); + vertex("u1", "BUFx2_ASAP7_75t_R", "u1/Y", "w1", "v", 14.77727, 6.57311, 0.48550, 1); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/D", "w1", "v", 14.77727, 6.57311, 0.48550, 0); + } + rise_data_required() { + time: -4.35383; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk", "^", 0.00000, 0.00000, 0.47554, 0); + } + rise_data_arrival() { + time: 13.09990; + vertex("", "timing_cell_dff", "in", "", "^", 0.00000, 0.00000, 0.39934, 1); + vertex("u1", "BUFx2_ASAP7_75t_R", "u1/A", "in", "^", 0.00000, 0.00000, 0.39934, 0); + vertex("u1", "BUFx2_ASAP7_75t_R", "u1/Y", "w1", "^", 13.09990, 6.81584, 0.49848, 1); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/D", "w1", "^", 13.09990, 6.81584, 0.49848, 0); + } + } + } + timing() { + related_pin : "clk"; + timing_type : setup_rising; + rise_constraint(scalar) { + values("25.85150"); + } + fall_constraint(scalar) { + values("19.21259"); + } + paths() { + slack : 474.14844; + fall_data_required() { + time: 495.71204; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk", "^", 0.00000, 0.00000, 0.35251, 0); + } + fall_data_arrival() { + time: 14.92461; + vertex("", "timing_cell_dff", "in", "", "v", 0.00000, 0.00000, 0.53423, 1); + vertex("u1", "BUFx2_ASAP7_75t_R", "u1/A", "in", "v", 0.00000, 0.00000, 0.53423, 0); + vertex("u1", "BUFx2_ASAP7_75t_R", "u1/Y", "w1", "v", 14.92461, 6.80067, 0.55560, 1); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/D", "w1", "v", 14.92461, 6.80067, 0.55560, 0); + } + rise_data_required() { + time: 487.39163; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk", "^", 0.00000, 0.00000, 0.35582, 0); + } + rise_data_arrival() { + time: 13.24316; + vertex("", "timing_cell_dff", "in", "", "^", 0.00000, 0.00000, 0.53428, 1); + vertex("u1", "BUFx2_ASAP7_75t_R", "u1/A", "in", "^", 0.00000, 0.00000, 0.53428, 0); + vertex("u1", "BUFx2_ASAP7_75t_R", "u1/Y", "w1", "^", 13.24316, 7.06547, 0.55869, 1); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/D", "w1", "^", 13.24316, 7.06547, 0.55869, 0); + } + } + } + } + pin("clk") { + direction : input; + clock : true; + capacitance : 0.4755; + timing() { + timing_sense : positive_unate; + timing_type : min_clock_tree_path; + cell_rise(scalar) { + values("0.00000"); + } + cell_fall(scalar) { + values("0.00000"); + } + } + timing() { + timing_sense : positive_unate; + timing_type : max_clock_tree_path; + cell_rise(scalar) { + values("0.00000"); + } + cell_fall(scalar) { + values("0.00000"); + } + } + } + pin("out") { + direction : output; + capacitance : 0.0000; + timing() { + related_pin : "clk"; + timing_type : rising_edge; + cell_rise(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("83.32793,86.62773,92.76222,104.28410,127.11913,172.72675,263.83578"); + } + rise_transition(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("10.71605,16.69499,28.89470,53.73843,103.98858,204.87100,406.84885"); + } + cell_fall(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("79.69898,82.75785,88.02093,97.69936,116.55051,154.05209,228.90894"); + } + fall_transition(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("9.67816,14.33623,23.61905,42.44593,80.75039,158.09352,313.27646"); + } + paths() { + slack : 76.37401; + fall_clocked_output() { + time: 76.37401; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk", "^", 0.00000, 0.00000, 0.35251, 0); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/Q", "w2", "v", 60.24781, 7.12158, 0.39404, 1); + vertex("u2", "BUFx2_ASAP7_75t_R", "u2/A", "w2", "v", 60.24781, 7.12158, 0.39404, 0); + vertex("u2", "BUFx2_ASAP7_75t_R", "u2/Y", "out", "v", 76.37401, 5.01939, 0.00000, 1); + vertex("", "timing_cell_dff", "out", "", "v", 76.37401, 5.01939, 0.00000, 0); + } + rise_clocked_output() { + time: 79.72392; + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/CLK", "clk", "^", 0.00000, 0.00000, 0.35582, 0); + vertex("r1", "DFFHQx4_ASAP7_75t_R", "r1/Q", "w2", "^", 64.55580, 8.70299, 0.39934, 1); + vertex("u2", "BUFx2_ASAP7_75t_R", "u2/A", "w2", "^", 64.55580, 8.70299, 0.39934, 0); + vertex("u2", "BUFx2_ASAP7_75t_R", "u2/Y", "out", "^", 79.72392, 4.73746, 0.00000, 1); + vertex("", "timing_cell_dff", "out", "", "^", 79.72392, 4.73746, 0.00000, 0); + } + } + } + } + } + +} diff --git a/test/timing_cell_dff.tcl b/test/timing_cell_dff.tcl new file mode 100644 index 00000000..ebf23239 --- /dev/null +++ b/test/timing_cell_dff.tcl @@ -0,0 +1,7 @@ +# timing cell dff example +read_liberty asap7_small.lib.gz +read_verilog timing_cell_dff.v +link_design timing_cell_dff +create_clock -name clk -period 500 {clk} +set_input_delay -clock clk 0 {in} +write_timing_model -paths results/timing_cell_dff.log diff --git a/test/timing_cell_dff.v b/test/timing_cell_dff.v new file mode 100644 index 00000000..8fcdea20 --- /dev/null +++ b/test/timing_cell_dff.v @@ -0,0 +1,9 @@ +module timing_cell_dff (in, clk, out); + input in, clk; + output out; + wire w1, w2; + + BUFx2_ASAP7_75t_R u1 (.A(in), .Y(w1)); + DFFHQx4_ASAP7_75t_R r1 (.D(w1), .CLK(clk), .Q(w2)); + BUFx2_ASAP7_75t_R u2 (.A(w2), .Y(out)); +endmodule diff --git a/test/timing_paths_non_propagated_clock.ok b/test/timing_paths_non_propagated_clock.ok new file mode 100644 index 00000000..d5aefe96 --- /dev/null +++ b/test/timing_paths_non_propagated_clock.ok @@ -0,0 +1,238 @@ +library (timing_cell) { + comment : ""; + delay_model : table_lookup; + simulation : false; + capacitive_load_unit (1,fF); + leakage_power_unit : 1pW; + current_unit : "1mA"; + pulling_resistance_unit : "1kohm"; + time_unit : "1ps"; + 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 : 10; + slew_lower_threshold_pct_fall : 10; + slew_upper_threshold_pct_rise : 90; + slew_upper_threshold_pct_fall : 90; + slew_derate_from_library : 1.0; + + + nom_process : 1.0; + nom_temperature : 25.0; + nom_voltage : 0.70; + + lu_table_template(template_1) { + variable_1 : total_output_net_capacitance; + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + } + + cell ("timing_cell") { + area : 0.948 + is_macro_cell : true; + pin("in") { + direction : input; + capacitance : 0.5587; + timing() { + related_pin : "clk"; + timing_type : hold_rising; + rise_constraint(scalar) { + values("-3.16857"); + } + fall_constraint(scalar) { + values("9.34610"); + } + paths() { + slack : -9.34610; + fall_data_required() { + time: 9.34610; + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 0.00000, 6.72075, 0.47543, 0); + } + fall_data_arrival() { + time: 0.00000; + vertex("", "timing_cell", "in", "", "v", 0.00000, 0.00000, 0.48550, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/D", "in", "v", 0.00000, 0.00000, 0.48550, 0); + } + rise_data_required() { + time: -3.16857; + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 0.00000, 6.72075, 0.47554, 0); + } + rise_data_arrival() { + time: 0.00000; + vertex("", "timing_cell", "in", "", "^", 0.00000, 0.00000, 0.49848, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/D", "in", "^", 0.00000, 0.00000, 0.49848, 0); + } + } + } + timing() { + related_pin : "clk"; + timing_type : setup_rising; + rise_constraint(scalar) { + values("11.41230"); + } + fall_constraint(scalar) { + values("3.06735"); + } + paths() { + slack : 488.58768; + fall_data_required() { + time: 496.93268; + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 0.00000, 6.22442, 0.35251, 0); + } + fall_data_arrival() { + time: 0.00000; + vertex("", "timing_cell", "in", "", "v", 0.00000, 0.00000, 0.55560, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/D", "in", "v", 0.00000, 0.00000, 0.55560, 0); + } + rise_data_required() { + time: 488.58768; + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 0.00000, 6.22442, 0.35582, 0); + } + rise_data_arrival() { + time: 0.00000; + vertex("", "timing_cell", "in", "", "^", 0.00000, 0.00000, 0.55869, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/D", "in", "^", 0.00000, 0.00000, 0.55869, 0); + } + } + } + } + pin("clk") { + direction : input; + clock : true; + capacitance : 1.0098; + timing() { + timing_sense : positive_unate; + timing_type : min_clock_tree_path; + cell_rise(scalar) { + values("0.00000"); + } + cell_fall(scalar) { + values("0.00000"); + } + } + timing() { + timing_sense : positive_unate; + timing_type : max_clock_tree_path; + cell_rise(scalar) { + values("13.04533"); + } + cell_fall(scalar) { + values("14.75609"); + } + } + } + pin("out") { + direction : output; + capacitance : 0.0000; + timing() { + related_pin : "clk"; + timing_type : rising_edge; + cell_rise(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("66.16869,68.40050,71.97030,78.11000,89.73939,112.75500,158.58101"); + } + rise_transition(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("10.66370,13.37680,18.80680,30.41660,54.95930,105.42699,207.52800"); + } + cell_fall(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("61.68679,63.66789,66.83360,72.15240,81.86710,100.74999,138.30800"); + } + fall_transition(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("8.76410,11.02540,15.42310,24.41700,43.13271,81.68120,159.89299"); + } + paths() { + slack : 59.70571; + fall_clocked_output() { + time: 59.70571; + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/CLK", "clk", "^", 0.00000, 0.00000, 0.74655, 0); + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/Q", "out", "v", 59.70571, 6.50280, 0.00000, 1); + vertex("", "timing_cell", "out", "", "v", 59.70571, 6.50280, 0.00000, 0); + } + rise_clocked_output() { + time: 63.93688; + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/CLK", "clk", "^", 0.00000, 0.00000, 0.75516, 0); + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/Q", "out", "^", 63.93688, 7.95060, 0.00000, 1); + vertex("", "timing_cell", "out", "", "^", 63.93688, 7.95060, 0.00000, 0); + } + } + } + } + worst_slack_paths() { + min_rise() { + slack : 100.45280; + rise_data_arrival() { + time: 96.10009; + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 0.00000, 6.22442, 0.35582, 0); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/Q", "w1", "^", 64.55580, 8.70299, 0.39934, 1); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/A", "w1", "^", 64.55580, 8.70299, 0.39934, 0); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/Y", "w2", "^", 80.64001, 6.39546, 0.39934, 1); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/A", "w2", "^", 80.64001, 6.39546, 0.39934, 0); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/Y", "w3", "^", 96.10009, 6.80940, 0.49848, 1); + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/D", "w3", "^", 96.10009, 6.80940, 0.49848, 0); + } + rise_data_required() { + time: -4.35271; + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/CLK", "clk", "^", 0.00000, 0.00000, 1.00982, 0); + } + } + min_fall() { + slack : 85.31351; + fall_data_arrival() { + time: 94.09145; + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 0.00000, 6.22442, 0.35251, 0); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/Q", "w1", "v", 60.24781, 7.12158, 0.39404, 1); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/A", "w1", "v", 60.24781, 7.12158, 0.39404, 0); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/Y", "w2", "v", 77.21077, 6.29417, 0.39404, 1); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/A", "w2", "v", 77.21077, 6.29417, 0.39404, 0); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/Y", "w3", "v", 94.09145, 6.58809, 0.48550, 1); + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/D", "w3", "v", 94.09145, 6.58809, 0.48550, 0); + } + fall_data_required() { + time: 8.77794; + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/CLK", "clk", "^", 0.00000, 0.00000, 1.00966, 0); + } + } + max_rise() { + slack : 390.33447; + rise_data_arrival() { + time: 97.05834; + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 0.00000, 6.72075, 0.47554, 0); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/Q", "w1", "^", 64.76494, 8.95723, 0.53428, 1); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/A", "w1", "^", 64.76494, 8.95723, 0.53428, 0); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/Y", "w2", "^", 81.25244, 6.95546, 0.53428, 1); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/A", "w2", "^", 81.25244, 6.95546, 0.53428, 0); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/Y", "w3", "^", 97.05834, 7.05876, 0.55869, 1); + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/D", "w3", "^", 97.05834, 7.05876, 0.55869, 0); + } + rise_data_required() { + time: 487.39279; + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/CLK", "clk", "^", 0.00000, 0.00000, 0.75516, 0); + } + } + max_fall() { + slack : 400.75314; + fall_data_arrival() { + time: 94.95612; + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 0.00000, 6.72075, 0.47543, 0); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/Q", "w1", "v", 60.44068, 7.34173, 0.53423, 1); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/A", "w1", "v", 60.44068, 7.34173, 0.53423, 0); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/Y", "w2", "v", 77.77494, 6.74820, 0.53423, 1); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/A", "w2", "v", 77.77494, 6.74820, 0.53423, 0); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/Y", "w3", "v", 94.95612, 6.81597, 0.55560, 1); + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/D", "w3", "v", 94.95612, 6.81597, 0.55560, 0); + } + fall_data_required() { + time: 495.70926; + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/CLK", "clk", "^", 0.00000, 0.00000, 0.74655, 0); + } + } + } + } + +} diff --git a/test/timing_paths_non_propagated_clock.tcl b/test/timing_paths_non_propagated_clock.tcl new file mode 100644 index 00000000..5d7765f1 --- /dev/null +++ b/test/timing_paths_non_propagated_clock.tcl @@ -0,0 +1,7 @@ +# timing paths propagated clock example +read_liberty asap7_small.lib.gz +read_verilog timing_paths_propagated_clock.v +link_design timing_cell +create_clock -name clk -period 500 {clk} +set_input_delay -clock clk 0 {in} +write_timing_model -paths results/timing_paths_non_propagated_clock.log diff --git a/test/timing_paths_propagated_clock.ok b/test/timing_paths_propagated_clock.ok new file mode 100644 index 00000000..bf460dcf --- /dev/null +++ b/test/timing_paths_propagated_clock.ok @@ -0,0 +1,258 @@ +library (timing_cell) { + comment : ""; + delay_model : table_lookup; + simulation : false; + capacitive_load_unit (1,fF); + leakage_power_unit : 1pW; + current_unit : "1mA"; + pulling_resistance_unit : "1kohm"; + time_unit : "1ps"; + 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 : 10; + slew_lower_threshold_pct_fall : 10; + slew_upper_threshold_pct_rise : 90; + slew_upper_threshold_pct_fall : 90; + slew_derate_from_library : 1.0; + + + nom_process : 1.0; + nom_temperature : 25.0; + nom_voltage : 0.70; + + lu_table_template(template_1) { + variable_1 : total_output_net_capacitance; + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + } + + cell ("timing_cell") { + area : 0.948 + is_macro_cell : true; + pin("in") { + direction : input; + capacitance : 0.5587; + timing() { + related_pin : "clk"; + timing_type : hold_rising; + rise_constraint(scalar) { + values("10.68728"); + } + fall_constraint(scalar) { + values("24.81533"); + } + paths() { + slack : -24.81533; + fall_data_required() { + time: 24.81533; + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/A", "clk", "^", 0.00000, 0.00000, 1.00966, 0); + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/Y", "clk_d", "^", 13.04533, 6.72075, 0.47543, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 13.04533, 6.72075, 0.47543, 0); + } + fall_data_arrival() { + time: 0.00000; + vertex("", "timing_cell", "in", "", "v", 0.00000, 0.00000, 0.48550, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/D", "in", "v", 0.00000, 0.00000, 0.48550, 0); + } + rise_data_required() { + time: 10.68728; + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/A", "clk", "^", 0.00000, 0.00000, 1.00982, 0); + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/Y", "clk_d", "^", 13.04533, 6.72075, 0.47554, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 13.04533, 6.72075, 0.47554, 0); + } + rise_data_arrival() { + time: 0.00000; + vertex("", "timing_cell", "in", "", "^", 0.00000, 0.00000, 0.49848, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/D", "in", "^", 0.00000, 0.00000, 0.49848, 0); + } + } + } + timing() { + related_pin : "clk"; + timing_type : setup_rising; + rise_constraint(scalar) { + values("-2.69291"); + } + fall_constraint(scalar) { + values("-11.90656"); + } + paths() { + slack : 502.69290; + fall_data_required() { + time: 511.90652; + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/A", "clk", "^", 0.00000, 0.00000, 0.74655, 0); + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/Y", "clk_d", "^", 12.76048, 6.22442, 0.35251, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 12.76048, 6.22442, 0.35251, 0); + } + fall_data_arrival() { + time: 0.00000; + vertex("", "timing_cell", "in", "", "v", 0.00000, 0.00000, 0.55560, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/D", "in", "v", 0.00000, 0.00000, 0.55560, 0); + } + rise_data_required() { + time: 502.69290; + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/A", "clk", "^", 0.00000, 0.00000, 0.75516, 0); + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/Y", "clk_d", "^", 12.76048, 6.22442, 0.35582, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 12.76048, 6.22442, 0.35582, 0); + } + rise_data_arrival() { + time: 0.00000; + vertex("", "timing_cell", "in", "", "^", 0.00000, 0.00000, 0.55869, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/D", "in", "^", 0.00000, 0.00000, 0.55869, 0); + } + } + } + } + pin("clk") { + direction : input; + clock : true; + capacitance : 1.0098; + timing() { + timing_sense : positive_unate; + timing_type : min_clock_tree_path; + cell_rise(scalar) { + values("0.00000"); + } + cell_fall(scalar) { + values("0.00000"); + } + } + timing() { + timing_sense : positive_unate; + timing_type : max_clock_tree_path; + cell_rise(scalar) { + values("13.04533"); + } + cell_fall(scalar) { + values("14.75609"); + } + } + } + pin("out") { + direction : output; + capacitance : 0.0000; + timing() { + related_pin : "clk"; + timing_type : rising_edge; + cell_rise(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("66.16869,68.40050,71.97030,78.11000,89.73939,112.75500,158.58101"); + } + rise_transition(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("10.66370,13.37680,18.80680,30.41660,54.95930,105.42699,207.52800"); + } + cell_fall(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("61.68679,63.66789,66.83360,72.15240,81.86710,100.74999,138.30800"); + } + fall_transition(template_1) { + index_1("1.44000, 2.88000, 5.76000, 11.52000, 23.04000, 46.08000, 92.16000"); + values("8.76410,11.02540,15.42310,24.41700,43.13271,81.68120,159.89299"); + } + paths() { + slack : 59.70571; + fall_clocked_output() { + time: 59.70571; + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/CLK", "clk", "^", 0.00000, 0.00000, 0.74655, 0); + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/Q", "out", "v", 59.70571, 6.50280, 0.00000, 1); + vertex("", "timing_cell", "out", "", "v", 59.70571, 6.50280, 0.00000, 0); + } + rise_clocked_output() { + time: 63.93688; + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/CLK", "clk", "^", 0.00000, 0.00000, 0.75516, 0); + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/Q", "out", "^", 63.93688, 7.95060, 0.00000, 1); + vertex("", "timing_cell", "out", "", "^", 63.93688, 7.95060, 0.00000, 0); + } + } + } + } + worst_slack_paths() { + min_rise() { + slack : 115.38334; + rise_data_arrival() { + time: 111.03062; + vertex("", "timing_cell", "clk", "", "^", 0.00000, 0.00000, 0.75516, 1); + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/A", "clk", "^", 0.00000, 0.00000, 0.75516, 0); + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/Y", "clk_d", "^", 12.76048, 6.22442, 0.35582, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 12.76048, 6.22442, 0.35582, 0); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/Q", "w1", "^", 79.48183, 8.71519, 0.39934, 1); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/A", "w1", "^", 79.48183, 8.71519, 0.39934, 0); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/Y", "w2", "^", 95.57055, 6.39545, 0.39934, 1); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/A", "w2", "^", 95.57055, 6.39545, 0.39934, 0); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/Y", "w3", "^", 111.03062, 6.80940, 0.49848, 1); + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/D", "w3", "^", 111.03062, 6.80940, 0.49848, 0); + } + rise_data_required() { + time: -4.35271; + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/CLK", "clk", "^", 0.00000, 0.00000, 1.00982, 0); + } + } + min_fall() { + slack : 100.27909; + fall_data_arrival() { + time: 109.05704; + vertex("", "timing_cell", "clk", "", "^", 0.00000, 0.00000, 0.74655, 1); + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/A", "clk", "^", 0.00000, 0.00000, 0.74655, 0); + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/Y", "clk_d", "^", 12.76048, 6.22442, 0.35251, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 12.76048, 6.22442, 0.35251, 0); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/Q", "w1", "v", 75.21534, 7.11580, 0.39404, 1); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/A", "w1", "v", 75.21534, 7.11580, 0.39404, 0); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/Y", "w2", "v", 92.17636, 6.29415, 0.39404, 1); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/A", "w2", "v", 92.17636, 6.29415, 0.39404, 0); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/Y", "w3", "v", 109.05704, 6.58809, 0.48550, 1); + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/D", "w3", "v", 109.05704, 6.58809, 0.48550, 0); + } + fall_data_required() { + time: 8.77794; + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/CLK", "clk", "^", 0.00000, 0.00000, 1.00966, 0); + } + } + max_rise() { + slack : 374.94595; + rise_data_arrival() { + time: 112.44682; + vertex("", "timing_cell", "clk", "", "^", 0.00000, 0.00000, 1.00982, 1); + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/A", "clk", "^", 0.00000, 0.00000, 1.00982, 0); + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/Y", "clk_d", "^", 13.04533, 6.72075, 0.47554, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 13.04533, 6.72075, 0.47554, 0); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/Q", "w1", "^", 80.14861, 8.97026, 0.53428, 1); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/A", "w1", "^", 80.14861, 8.97026, 0.53428, 0); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/Y", "w2", "^", 96.64091, 6.95545, 0.53428, 1); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/A", "w2", "^", 96.64091, 6.95545, 0.53428, 0); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/Y", "w3", "^", 112.44682, 7.05876, 0.55869, 1); + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/D", "w3", "^", 112.44682, 7.05876, 0.55869, 0); + } + rise_data_required() { + time: 487.39279; + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/CLK", "clk", "^", 0.00000, 0.00000, 0.75516, 0); + } + } + max_fall() { + slack : 385.32700; + fall_data_arrival() { + time: 110.38225; + vertex("", "timing_cell", "clk", "", "^", 0.00000, 0.00000, 1.00966, 1); + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/A", "clk", "^", 0.00000, 0.00000, 1.00966, 0); + vertex("b1", "BUFx2_ASAP7_75t_R", "b1/Y", "clk_d", "^", 13.04533, 6.72075, 0.47543, 1); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/CLK", "clk_d", "^", 13.04533, 6.72075, 0.47543, 0); + vertex("d1", "DFFHQx4_ASAP7_75t_R", "d1/Q", "w1", "v", 75.86876, 7.33594, 0.53423, 1); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/A", "w1", "v", 75.86876, 7.33594, 0.53423, 0); + vertex("b2", "BUFx2_ASAP7_75t_R", "b2/Y", "w2", "v", 93.20108, 6.74819, 0.53423, 1); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/A", "w2", "v", 93.20108, 6.74819, 0.53423, 0); + vertex("b3", "BUFx2_ASAP7_75t_R", "b3/Y", "w3", "v", 110.38225, 6.81597, 0.55560, 1); + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/D", "w3", "v", 110.38225, 6.81597, 0.55560, 0); + } + fall_data_required() { + time: 495.70926; + vertex("d2", "DFFHQx4_ASAP7_75t_R", "d2/CLK", "clk", "^", 0.00000, 0.00000, 0.74655, 0); + } + } + } + } + +} diff --git a/test/timing_paths_propagated_clock.tcl b/test/timing_paths_propagated_clock.tcl new file mode 100644 index 00000000..6e2276f3 --- /dev/null +++ b/test/timing_paths_propagated_clock.tcl @@ -0,0 +1,8 @@ +# timing paths non propagated clock example +read_liberty asap7_small.lib.gz +read_verilog timing_paths_propagated_clock.v +link_design timing_cell +create_clock -name clk -period 500 {clk} +set_propagated_clock {clk} +set_input_delay -clock clk 0 {in} +write_timing_model -paths results/timing_paths_propagated_clock.log diff --git a/test/timing_paths_propagated_clock.v b/test/timing_paths_propagated_clock.v new file mode 100644 index 00000000..b969753f --- /dev/null +++ b/test/timing_paths_propagated_clock.v @@ -0,0 +1,14 @@ +module timing_cell (input in, input clk, output out); + + wire clk_d, w1, w2, w3; + + BUFx2_ASAP7_75t_R b1 (.A(clk), .Y(clk_d)); + + DFFHQx4_ASAP7_75t_R d1 (.D(in), .Q(w1), .CLK(clk_d)); + + BUFx2_ASAP7_75t_R b2 (.A(w1), .Y(w2)); + BUFx2_ASAP7_75t_R b3 (.A(w2), .Y(w3)); + + DFFHQx4_ASAP7_75t_R d2 (.D(w3), .Q(out), .CLK(clk)); + +endmodule