From 8a9059545e12fddbaefa30d772ce17a4b1d9f7bc Mon Sep 17 00:00:00 2001 From: Wenz-jam Date: Sat, 1 Aug 2026 15:57:46 +0800 Subject: [PATCH 1/9] feat(ista): align SDF arc emission with PT --- src/operation/iSTA/interface/STAInterface.cpp | 1 + .../data_manager/advance/TimingCellArc.hpp | 3 ++ .../source/module/sdf_writer/SDFWriter.cpp | 46 +++++++++++++++++-- .../source/module/sdf_writer/SDFWriter.hpp | 1 + 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/operation/iSTA/interface/STAInterface.cpp b/src/operation/iSTA/interface/STAInterface.cpp index a298f6acb..d3f923755 100644 --- a/src/operation/iSTA/interface/STAInterface.cpp +++ b/src/operation/iSTA/interface/STAInterface.cpp @@ -835,6 +835,7 @@ void STAInterface::wrapTimingCellArc(TimingCell& timing_cell, idb::LibArcSet* li if (isSDFDelayArc(lib_arc)) { TimingCellArc timing_cell_arc = wrapDelayArc(lib_arc_set); timing_cell_arc.set_is_timing_graph_arc(lib_arc->isDelayArc()); + timing_cell_arc.set_is_clear_preset_arc(lib_arc->isClearPresetArc()); timing_cell.get_cell_arc_list().push_back(timing_cell_arc); if (lib_arc->isClearPresetArc()) { wrapClearPresetArc(timing_cell, lib_arc); diff --git a/src/operation/iSTA/source/data_manager/advance/TimingCellArc.hpp b/src/operation/iSTA/source/data_manager/advance/TimingCellArc.hpp index 2aea7d49c..ed64da21f 100644 --- a/src/operation/iSTA/source/data_manager/advance/TimingCellArc.hpp +++ b/src/operation/iSTA/source/data_manager/advance/TimingCellArc.hpp @@ -36,6 +36,7 @@ class TimingCellArc bool get_is_timing_graph_arc() const { return _is_timing_graph_arc; } bool get_is_clock_arc() const { return _is_clock_arc; } bool get_is_disable_arc() const { return _is_disable_arc; } + bool get_is_clear_preset_arc() const { return _is_clear_preset_arc; } // setter void set_source_port(const std::string& source_port) { _source_port = source_port; } void set_sink_port(const std::string& sink_port) { _sink_port = sink_port; } @@ -46,6 +47,7 @@ class TimingCellArc void set_is_timing_graph_arc(const bool is_timing_graph_arc) { _is_timing_graph_arc = is_timing_graph_arc; } void set_is_clock_arc(const bool is_clock_arc) { _is_clock_arc = is_clock_arc; } void set_is_disable_arc(const bool is_disable_arc) { _is_disable_arc = is_disable_arc; } + void set_is_clear_preset_arc(const bool is_clear_preset_arc) { _is_clear_preset_arc = is_clear_preset_arc; } // function private: @@ -58,6 +60,7 @@ class TimingCellArc bool _is_timing_graph_arc = true; bool _is_clock_arc = false; bool _is_disable_arc = false; + bool _is_clear_preset_arc = false; }; } // namespace ista diff --git a/src/operation/iSTA/source/module/sdf_writer/SDFWriter.cpp b/src/operation/iSTA/source/module/sdf_writer/SDFWriter.cpp index 9ba05d7bb..dc3ee456c 100644 --- a/src/operation/iSTA/source/module/sdf_writer/SDFWriter.cpp +++ b/src/operation/iSTA/source/module/sdf_writer/SDFWriter.cpp @@ -125,7 +125,7 @@ void SDFWriter::outputSDFInterconnect(std::ofstream* sdf_file) (*sdf_file) << " (DELAY\n"; (*sdf_file) << " (ABSOLUTE\n"; for (Arc& arc : database.get_arc_list()) { - if (arc.get_type() != ArcType::kNet || arc.get_is_disable_arc()) { + if (arc.get_type() != ArcType::kNet || arc.get_is_disable_arc() || isSDFOutputOnlyCellArc(arc)) { continue; } outputSDFInterconnectArc(sdf_file, arc); @@ -152,6 +152,36 @@ void SDFWriter::outputSDFInterconnectArc(std::ofstream* sdf_file, Arc& arc) (*sdf_file) << ")\n"; } +bool SDFWriter::isSDFOutputOnlyCellArc(Arc& arc) +{ + Database& database = STADM.getDatabase(); + auto source_pin_it = database.get_pin_map().find(arc.get_source_pin()); + if (source_pin_it == database.get_pin_map().end() || source_pin_it->second.get_is_port()) { + return false; + } + auto instance_it = database.get_instance_map().find(source_pin_it->second.get_instance_name()); + if (instance_it == database.get_instance_map().end()) { + return false; + } + + bool has_output_pin = false; + for (std::string& pin_name : instance_it->second.get_pin_name_list()) { + auto pin_it = database.get_pin_map().find(pin_name); + if (pin_it == database.get_pin_map().end()) { + continue; + } + + const PinDirection direction = pin_it->second.get_direction(); + if (direction == PinDirection::kInput || direction == PinDirection::kInout) { + return false; + } + if (direction == PinDirection::kOutput) { + has_output_pin = true; + } + } + return has_output_pin; +} + void SDFWriter::outputSDFCellList(std::ofstream* sdf_file) { Database& database = STADM.getDatabase(); @@ -317,7 +347,8 @@ void SDFWriter::outputSDFOnlyCellArcList(std::ofstream* sdf_file, Instance& inst return; } for (TimingCellArc& timing_cell_arc : timing_cell->get_cell_arc_list()) { - if (timing_cell_arc.get_is_timing_graph_arc() || timing_cell_arc.get_is_disable_arc() || !isSDFCellArc(instance, timing_cell_arc)) { + if (timing_cell_arc.get_is_timing_graph_arc() || timing_cell_arc.get_is_clear_preset_arc() || timing_cell_arc.get_is_disable_arc() + || !isSDFCellArc(instance, timing_cell_arc)) { continue; } outputSDFOnlyCellArc(sdf_file, instance, timing_cell_arc); @@ -490,10 +521,19 @@ void SDFWriter::outputSDFWidthTimingCheck(std::ofstream* sdf_file, Instance& ins } std::string port_name = getSDFPortName(database.get_pin_map()[pin_name]); std::string edge_name = getSDFEdgeName(trans_type); + std::string condition = getSDFCondition(timing_arc); double min_delay = getSDFTimingCheckDelay(instance, timing_check_arc, timing_arc, AnalysisType::kMin, trans_type); double max_delay = getSDFTimingCheckDelay(instance, timing_check_arc, timing_arc, AnalysisType::kMax, trans_type); - (*sdf_file) << " (WIDTH (" << edge_name << " " << port_name << ") "; + (*sdf_file) << " (WIDTH "; + if (!condition.empty()) { + (*sdf_file) << "(COND " << condition << " "; + } + (*sdf_file) << "(" << edge_name << " " << port_name << ")"; + if (!condition.empty()) { + (*sdf_file) << ")"; + } + (*sdf_file) << " "; outputSDFTriple(sdf_file, min_delay, max_delay); (*sdf_file) << ")\n"; } diff --git a/src/operation/iSTA/source/module/sdf_writer/SDFWriter.hpp b/src/operation/iSTA/source/module/sdf_writer/SDFWriter.hpp index e474d9d71..afeb78fbe 100644 --- a/src/operation/iSTA/source/module/sdf_writer/SDFWriter.hpp +++ b/src/operation/iSTA/source/module/sdf_writer/SDFWriter.hpp @@ -61,6 +61,7 @@ class SDFWriter void outputSDFHeader(std::ofstream* sdf_file); void outputSDFInterconnect(std::ofstream* sdf_file); void outputSDFInterconnectArc(std::ofstream* sdf_file, Arc& arc); + bool isSDFOutputOnlyCellArc(Arc& arc); void outputSDFCellList(std::ofstream* sdf_file); void buildInstanceCellArcMap(); void outputSDFCell(std::ofstream* sdf_file, Instance& instance); From 75c3f7cc9c2052f0db7b2a1263c4e1893b1c7a5a Mon Sep 17 00:00:00 2001 From: Wenz-jam Date: Sun, 2 Aug 2026 14:50:41 +0800 Subject: [PATCH 2/9] feat(ista): align SDF arc emission with PT --- .../delay_calculator/DelayCalculator.cpp | 4 ++- .../dc_data_manager/DCTask.hpp | 10 ++++++ .../timing_propagator/TimingPropagator.cpp | 35 ++++++++++++++++--- .../timing_propagator/TimingPropagator.hpp | 1 + 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/operation/iSTA/source/module/delay_calculator/DelayCalculator.cpp b/src/operation/iSTA/source/module/delay_calculator/DelayCalculator.cpp index af639729f..89ba882b4 100644 --- a/src/operation/iSTA/source/module/delay_calculator/DelayCalculator.cpp +++ b/src/operation/iSTA/source/module/delay_calculator/DelayCalculator.cpp @@ -190,8 +190,10 @@ void DelayCalculator::calculateArc(DCTask& dc_task) timing_result.set_output_trans_type(dc_task.get_output_trans_type()); timing_result.set_delay(calcArcDelay(*arc, dc_task.get_analysis_type(), dc_task.get_input_trans_type(), dc_task.get_output_trans_type(), dc_task.get_input_slew())); + const double output_slew_input_slew + = dc_task.get_has_output_slew_input_slew() ? dc_task.get_output_slew_input_slew() : dc_task.get_input_slew(); timing_result.set_slew(calcArcSlew(*arc, dc_task.get_analysis_type(), dc_task.get_input_trans_type(), dc_task.get_output_trans_type(), - dc_task.get_input_slew())); + output_slew_input_slew)); dc_task.set_timing_result(timing_result); dc_task.set_is_valid(true); } diff --git a/src/operation/iSTA/source/module/delay_calculator/dc_data_manager/DCTask.hpp b/src/operation/iSTA/source/module/delay_calculator/dc_data_manager/DCTask.hpp index 53b67690c..d27ce3e6b 100644 --- a/src/operation/iSTA/source/module/delay_calculator/dc_data_manager/DCTask.hpp +++ b/src/operation/iSTA/source/module/delay_calculator/dc_data_manager/DCTask.hpp @@ -42,6 +42,8 @@ class DCTask TransType& get_input_trans_type() { return _input_trans_type; } TransType& get_output_trans_type() { return _output_trans_type; } double get_input_slew() { return _input_slew; } + double get_output_slew_input_slew() { return _output_slew_input_slew; } + bool get_has_output_slew_input_slew() { return _has_output_slew_input_slew; } TransType& get_clock_trans_type() { return _clock_trans_type; } TransType& get_data_trans_type() { return _data_trans_type; } double get_clock_slew() { return _clock_slew; } @@ -58,6 +60,11 @@ class DCTask void set_input_trans_type(const TransType& input_trans_type) { _input_trans_type = input_trans_type; } void set_output_trans_type(const TransType& output_trans_type) { _output_trans_type = output_trans_type; } void set_input_slew(const double input_slew) { _input_slew = input_slew; } + void set_output_slew_input_slew(const double input_slew) + { + _output_slew_input_slew = input_slew; + _has_output_slew_input_slew = true; + } void set_clock_trans_type(const TransType& clock_trans_type) { _clock_trans_type = clock_trans_type; } void set_data_trans_type(const TransType& data_trans_type) { _data_trans_type = data_trans_type; } void set_clock_slew(const double clock_slew) { _clock_slew = clock_slew; } @@ -76,6 +83,9 @@ class DCTask TransType _input_trans_type = TransType::kNone; TransType _output_trans_type = TransType::kNone; double _input_slew = 0.0; + // A sequential C2Q arc can use a different slew for delay and transition lookup. + double _output_slew_input_slew = 0.0; + bool _has_output_slew_input_slew = false; TransType _clock_trans_type = TransType::kNone; TransType _data_trans_type = TransType::kNone; double _clock_slew = 0.0; diff --git a/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.cpp b/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.cpp index 190e6a59a..bee0ccdaa 100644 --- a/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.cpp +++ b/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.cpp @@ -101,6 +101,18 @@ bool TimingPropagator::isSequentialClockPin(std::string& pin_name) return instance.get_is_sequential() && pin_name == instance.get_clock_pin_name(); } +bool TimingPropagator::hasIncomingPhysicalSlewArc(std::string& pin_name) +{ + Database& database = STADM.getDatabase(); + for (std::size_t arc_idx : database.get_incoming_arc_list_map()[pin_name]) { + Arc& arc = database.get_arc_list()[arc_idx]; + if (arc.get_type() == ArcType::kNet && !isDisableArc(arc)) { + return true; + } + } + return false; +} + TPModel TimingPropagator::initTPModel() { TPModel tp_model; @@ -193,8 +205,16 @@ void TimingPropagator::seedDataSlew(std::string& start_point, AnalysisType analy void TimingPropagator::seedDataSlew(std::string& start_point, AnalysisType analysis_type, TransType trans_type) { Database& database = STADM.getDatabase(); - database.get_timing_point_map()[start_point].get_data_slew_map()[analysis_type][trans_type] - = getStartPointSlew(start_point, analysis_type, trans_type); + TimingPoint& timing_point = database.get_timing_point_map()[start_point]; + if (isSequentialClockPin(start_point) + && hasIncomingPhysicalSlewArc(start_point) + && (timing_point.get_clock_slew_map().count(analysis_type) == 0 + || timing_point.get_clock_slew_map()[analysis_type].count(trans_type) == 0)) { + // A propagated divider or gated-clock path will provide the physical slew + // through this net arc. Seeding zero here would win min-slew selection. + return; + } + timing_point.get_data_slew_map()[analysis_type][trans_type] = getStartPointSlew(start_point, analysis_type, trans_type); } void TimingPropagator::propagateDataSlewDelayArc(std::size_t arc_idx) @@ -212,9 +232,8 @@ void TimingPropagator::propagateDataSlewDelayArc(std::size_t arc_idx, AnalysisTy if (isDisableArc(arc)) { return; } - if (shouldStopDataPropagation(arc)) { - return; - } + // Slew must reach a sequential CK for C2Q delay and timing checks. Arrival + // and path-state propagation still stop at this boundary. TimingPoint& source_point = database.get_timing_point_map()[arc.get_source_pin()]; if (!hasDataSlew(source_point, analysis_type, input_trans_type)) { return; @@ -236,6 +255,12 @@ void TimingPropagator::updateDataSlewDelay(Arc& arc, TimingPoint& source_point, dc_task.set_input_trans_type(input_trans_type); dc_task.set_output_trans_type(output_trans_type); dc_task.set_input_slew(input_slew); + if (isSequentialClockPin(arc.get_source_pin())) { + // PT uses the ordinary propagated slew for C2Q delay, but uses the clock + // slew for its output-transition lookup. A CK reached through data logic + // has no clock slew, so the latter intentionally falls back to zero. + dc_task.set_output_slew_input_slew(getClockSlew(arc.get_source_pin(), analysis_type, input_trans_type)); + } STADC.calculate(dc_task); if (!dc_task.get_is_valid()) { return; diff --git a/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.hpp b/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.hpp index 8c03aa6b4..0354ee169 100644 --- a/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.hpp +++ b/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.hpp @@ -45,6 +45,7 @@ class TimingPropagator bool isDisableArc(Arc& arc); bool shouldStopDataPropagation(Arc& arc); bool isSequentialClockPin(std::string& pin_name); + bool hasIncomingPhysicalSlewArc(std::string& pin_name); TPModel initTPModel(); void buildStartPointList(TPModel& tp_model); double getClockArrival(std::string& pin_name, AnalysisType analysis_type); From d1b5ba1c50839fd2cfe8f673a5946bf02b121c1b Mon Sep 17 00:00:00 2001 From: Wenz-jam Date: Sun, 2 Aug 2026 14:56:59 +0800 Subject: [PATCH 3/9] fix(ista): order WIDTH SDF timing checks --- src/operation/iSTA/source/module/sdf_writer/SDFWriter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/operation/iSTA/source/module/sdf_writer/SDFWriter.cpp b/src/operation/iSTA/source/module/sdf_writer/SDFWriter.cpp index dc3ee456c..a148e545e 100644 --- a/src/operation/iSTA/source/module/sdf_writer/SDFWriter.cpp +++ b/src/operation/iSTA/source/module/sdf_writer/SDFWriter.cpp @@ -524,6 +524,11 @@ void SDFWriter::outputSDFWidthTimingCheck(std::ofstream* sdf_file, Instance& ins std::string condition = getSDFCondition(timing_arc); double min_delay = getSDFTimingCheckDelay(instance, timing_check_arc, timing_arc, AnalysisType::kMin, trans_type); double max_delay = getSDFTimingCheckDelay(instance, timing_check_arc, timing_arc, AnalysisType::kMax, trans_type); + // Keep the WIDTH SDF triple ordered when the constraint table reverses the + // numeric order of the analysis corners. + if (min_delay > max_delay) { + std::swap(min_delay, max_delay); + } (*sdf_file) << " (WIDTH "; if (!condition.empty()) { From f4fefb44b86ac5791779a775c58d0d76abfac3c3 Mon Sep 17 00:00:00 2001 From: Wenz-jam Date: Tue, 4 Aug 2026 13:53:18 +0800 Subject: [PATCH 4/9] fix: drop bmp.hpp include (no side effects) to fix build with g++11 --- src/platform/report/report_place/report_place.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/report/report_place/report_place.h b/src/platform/report/report_place/report_place.h index 1934e481d..6df17db89 100644 --- a/src/platform/report/report_place/report_place.h +++ b/src/platform/report/report_place/report_place.h @@ -17,7 +17,7 @@ #pragma once // #include "utility/logger/Logger.hpp" -#include +// #include #include #include "IdbInstance.h" From d8e58a28e180b59f9aef76b3be04d7ef1f9a4e11 Mon Sep 17 00:00:00 2001 From: Wenz-jam Date: Tue, 4 Aug 2026 17:09:35 +0800 Subject: [PATCH 5/9] refactor(ista): replace recursive parameter pack expansion with fold expression Use C++17 fold expression to simplify pushStream, and cast to void to suppress unused-value warnings. --- src/operation/iSTA/source/toolkit/logger/Logger.hpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/operation/iSTA/source/toolkit/logger/Logger.hpp b/src/operation/iSTA/source/toolkit/logger/Logger.hpp index 72ec5799e..d793af3b0 100644 --- a/src/operation/iSTA/source/toolkit/logger/Logger.hpp +++ b/src/operation/iSTA/source/toolkit/logger/Logger.hpp @@ -157,13 +157,12 @@ class Logger void pushStream(Stream& stream, T t, const Args&... args) { stream << t; - pushStream(stream, args...); - } - - template - void pushStream(Stream& stream, T t) - { - stream << t; + /* + * this equals to + * stream << args[0] << args[1], ... + * use void to suppress unused-value warning for fold expression + */ + (void)(stream << ... << args); } std::string getTimestamp() From df6e0b58c09068649b917b21299a13f5659dd4e6 Mon Sep 17 00:00:00 2001 From: Wenz-jam Date: Tue, 4 Aug 2026 17:25:07 +0800 Subject: [PATCH 6/9] annotate(ista): mark Logger::error() as [[noreturn]] --- src/operation/iSTA/source/toolkit/logger/Logger.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/operation/iSTA/source/toolkit/logger/Logger.hpp b/src/operation/iSTA/source/toolkit/logger/Logger.hpp index d793af3b0..592850d00 100644 --- a/src/operation/iSTA/source/toolkit/logger/Logger.hpp +++ b/src/operation/iSTA/source/toolkit/logger/Logger.hpp @@ -67,7 +67,7 @@ class Logger } template - void error(Loc location, const T& value, const Args&... args) + [[noreturn]] void error(Loc location, const T& value, const Args&... args) { printLog(LogLevel::kError, location, value, args...); closeLogFileStream(); From 4f2da980a3cc155fcee5058332af560cda67902c Mon Sep 17 00:00:00 2001 From: Wenz-jam Date: Tue, 4 Aug 2026 22:42:59 +0800 Subject: [PATCH 7/9] refactor(ista): use string_view for utility file path APIs --- src/operation/iSTA/source/toolkit/utility/Utility.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/operation/iSTA/source/toolkit/utility/Utility.hpp b/src/operation/iSTA/source/toolkit/utility/Utility.hpp index 0b6e53172..8f0b18eda 100644 --- a/src/operation/iSTA/source/toolkit/utility/Utility.hpp +++ b/src/operation/iSTA/source/toolkit/utility/Utility.hpp @@ -171,14 +171,14 @@ class Utility return all; } - static std::ifstream* getInputFileStream(std::string file_path) { return getFileStream(file_path); } + static std::ifstream* getInputFileStream(const std::string_view file_path) { return getFileStream(std::string{file_path}); } - static std::ofstream* getOutputFileStream(std::string file_path) { return getFileStream(file_path); } + static std::ofstream* getOutputFileStream(const std::string_view file_path) { return getFileStream(std::string{file_path}); } template - static T* getFileStream(std::string file_path) + static T* getFileStream(const std::string_view file_path) { - T* file = new T(file_path); + T* file = new T(std::string{file_path}); if (!file->is_open()) { STALOG.error(Loc::current(), "Failed to open file '", file_path, "'!"); } From 552ca91bbc63d864f02eb890d67f6012f9ba312c Mon Sep 17 00:00:00 2001 From: Wenz-jam Date: Wed, 5 Aug 2026 16:04:50 +0800 Subject: [PATCH 8/9] feat(ista): add staged STA flow and clock Tcl commands and support ideal/propagated multi-clock trees. Add create_clock and set_propagated_clock support, together with update_timing, write_sdf, report_timing, and remove_wire_load_model Tcl commands. Split STA initialization from timing propagation and support ideal and propagated multi-clock trees. --- src/interface/tcl/tcl_ista/CMakeLists.txt | 8 + .../tcl/tcl_ista/include/tcl_ista_util.hpp | 48 ++++ .../tcl/tcl_ista/include/tcl_register_ista.h | 8 + src/interface/tcl/tcl_ista/include/tcl_sta.h | 88 ++++++- .../tcl/tcl_ista/src/tcl_create_clock.cpp | 63 +++++ .../tcl/tcl_ista/src/tcl_get_clocks.cpp | 44 ++++ .../tcl/tcl_ista/src/tcl_get_ports.cpp | 44 ++++ .../src/tcl_remove_wire_load_model.cpp | 31 +++ .../tcl/tcl_ista/src/tcl_report_timing.cpp | 39 +++ .../tcl/tcl_ista/src/tcl_run_sta.cpp | 2 +- .../tcl_ista/src/tcl_set_propagated_clock.cpp | 42 +++ .../tcl/tcl_ista/src/tcl_update_timing.cpp | 39 +++ .../tcl/tcl_ista/src/tcl_write_sdf.cpp | 42 +++ src/operation/iSTA/interface/STAInterface.cpp | 246 +++++++++++++++++- src/operation/iSTA/interface/STAInterface.hpp | 15 +- .../data_manager/advance/TimingPathState.hpp | 8 +- .../data_manager/advance/TimingPoint.hpp | 47 +++- .../clock_propagator/ClockPropagator.cpp | 201 ++++++++------ .../clock_propagator/ClockPropagator.hpp | 35 ++- .../cp_data_manager/CPModel.hpp | 39 ++- .../source/module/sdf_writer/SDFWriter.cpp | 12 +- .../source/module/sdf_writer/SDFWriter.hpp | 3 +- .../module/timing_analyzer/TimingAnalyzer.cpp | 27 +- .../module/timing_analyzer/TimingAnalyzer.hpp | 4 +- .../timing_propagator/TimingPropagator.cpp | 30 ++- .../timing_propagator/TimingPropagator.hpp | 3 +- 26 files changed, 1023 insertions(+), 145 deletions(-) create mode 100644 src/interface/tcl/tcl_ista/include/tcl_ista_util.hpp create mode 100644 src/interface/tcl/tcl_ista/src/tcl_create_clock.cpp create mode 100644 src/interface/tcl/tcl_ista/src/tcl_get_clocks.cpp create mode 100644 src/interface/tcl/tcl_ista/src/tcl_get_ports.cpp create mode 100644 src/interface/tcl/tcl_ista/src/tcl_remove_wire_load_model.cpp create mode 100644 src/interface/tcl/tcl_ista/src/tcl_report_timing.cpp create mode 100644 src/interface/tcl/tcl_ista/src/tcl_set_propagated_clock.cpp create mode 100644 src/interface/tcl/tcl_ista/src/tcl_update_timing.cpp create mode 100644 src/interface/tcl/tcl_ista/src/tcl_write_sdf.cpp diff --git a/src/interface/tcl/tcl_ista/CMakeLists.txt b/src/interface/tcl/tcl_ista/CMakeLists.txt index 44d627e7a..abbf6939f 100644 --- a/src/interface/tcl/tcl_ista/CMakeLists.txt +++ b/src/interface/tcl/tcl_ista/CMakeLists.txt @@ -1,6 +1,14 @@ add_library(tcl_ista ${HOME_INTERFACE}/tcl/tcl_ista/src/tcl_init_sta.cpp ${HOME_INTERFACE}/tcl/tcl_ista/src/tcl_run_sta.cpp + ${HOME_INTERFACE}/tcl/tcl_ista/src/tcl_remove_wire_load_model.cpp + ${HOME_INTERFACE}/tcl/tcl_ista/src/tcl_update_timing.cpp + ${HOME_INTERFACE}/tcl/tcl_ista/src/tcl_write_sdf.cpp + ${HOME_INTERFACE}/tcl/tcl_ista/src/tcl_report_timing.cpp + ${HOME_INTERFACE}/tcl/tcl_ista/src/tcl_create_clock.cpp + ${HOME_INTERFACE}/tcl/tcl_ista/src/tcl_set_propagated_clock.cpp + ${HOME_INTERFACE}/tcl/tcl_ista/src/tcl_get_clocks.cpp + ${HOME_INTERFACE}/tcl/tcl_ista/src/tcl_get_ports.cpp ${HOME_INTERFACE}/tcl/tcl_ista/src/tcl_extract_lib.cpp ${HOME_INTERFACE}/tcl/tcl_ista/src/tcl_destroy_sta.cpp ) diff --git a/src/interface/tcl/tcl_ista/include/tcl_ista_util.hpp b/src/interface/tcl/tcl_ista/include/tcl_ista_util.hpp new file mode 100644 index 000000000..c7b27ac5b --- /dev/null +++ b/src/interface/tcl/tcl_ista/include/tcl_ista_util.hpp @@ -0,0 +1,48 @@ +// *************************************************************************************** +// Copyright (c) 2023-2025 Peng Cheng Laboratory +// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences +// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip +// +// iEDA is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// +// See the Mulan PSL v2 for more details. +// *************************************************************************************** +#pragma once + +#include "tcl_util.h" + +namespace { + +void setResult(const std::string& result) +{ + Tcl_Interp* _interp{tcl::ScriptEngine::getOrCreateInstance()->get_interp()}; + auto* buffer = new char[result.size() + 1]; + std::strcpy(buffer, result.c_str()); + Tcl_SetResult(_interp, buffer, [](auto* buffer) { + delete[] buffer; + }); +} + +void setResult(const std::vector& result) +{ + Tcl_Obj* list_obj{Tcl_NewListObj(0, nullptr)}; + Tcl_Interp* _interp{tcl::ScriptEngine::getOrCreateInstance()->get_interp()}; + for (const std::string& value : result) { + Tcl_ListObjAppendElement(_interp, list_obj, Tcl_NewStringObj(value.c_str(), static_cast(value.size()))); + } + Tcl_SetObjResult(_interp, list_obj); +} + +void setTclError(const std::string& message) +{ + setResult(message); +} + +} // namespace diff --git a/src/interface/tcl/tcl_ista/include/tcl_register_ista.h b/src/interface/tcl/tcl_ista/include/tcl_register_ista.h index 034e7fce2..8f46333ab 100644 --- a/src/interface/tcl/tcl_ista/include/tcl_register_ista.h +++ b/src/interface/tcl/tcl_ista/include/tcl_register_ista.h @@ -27,6 +27,14 @@ int registerCmdSTA() // sta registerTclCmd(TclInitSTA, "init_sta"); registerTclCmd(TclRunSTA, "run_sta"); + registerTclCmd(TclRemoveWireLoadModel, "remove_wire_load_model"); + registerTclCmd(TclUpdateTiming, "update_timing"); + registerTclCmd(TclWriteSDF, "write_sdf"); + registerTclCmd(TclReportTiming, "report_timing"); + registerTclCmd(TclCreateClock, "create_clock"); + registerTclCmd(TclSetPropagatedClock, "set_propagated_clock"); + registerTclCmd(TclGetPorts, "get_ports"); + registerTclCmd(TclGetClocks, "get_clocks"); registerTclCmd(TclExtractLib, "extract_lib"); registerTclCmd(TclDestroySTA, "destroy_sta"); return EXIT_SUCCESS; diff --git a/src/interface/tcl/tcl_ista/include/tcl_sta.h b/src/interface/tcl/tcl_ista/include/tcl_sta.h index 24a85a27c..62642ac3f 100644 --- a/src/interface/tcl/tcl_ista/include/tcl_sta.h +++ b/src/interface/tcl/tcl_ista/include/tcl_sta.h @@ -28,7 +28,7 @@ class TclInitSTA : public TclCmd explicit TclInitSTA(const char* cmd_name); ~TclInitSTA() override = default; - unsigned check() override { return 1; }; + unsigned check() override { return 1; } unsigned exec() override; @@ -42,7 +42,7 @@ class TclRunSTA : public TclCmd explicit TclRunSTA(const char* cmd_name); ~TclRunSTA() override = default; - unsigned check() override { return 1; }; + unsigned check() override { return 1; } unsigned exec() override; @@ -50,13 +50,93 @@ class TclRunSTA : public TclCmd std::vector> _config_list; }; +class TclRemoveWireLoadModel : public TclCmd +{ + public: + explicit TclRemoveWireLoadModel(const char* cmd_name); + ~TclRemoveWireLoadModel() override = default; + + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclUpdateTiming : public TclCmd +{ + public: + explicit TclUpdateTiming(const char* cmd_name); + ~TclUpdateTiming() override = default; + + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclWriteSDF : public TclCmd +{ + public: + explicit TclWriteSDF(const char* cmd_name); + ~TclWriteSDF() override = default; + + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclReportTiming : public TclCmd +{ + public: + explicit TclReportTiming(const char* cmd_name); + ~TclReportTiming() override = default; + + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclCreateClock : public TclCmd +{ + public: + explicit TclCreateClock(const char* cmd_name); + ~TclCreateClock() override = default; + + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclSetPropagatedClock : public TclCmd +{ + public: + explicit TclSetPropagatedClock(const char* cmd_name); + ~TclSetPropagatedClock() override = default; + + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclGetPorts : public TclCmd +{ + public: + explicit TclGetPorts(const char* cmd_name); + ~TclGetPorts() override = default; + + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclGetClocks : public TclCmd +{ + public: + explicit TclGetClocks(const char* cmd_name); + ~TclGetClocks() override = default; + + unsigned check() override { return 1; } + unsigned exec() override; +}; + class TclExtractLib : public TclCmd { public: explicit TclExtractLib(const char* cmd_name); ~TclExtractLib() override = default; - unsigned check() override { return 1; }; + unsigned check() override { return 1; } unsigned exec() override; @@ -70,7 +150,7 @@ class TclDestroySTA : public TclCmd explicit TclDestroySTA(const char* cmd_name); ~TclDestroySTA() override = default; - unsigned check() override { return 1; }; + unsigned check() override { return 1; } unsigned exec() override; diff --git a/src/interface/tcl/tcl_ista/src/tcl_create_clock.cpp b/src/interface/tcl/tcl_ista/src/tcl_create_clock.cpp new file mode 100644 index 000000000..4750ab4a8 --- /dev/null +++ b/src/interface/tcl/tcl_ista/src/tcl_create_clock.cpp @@ -0,0 +1,63 @@ +// *************************************************************************************** +// Copyright (c) 2023-2025 Peng Cheng Laboratory +// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences +// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip +// +// iEDA is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// +// See the Mulan PSL v2 for more details. +// *************************************************************************************** +#include "STAInterface.hpp" +#include "tcl_ista_util.hpp" +#include "tcl_sta.h" + +namespace tcl { + +TclCreateClock::TclCreateClock(const char* cmd_name) : TclCmd(cmd_name) +{ + addOption(new TclStringOption("-name", 0)); + addOption(new TclDoubleOption("-period", 0)); + addOption(new TclDoubleListOption("-waveform", 0)); + addOption(new TclStringListOption("objects", 1)); +} + +unsigned TclCreateClock::exec() +{ + TclOption* name_option = getOptionOrArg("-name"); + TclOption* period_option = getOptionOrArg("-period"); + TclOption* waveform_option = getOptionOrArg("-waveform"); + TclOption* object_option = getOptionOrArg("objects"); + if (!name_option->is_set_val() || !period_option->is_set_val() || !object_option->is_set_val()) { + setTclError("create_clock requires -name, -period, and a port collection"); + return 0; + } + + const double period = period_option->getDoubleVal(); + double rise_edge = 0.0; + double fall_edge = period / 2.0; + if (waveform_option->is_set_val()) { + const std::vector waveform = waveform_option->getDoubleList(); + if (waveform.size() != 2) { + setTclError("create_clock -waveform must contain exactly two values"); + return 0; + } + rise_edge = waveform[0]; + fall_edge = waveform[1]; + } + + if (std::string error_message; + !STAI.createClock(name_option->getStringVal(), period, rise_edge, fall_edge, object_option->getStringList(), error_message)) { + setTclError(error_message); + return 0; + } + return 1; +} + +} // namespace tcl diff --git a/src/interface/tcl/tcl_ista/src/tcl_get_clocks.cpp b/src/interface/tcl/tcl_ista/src/tcl_get_clocks.cpp new file mode 100644 index 000000000..7a375e226 --- /dev/null +++ b/src/interface/tcl/tcl_ista/src/tcl_get_clocks.cpp @@ -0,0 +1,44 @@ +// *************************************************************************************** +// Copyright (c) 2023-2025 Peng Cheng Laboratory +// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences +// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip +// +// iEDA is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// +// See the Mulan PSL v2 for more details. +// *************************************************************************************** +#include "STAInterface.hpp" +#include "tcl_ista_util.hpp" +#include "tcl_sta.h" + +namespace tcl { + +TclGetClocks::TclGetClocks(const char* cmd_name) : TclCmd(cmd_name) +{ + addOption(new TclStringListOption("clocks", 1)); +} + +unsigned TclGetClocks::exec() +{ + TclOption* clock_option = getOptionOrArg("clocks"); + if (!clock_option->is_set_val()) { + setTclError("get_clocks requires a clock list"); + return 0; + } + std::vector resolved_clock_list; + if (std::string error_message; !STAI.getClocks(clock_option->getStringList(), resolved_clock_list, error_message)) { + setTclError(error_message); + return 0; + } + setResult(resolved_clock_list); + return 1; +} + +} // namespace tcl diff --git a/src/interface/tcl/tcl_ista/src/tcl_get_ports.cpp b/src/interface/tcl/tcl_ista/src/tcl_get_ports.cpp new file mode 100644 index 000000000..278adc3a1 --- /dev/null +++ b/src/interface/tcl/tcl_ista/src/tcl_get_ports.cpp @@ -0,0 +1,44 @@ +// *************************************************************************************** +// Copyright (c) 2023-2025 Peng Cheng Laboratory +// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences +// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip +// +// iEDA is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// +// See the Mulan PSL v2 for more details. +// *************************************************************************************** +#include "STAInterface.hpp" +#include "tcl_ista_util.hpp" +#include "tcl_sta.h" + +namespace tcl { + +TclGetPorts::TclGetPorts(const char* cmd_name) : TclCmd(cmd_name) +{ + addOption(new TclStringListOption("ports", 1)); +} + +unsigned TclGetPorts::exec() +{ + TclOption* port_option = getOptionOrArg("ports"); + if (!port_option->is_set_val()) { + setTclError("get_ports requires a port list"); + return 0; + } + std::vector resolved_port_list; + if (std::string error_message; !STAI.getPorts(port_option->getStringList(), resolved_port_list, error_message)) { + setTclError(error_message); + return 0; + } + setResult(resolved_port_list); + return 1; +} + +} // namespace tcl diff --git a/src/interface/tcl/tcl_ista/src/tcl_remove_wire_load_model.cpp b/src/interface/tcl/tcl_ista/src/tcl_remove_wire_load_model.cpp new file mode 100644 index 000000000..5c27c8a3f --- /dev/null +++ b/src/interface/tcl/tcl_ista/src/tcl_remove_wire_load_model.cpp @@ -0,0 +1,31 @@ +// *************************************************************************************** +// Copyright (c) 2023-2025 Peng Cheng Laboratory +// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences +// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip +// +// iEDA is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// +// See the Mulan PSL v2 for more details. +// *************************************************************************************** +#include "tcl_sta.h" + +namespace tcl { + +TclRemoveWireLoadModel::TclRemoveWireLoadModel(const char* cmd_name) : TclCmd(cmd_name) +{ +} + +unsigned TclRemoveWireLoadModel::exec() +{ + // Wire-load models are not modeled by iSTA yet. + return check(); +} + +} // namespace tcl diff --git a/src/interface/tcl/tcl_ista/src/tcl_report_timing.cpp b/src/interface/tcl/tcl_ista/src/tcl_report_timing.cpp new file mode 100644 index 000000000..ec5b5d8c2 --- /dev/null +++ b/src/interface/tcl/tcl_ista/src/tcl_report_timing.cpp @@ -0,0 +1,39 @@ +// *************************************************************************************** +// Copyright (c) 2023-2025 Peng Cheng Laboratory +// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences +// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip +// +// iEDA is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// +// See the Mulan PSL v2 for more details. +// *************************************************************************************** +#include "STAInterface.hpp" +#include "tcl_ista_util.hpp" +#include "tcl_sta.h" + +namespace tcl { + +TclReportTiming::TclReportTiming(const char* cmd_name) : TclCmd(cmd_name) +{ +} + +unsigned TclReportTiming::exec() +{ + if (!check()) { + return 0; + } + if (std::string error_message; !STAI.reportTiming(error_message)) { + setTclError(error_message); + return 0; + } + return 1; +} + +} // namespace tcl diff --git a/src/interface/tcl/tcl_ista/src/tcl_run_sta.cpp b/src/interface/tcl/tcl_ista/src/tcl_run_sta.cpp index 726b7c1ad..bc4ed8ab9 100644 --- a/src/interface/tcl/tcl_ista/src/tcl_run_sta.cpp +++ b/src/interface/tcl/tcl_ista/src/tcl_run_sta.cpp @@ -10,7 +10,7 @@ // // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, // EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // // See the Mulan PSL v2 for more details. // *************************************************************************************** diff --git a/src/interface/tcl/tcl_ista/src/tcl_set_propagated_clock.cpp b/src/interface/tcl/tcl_ista/src/tcl_set_propagated_clock.cpp new file mode 100644 index 000000000..377eb20e3 --- /dev/null +++ b/src/interface/tcl/tcl_ista/src/tcl_set_propagated_clock.cpp @@ -0,0 +1,42 @@ +// *************************************************************************************** +// Copyright (c) 2023-2025 Peng Cheng Laboratory +// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences +// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip +// +// iEDA is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// +// See the Mulan PSL v2 for more details. +// *************************************************************************************** +#include "STAInterface.hpp" +#include "tcl_ista_util.hpp" +#include "tcl_sta.h" + +namespace tcl { + +TclSetPropagatedClock::TclSetPropagatedClock(const char* cmd_name) : TclCmd(cmd_name) +{ + addOption(new TclStringListOption("clocks", 1)); +} + +unsigned TclSetPropagatedClock::exec() +{ + TclOption* clock_option = getOptionOrArg("clocks"); + if (!clock_option->is_set_val()) { + setTclError("set_propagated_clock requires a clock collection"); + return 0; + } + if (std::string error_message; !STAI.setPropagatedClock(clock_option->getStringList(), error_message)) { + setTclError(error_message); + return 0; + } + return 1; +} + +} // namespace tcl diff --git a/src/interface/tcl/tcl_ista/src/tcl_update_timing.cpp b/src/interface/tcl/tcl_ista/src/tcl_update_timing.cpp new file mode 100644 index 000000000..d58fd0f78 --- /dev/null +++ b/src/interface/tcl/tcl_ista/src/tcl_update_timing.cpp @@ -0,0 +1,39 @@ +// *************************************************************************************** +// Copyright (c) 2023-2025 Peng Cheng Laboratory +// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences +// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip +// +// iEDA is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// +// See the Mulan PSL v2 for more details. +// *************************************************************************************** +#include "STAInterface.hpp" +#include "tcl_ista_util.hpp" +#include "tcl_sta.h" + +namespace tcl { + +TclUpdateTiming::TclUpdateTiming(const char* cmd_name) : TclCmd(cmd_name) +{ +} + +unsigned TclUpdateTiming::exec() +{ + if (!check()) { + return 0; + } + if (std::string error_message; !STAI.updateTiming(error_message)) { + setTclError(error_message); + return 0; + } + return 1; +} + +} // namespace tcl diff --git a/src/interface/tcl/tcl_ista/src/tcl_write_sdf.cpp b/src/interface/tcl/tcl_ista/src/tcl_write_sdf.cpp new file mode 100644 index 000000000..828ea09e8 --- /dev/null +++ b/src/interface/tcl/tcl_ista/src/tcl_write_sdf.cpp @@ -0,0 +1,42 @@ +// *************************************************************************************** +// Copyright (c) 2023-2025 Peng Cheng Laboratory +// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences +// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip +// +// iEDA is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// +// See the Mulan PSL v2 for more details. +// *************************************************************************************** +#include "STAInterface.hpp" +#include "tcl_ista_util.hpp" +#include "tcl_sta.h" + +namespace tcl { + +TclWriteSDF::TclWriteSDF(const char* cmd_name) : TclCmd(cmd_name) +{ + addOption(new TclStringOption("file", 1)); +} + +unsigned TclWriteSDF::exec() +{ + TclOption* file_option = getOptionOrArg("file"); + if (!file_option->is_set_val()) { + setTclError("write_sdf requires an output file path"); + return 0; + } + if (std::string error_message; !STAI.writeSDF(file_option->getStringVal(), error_message)) { + setTclError(error_message); + return 0; + } + return 1; +} + +} // namespace tcl diff --git a/src/operation/iSTA/interface/STAInterface.cpp b/src/operation/iSTA/interface/STAInterface.cpp index d3f923755..e7698fe55 100644 --- a/src/operation/iSTA/interface/STAInterface.cpp +++ b/src/operation/iSTA/interface/STAInterface.cpp @@ -16,10 +16,14 @@ // *************************************************************************************** #include "STAInterface.hpp" +#include +#include + +#include "ClockPropagator.hpp" #include "DataManager.hpp" #include "DelayCalculator.hpp" -#include "ClockPropagator.hpp" #include "GraphBuilder.hpp" +#include "Lib.hh" #include "Logger.hpp" #include "Monitor.hpp" #include "PowerAnalyzer.hpp" @@ -28,14 +32,13 @@ #include "SDFWriter.hpp" #include "STAHeader.hpp" #include "TCModel.hpp" -#include "TimingCharacterizer.hpp" #include "TimingAnalyzer.hpp" +#include "TimingCharacterizer.hpp" #include "TimingPropagator.hpp" #include "TimingReporter.hpp" #include "Utility.hpp" #include "VcdParser.hh" #include "idm.h" -#include "Lib.hh" #include "spef/SpefParser.hh" namespace ista { @@ -84,6 +87,8 @@ void STAInterface::initSTA(std::map config_map) DataManager::initInst(); STADM.input(config_map); DelayCalculator::initInst(); + _is_initialized = true; + _is_timing_updated = false; STALOG.info(Loc::current(), "Completed", monitor.getStatsInfo()); } @@ -173,9 +178,12 @@ void STAInterface::destroySTA() Monitor monitor; STALOG.info(Loc::current(), "Starting..."); + STADC.destroy(); DelayCalculator::destroyInst(); STADM.output(); DataManager::destroyInst(); + _is_initialized = false; + _is_timing_updated = false; STALOG.info(Loc::current(), "Completed", monitor.getStatsInfo()); @@ -192,6 +200,224 @@ void STAInterface::destroySTA() Logger::destroyInst(); } +bool STAInterface::updateTiming(std::string& error_message) +{ + if (!isSTAInitialized(error_message)) { + return false; + } + if (_is_timing_updated) { + error_message = "update_timing may only be called once after init_sta"; + return false; + } + + STADC.init(); + + GraphBuilder::initInst(); + STAGB.build(); + GraphBuilder::destroyInst(); + + ClockPropagator::initInst(); + STACP.propagate(); + ClockPropagator::destroyInst(); + + TimingPropagator::initInst(); + STATP.propagate(); + TimingPropagator::destroyInst(); + + _is_timing_updated = true; + return true; +} + +bool STAInterface::writeSDF(const std::string& file_path, std::string& error_message) +{ + if (!isSTAInitialized(error_message)) { + return false; + } + if (!_is_timing_updated) { + error_message = "update_timing must be called before write_sdf"; + return false; + } + if (file_path.empty()) { + error_message = "write_sdf requires a non-empty output file path"; + return false; + } + + SDFWriter::initInst(); + STASW.write(file_path); + SDFWriter::destroyInst(); + return true; +} + +bool STAInterface::reportTiming(std::string& error_message) +{ + if (!isSTAInitialized(error_message)) { + return false; + } + if (!_is_timing_updated) { + error_message = "update_timing must be called before report_timing"; + return false; + } + + TimingAnalyzer::initInst(); + STATA.analyze(); + TimingAnalyzer::destroyInst(); + + TimingReporter::initInst(); + STATR.report(); + TimingReporter::destroyInst(); + + return true; +} + +bool STAInterface::createClock(const std::string& clock_name, const double period, const double rise_edge, const double fall_edge, + const std::vector& source_list, std::string& error_message) +{ + if (!isSTAInitialized(error_message)) { + return false; + } + if (clock_name.empty()) { + error_message = "create_clock requires a non-empty -name"; + return false; + } + if (!std::isfinite(period) || period <= 0.0) { + error_message = "create_clock -period must be a positive finite value"; + return false; + } + if (!std::isfinite(rise_edge) || !std::isfinite(fall_edge) || rise_edge < 0.0 || fall_edge < 0.0 || rise_edge >= fall_edge || fall_edge >= period) { + error_message = "create_clock -waveform must satisfy 0 <= rise < fall < period"; + return false; + } + if (source_list.empty()) { + error_message = "create_clock requires at least one source port"; + return false; + } + + Database& database = STADM.getDatabase(); + auto& clock_map = database.get_timing_constraint().get_clock_map(); + if (clock_map.contains(clock_name)) { + STALOG.warn(Loc::current(), "clock '", clock_name, "' already exists and will be overwritten"); + } + + std::vector unique_source_list; + std::set source_set; + for (const auto& source_name : source_list) { + auto pin_it = database.get_pin_map().find(source_name); + if (pin_it == database.get_pin_map().end()) { + STALOG.warn(Loc::current(), "source '", source_name, "' not found"); + error_message = "clock source is not a top-level port"; + return false; + } + if (!pin_it->second.get_is_port()) { + STALOG.warn(Loc::current(), "clock source '", source_name, "' found but is not a top-level port"); + error_message = "clock source is not a top-level port"; + return false; + } + if (source_set.insert(source_name).second) { + unique_source_list.push_back(source_name); + } + } + + TimingClock timing_clock; + timing_clock.set_clock_name(clock_name); + timing_clock.set_period(period); + timing_clock.set_rise_edge(rise_edge); + timing_clock.set_fall_edge(fall_edge); + timing_clock.set_source_list(unique_source_list); + timing_clock.set_is_propagated(false); + clock_map[clock_name] = timing_clock; + return true; +} + +bool STAInterface::setPropagatedClock(const std::vector& clock_name_list, std::string& error_message) +{ + if (!isSTAInitialized(error_message)) { + return false; + } + if (clock_name_list.empty()) { + error_message = "set_propagated_clock requires at least one clock"; + return false; + } + + auto& clock_map = STADM.getDatabase().get_timing_constraint().get_clock_map(); + for (const std::string& clock_name : clock_name_list) { + if (!clock_map.contains(clock_name)) { + STALOG.warn(Loc::current(), "clock '", clock_name, "' does not exist"); + error_message = "clock does not exist"; + return false; + } + } + for (const auto& clock_name : clock_name_list) { + clock_map[clock_name].set_is_propagated(true); + } + return true; +} + +bool STAInterface::getPorts(const std::vector& port_name_list, std::vector& resolved_port_list, std::string& error_message) +{ + if (!isSTAInitialized(error_message)) { + return false; + } + if (port_name_list.empty()) { + error_message = "get_ports requires at least one port name"; + return false; + } + + Database& database = STADM.getDatabase(); + resolved_port_list.clear(); + for (const std::string& port_name : port_name_list) { + auto pin_it = database.get_pin_map().find(port_name); + if (pin_it == database.get_pin_map().end()) { + STALOG.warn(Loc::current(), "source '", port_name, "' not found"); + error_message = "clock source not found"; + return false; + } + if (!pin_it->second.get_is_port()) { + STALOG.warn(Loc::current(), "clock source '", port_name, "' found but is not a top-level port"); + error_message = "clock source is not a top-level port"; + return false; + } + if (auto pin_it = database.get_pin_map().find(port_name); pin_it == database.get_pin_map().end() || !pin_it->second.get_is_port()) { + STALOG.warn(Loc::current(), "port '", port_name, "' does not exist"); + error_message = "port does not exist"; + return false; + } + resolved_port_list.push_back(port_name); + } + return true; +} + +bool STAInterface::getClocks(const std::vector& clock_name_list, std::vector& resolved_clock_list, std::string& error_message) +{ + if (!isSTAInitialized(error_message)) { + return false; + } + if (clock_name_list.empty()) { + error_message = "get_clocks requires at least one clock name"; + return false; + } + + std::map& clock_map = STADM.getDatabase().get_timing_constraint().get_clock_map(); + resolved_clock_list.clear(); + for (const std::string& clock_name : clock_name_list) { + if (!clock_map.contains(clock_name)) { + STALOG.error(Loc::current(), "clock '", clock_name, "' does not exist"); + error_message = "clock does not exist"; + return false; + } + resolved_clock_list.push_back(clock_name); + } + return true; +} + +bool STAInterface::isSTAInitialized(std::string& error_message) const +{ + if (_is_initialized) { + return true; + } + error_message = "init_sta must be called before STA commands"; + return false; +} + #endif #endif @@ -218,8 +444,8 @@ void STAInterface::wrapConfig(std::map& config_map) STADM.getConfig().timing_path_limit = STAUTIL.getConfigValue(config_map, "-timing_path_limit", 20); STADM.getConfig().timing_corner = STAUTIL.getConfigValue(config_map, "-timing_corner", ""); STADM.getConfig().is_path_report_number_specified = STAUTIL.exist(config_map, std::string("-max_paths")) - || STAUTIL.exist(config_map, std::string("-max_path")) - || STAUTIL.exist(config_map, std::string("-path_report_number")); + || STAUTIL.exist(config_map, std::string("-max_path")) + || STAUTIL.exist(config_map, std::string("-path_report_number")); STADM.getConfig().path_report_number = STAUTIL.getConfigValue(config_map, "-max_paths", 1); if (STAUTIL.exist(config_map, std::string("-max_path"))) { STADM.getConfig().path_report_number = std::any_cast(config_map["-max_path"]); @@ -683,7 +909,6 @@ void STAInterface::wrapTimingCellPort(TimingCell& timing_cell, idb::LibPort* lib timing_cell.get_port_map()[timing_cell_port.get_port_name()] = timing_cell_port; } - void STAInterface::wrapTimingCellPower(TimingCell& timing_cell, idb::LibCell* lib_cell) { idb::LibLibrary* lib_library = lib_cell->get_owner_lib(); @@ -723,8 +948,7 @@ TimingPowerArc STAInterface::wrapTimingPowerArc(idb::LibPowerArc* lib_power_arc) return timing_power_arc; } -TimingPowerArc STAInterface::wrapTimingPortPowerArc(idb::LibInternalPowerInfo* internal_power_info, std::string& port_name, - idb::LibLibrary* lib_library) +TimingPowerArc STAInterface::wrapTimingPortPowerArc(idb::LibInternalPowerInfo* internal_power_info, std::string& port_name, idb::LibLibrary* lib_library) { TimingPowerArc timing_power_arc; timing_power_arc.set_sink_port(port_name); @@ -1024,8 +1248,7 @@ TimingTableVariableType STAInterface::wrapTimingTableVariableType(idb::LibTable* if (*variable == idb::LibLutTableTemplate::Variable::CONSTRAINED_PIN_TRANSITION) { return TimingTableVariableType::kConstrainedTransition; } - if (*variable == idb::LibLutTableTemplate::Variable::INPUT_NET_TRANSITION - || *variable == idb::LibLutTableTemplate::Variable::RELATED_PIN_TRANSITION + if (*variable == idb::LibLutTableTemplate::Variable::INPUT_NET_TRANSITION || *variable == idb::LibLutTableTemplate::Variable::RELATED_PIN_TRANSITION || *variable == idb::LibLutTableTemplate::Variable::INPUT_TRANSITION_TIME) { return TimingTableVariableType::kInputTransition; } @@ -1447,8 +1670,7 @@ std::unique_ptr STAInterface::makeLibArc(std::string& source_port, std::unique_ptr STAInterface::makeLibScalarTable(int32_t table_type, TCScalarTable& tc_scalar_table) { - std::unique_ptr lib_table - = std::make_unique(static_cast(table_type), nullptr); + std::unique_ptr lib_table = std::make_unique(static_cast(table_type), nullptr); lib_table->addTableValue(std::make_unique(tc_scalar_table.get_value())); return lib_table; } diff --git a/src/operation/iSTA/interface/STAInterface.hpp b/src/operation/iSTA/interface/STAInterface.hpp index 716ef7285..37db6e2cf 100644 --- a/src/operation/iSTA/interface/STAInterface.hpp +++ b/src/operation/iSTA/interface/STAInterface.hpp @@ -104,6 +104,16 @@ class STAInterface void runSTA(); void extractLib(); void destroySTA(); + bool updateTiming(std::string& error_message); + bool writeSDF(const std::string& file_path, std::string& error_message); + bool reportTiming(std::string& error_message); + bool createClock(const std::string& clock_name, double period, double rise_edge, double fall_edge, + const std::vector& source_list, std::string& error_message); + bool setPropagatedClock(const std::vector& clock_name_list, std::string& error_message); + bool getPorts(const std::vector& port_name_list, std::vector& resolved_port_list, + std::string& error_message); + bool getClocks(const std::vector& clock_name_list, std::vector& resolved_clock_list, + std::string& error_message); #endif #endif @@ -158,7 +168,7 @@ class STAInterface TimingLeakagePower wrapTimingLeakagePower(idb::LibLeakagePower* lib_leakage_power); LogicExpression wrapLogicExpression(std::string& expression_string); void wrapLogicExpressionTermList(LogicExpression& logic_expression, LibertyExpr* liberty_expr); - LogicOperationType wrapLogicOperationType(const int32_t liberty_expr_op); + LogicOperationType wrapLogicOperationType(int32_t liberty_expr_op); void wrapTimingCellArc(TimingCell& timing_cell, idb::LibArcSet* lib_arc_set); bool isSDFDelayArc(idb::LibArc* lib_arc); bool isSDFCheckArc(idb::LibArc* lib_arc); @@ -214,6 +224,8 @@ class STAInterface private: static STAInterface* _sta_interface_instance; + bool _is_initialized = false; + bool _is_timing_updated = false; STAInterface() = default; STAInterface(const STAInterface& other) = delete; @@ -222,6 +234,7 @@ class STAInterface STAInterface& operator=(const STAInterface& other) = delete; STAInterface& operator=(STAInterface&& other) = delete; // function + bool isSTAInitialized(std::string& error_message) const; }; } // namespace ista diff --git a/src/operation/iSTA/source/data_manager/advance/TimingPathState.hpp b/src/operation/iSTA/source/data_manager/advance/TimingPathState.hpp index 6ef422303..dd71a32be 100644 --- a/src/operation/iSTA/source/data_manager/advance/TimingPathState.hpp +++ b/src/operation/iSTA/source/data_manager/advance/TimingPathState.hpp @@ -43,10 +43,10 @@ class TimingPathState void set_arrival(const double arrival) { _arrival = arrival; } void set_slew(const double slew) { _slew = slew; } void set_launch_time(const double launch_time) { _launch_time = launch_time; } - void set_start_point(const std::string& start_point) { _start_point = start_point; } - void set_predecessor(const std::string& predecessor) { _predecessor = predecessor; } - void set_clock_name(const std::string& clock_name) { _clock_name = clock_name; } - void set_crpr_clock_pin(const std::string& crpr_clock_pin) { _crpr_clock_pin = crpr_clock_pin; } + void set_start_point(const std::string_view start_point) { _start_point = start_point; } + void set_predecessor(const std::string_view predecessor) { _predecessor = predecessor; } + void set_clock_name(const std::string_view clock_name) { _clock_name = clock_name; } + void set_crpr_clock_pin(const std::string_view crpr_clock_pin) { _crpr_clock_pin = crpr_clock_pin; } void set_predecessor_arc_idx(const std::size_t predecessor_arc_idx) { _predecessor_arc_idx = predecessor_arc_idx; } void set_predecessor_arc_delay(const double predecessor_arc_delay) { _predecessor_arc_delay = predecessor_arc_delay; } void set_trans_type(const TransType& trans_type) { _trans_type = trans_type; } diff --git a/src/operation/iSTA/source/data_manager/advance/TimingPoint.hpp b/src/operation/iSTA/source/data_manager/advance/TimingPoint.hpp index f30b3d5f0..0a0f621bf 100644 --- a/src/operation/iSTA/source/data_manager/advance/TimingPoint.hpp +++ b/src/operation/iSTA/source/data_manager/advance/TimingPoint.hpp @@ -22,6 +22,10 @@ #include "TimingPathState.hpp" #include "TransType.hpp" +#include +#include +#include + namespace ista { class TimingPoint @@ -37,12 +41,24 @@ class TimingPoint int32_t get_level() const { return _level; } std::string& get_predecessor() { return _predecessor; } std::string& get_clock_name() { return _clock_name; } + [[nodiscard]] std::string_view get_clock_name() const { return _clock_name; } std::size_t get_predecessor_arc_idx() const { return _predecessor_arc_idx; } std::map>& get_clock_arrival_map() { return _clock_arrival_map; } std::map>& get_clock_slew_map() { return _clock_slew_map; } + std::map>& get_physical_clock_arrival_map() { return _physical_clock_arrival_map; } + std::map>& get_physical_clock_slew_map() { return _physical_clock_slew_map; } std::map>& get_clock_predecessor_map() { return _clock_predecessor_map; } std::map>& get_clock_predecessor_arc_delay_map() { return _clock_predecessor_arc_delay_map; } std::map>& get_clock_predecessor_trans_type_map() { return _clock_predecessor_trans_type_map; } + std::map>& get_physical_clock_predecessor_map() { return _physical_clock_predecessor_map; } + std::map>& get_physical_clock_predecessor_arc_delay_map() + { + return _physical_clock_predecessor_arc_delay_map; + } + std::map>& get_physical_clock_predecessor_trans_type_map() + { + return _physical_clock_predecessor_trans_type_map; + } std::map>& get_data_slew_map() { return _data_slew_map; } std::map>>>& get_path_state_map() { @@ -55,11 +71,19 @@ class TimingPoint void set_slack(const double slack) { _slack = slack; } void set_launch_time(const double launch_time) { _launch_time = launch_time; } void set_level(const int32_t level) { _level = level; } - void set_predecessor(const std::string& predecessor) { _predecessor = predecessor; } - void set_clock_name(const std::string& clock_name) { _clock_name = clock_name; } + void set_predecessor(std::string_view predecessor) { _predecessor = predecessor; } + void set_clock_name(std::string_view clock_name) { _clock_name = clock_name; } void set_predecessor_arc_idx(const std::size_t predecessor_arc_idx) { _predecessor_arc_idx = predecessor_arc_idx; } void set_clock_arrival_map(const std::map>& clock_arrival_map) { _clock_arrival_map = clock_arrival_map; } void set_clock_slew_map(const std::map>& clock_slew_map) { _clock_slew_map = clock_slew_map; } + void set_physical_clock_arrival_map(const std::map>& clock_arrival_map) + { + _physical_clock_arrival_map = clock_arrival_map; + } + void set_physical_clock_slew_map(const std::map>& clock_slew_map) + { + _physical_clock_slew_map = clock_slew_map; + } void set_clock_predecessor_map(const std::map>& clock_predecessor_map) { _clock_predecessor_map = clock_predecessor_map; @@ -72,6 +96,19 @@ class TimingPoint { _clock_predecessor_trans_type_map = clock_predecessor_trans_type_map; } + void set_physical_clock_predecessor_map(const std::map>& clock_predecessor_map) + { + _physical_clock_predecessor_map = clock_predecessor_map; + } + void set_physical_clock_predecessor_arc_delay_map(const std::map>& clock_predecessor_arc_delay_map) + { + _physical_clock_predecessor_arc_delay_map = clock_predecessor_arc_delay_map; + } + void set_physical_clock_predecessor_trans_type_map( + const std::map>& clock_predecessor_trans_type_map) + { + _physical_clock_predecessor_trans_type_map = clock_predecessor_trans_type_map; + } void set_data_slew_map(const std::map>& data_slew_map) { _data_slew_map = data_slew_map; } void set_path_state_map( const std::map>>>& path_state_map) @@ -90,11 +127,17 @@ class TimingPoint std::string _predecessor; std::string _clock_name; std::size_t _predecessor_arc_idx = std::numeric_limits::max(); + // Effective state drives timing analysis; physical state retains the full clock-tree calculation. std::map> _clock_arrival_map; std::map> _clock_slew_map; + std::map> _physical_clock_arrival_map; + std::map> _physical_clock_slew_map; std::map> _clock_predecessor_map; std::map> _clock_predecessor_arc_delay_map; std::map> _clock_predecessor_trans_type_map; + std::map> _physical_clock_predecessor_map; + std::map> _physical_clock_predecessor_arc_delay_map; + std::map> _physical_clock_predecessor_trans_type_map; std::map> _data_slew_map; std::map>>> _path_state_map; bool _is_clock_point = false; diff --git a/src/operation/iSTA/source/module/clock_propagator/ClockPropagator.cpp b/src/operation/iSTA/source/module/clock_propagator/ClockPropagator.cpp index 8d4ea228b..45795c573 100644 --- a/src/operation/iSTA/source/module/clock_propagator/ClockPropagator.cpp +++ b/src/operation/iSTA/source/module/clock_propagator/ClockPropagator.cpp @@ -66,7 +66,6 @@ void ClockPropagator::propagate() ClockPropagator* ClockPropagator::_cp_instance = nullptr; - bool ClockPropagator::isDisableArc(Arc& arc) { return arc.get_is_disable_arc() || arc.get_is_loop_disable(); @@ -81,10 +80,8 @@ CPModel ClockPropagator::initCPModel() void ClockPropagator::buildClockSourceList(CPModel& cp_model) { Database& database = STADM.getDatabase(); - for (std::pair& clock_pair : database.get_timing_constraint().get_clock_map()) { - for (std::string& clock_source : clock_pair.second.get_source_list()) { - cp_model.get_clock_source_list().push_back(clock_source); - } + for (auto& [name, clock] : database.get_timing_constraint().get_clock_map()) { + cp_model.clock_list.emplace_back(name, clock.get_source_list(), clock.get_is_propagated()); } } @@ -99,11 +96,17 @@ void ClockPropagator::initTimingPointList() timing_pair.second.get_predecessor().clear(); timing_pair.second.get_clock_name().clear(); timing_pair.second.get_clock_arrival_map().clear(); + timing_pair.second.get_clock_slew_map().clear(); + timing_pair.second.get_physical_clock_arrival_map().clear(); + timing_pair.second.get_physical_clock_slew_map().clear(); timing_pair.second.get_path_state_map().clear(); timing_pair.second.get_data_slew_map().clear(); timing_pair.second.get_clock_predecessor_map().clear(); timing_pair.second.get_clock_predecessor_arc_delay_map().clear(); timing_pair.second.get_clock_predecessor_trans_type_map().clear(); + timing_pair.second.get_physical_clock_predecessor_map().clear(); + timing_pair.second.get_physical_clock_predecessor_arc_delay_map().clear(); + timing_pair.second.get_physical_clock_predecessor_trans_type_map().clear(); timing_pair.second.set_predecessor_arc_idx(std::numeric_limits::max()); timing_pair.second.set_is_clock_point(false); } @@ -111,39 +114,47 @@ void ClockPropagator::initTimingPointList() void ClockPropagator::markClockPointList(CPModel& cp_model) { - for (std::string& clock_source : cp_model.get_clock_source_list()) { - markClockPoint(clock_source); + for (auto& clock : cp_model.clock_list) { + markClockPoint(clock); } } -void ClockPropagator::markClockPoint(std::string& clock_source) +void ClockPropagator::markClockPoint(CPClock& clock) { Database& database = STADM.getDatabase(); - if (database.get_timing_point_map().count(clock_source) == 0) { - return; - } - std::queue pin_queue; - database.get_timing_point_map()[clock_source].set_is_clock_point(true); - pin_queue.push(clock_source); + for (const auto& clock_source : clock.get_source_list()) { + if (!database.get_timing_point_map().contains(clock_source)) { + STALOG.warn(Loc::current(), "clock '", clock.get_clock_name(), "' has no source"); + continue; + } + pin_queue.push(clock_source); + } while (!pin_queue.empty()) { std::string pin_name = pin_queue.front(); pin_queue.pop(); + TimingPoint& timing_point = database.get_timing_point_map()[pin_name]; + if (is_clock_tree_overlap(timing_point, clock)) { + STALOG.error(Loc::current(), "clock trees overlap at pin '", pin_name, "': '", timing_point.get_clock_name(), "' and '", clock.get_clock_name(), "'"); + } + if (timing_point.get_clock_name() == clock.get_clock_name()) { + continue; + } + + timing_point.set_is_clock_point(true); + timing_point.set_clock_name(clock.get_clock_name()); + clock.add_clock_pin(pin_name); + if (shouldStopClockPropagation(pin_name)) { continue; } - for (std::size_t arc_idx : database.get_outgoing_arc_list_map()[pin_name]) { + for (const auto& arc_idx : database.get_outgoing_arc_list_map()[pin_name]) { Arc& arc = database.get_arc_list()[arc_idx]; if (isDisableArc(arc)) { continue; } - TimingPoint& sink_point = database.get_timing_point_map()[arc.get_sink_pin()]; - if (sink_point.get_is_clock_point()) { - continue; - } - sink_point.set_is_clock_point(true); pin_queue.push(arc.get_sink_pin()); } } @@ -152,81 +163,104 @@ void ClockPropagator::markClockPoint(std::string& clock_source) void ClockPropagator::propagateClockArrival(CPModel& cp_model) { Database& database = STADM.getDatabase(); - for (std::string& clock_source : cp_model.get_clock_source_list()) { - seedClockArrival(clock_source); + for (CPClock& clock : cp_model.clock_list) { + seedPhysicalClockState(clock); + propagateClockSlewDelay(clock); + for (std::string& pin_name : database.get_timing_order_list()) { + if (TimingPoint& timing_point = database.get_timing_point_map()[pin_name]; + !timing_point.get_is_clock_point() || timing_point.get_clock_name() != clock.get_clock_name() || shouldStopClockPropagation(pin_name)) { + continue; + } + for (const auto& arc_idx : database.get_outgoing_arc_list_map()[pin_name]) { + if (isDisableArc(database.get_arc_list()[arc_idx])) { + continue; + } + propagateClockArrivalArc(clock, arc_idx, AnalysisType::kMax); + propagateClockArrivalArc(clock, arc_idx, AnalysisType::kMin); + } + } + updateEffectiveClockState(clock); } - propagateClockSlewDelay(); +} - for (std::string& pin_name : database.get_timing_order_list()) { - TimingPoint& timing_point = database.get_timing_point_map()[pin_name]; - if (!timing_point.get_is_clock_point()) { - continue; - } - if (shouldStopClockPropagation(pin_name)) { +void ClockPropagator::seedPhysicalClockState(CPClock& clock) +{ + Database& database = STADM.getDatabase(); + for (const auto& pin_name : clock.get_source_list()) { + if (!database.get_timing_point_map().contains(pin_name)) { continue; } - for (std::size_t arc_idx : database.get_outgoing_arc_list_map()[pin_name]) { - if (isDisableArc(database.get_arc_list()[arc_idx])) { - continue; - } - propagateClockArrivalArc(arc_idx, AnalysisType::kMax); - propagateClockArrivalArc(arc_idx, AnalysisType::kMin); - } + TimingPoint& timing_point = database.get_timing_point_map()[pin_name]; + timing_point.get_physical_clock_arrival_map()[AnalysisType::kMax][TransType::kRise] = 0.0; + timing_point.get_physical_clock_arrival_map()[AnalysisType::kMax][TransType::kFall] = 0.0; + timing_point.get_physical_clock_arrival_map()[AnalysisType::kMin][TransType::kRise] = 0.0; + timing_point.get_physical_clock_arrival_map()[AnalysisType::kMin][TransType::kFall] = 0.0; + timing_point.get_physical_clock_slew_map()[AnalysisType::kMax][TransType::kRise] = 0.0; + timing_point.get_physical_clock_slew_map()[AnalysisType::kMax][TransType::kFall] = 0.0; + timing_point.get_physical_clock_slew_map()[AnalysisType::kMin][TransType::kRise] = 0.0; + timing_point.get_physical_clock_slew_map()[AnalysisType::kMin][TransType::kFall] = 0.0; } } -void ClockPropagator::seedClockArrival(std::string& clock_source) +void ClockPropagator::updateEffectiveClockState(CPClock& clock) { Database& database = STADM.getDatabase(); - if (database.get_timing_point_map().count(clock_source) == 0) { - return; + for (const auto& pin_name : clock.get_clock_point_list()) { + TimingPoint& timing_point = database.get_timing_point_map()[pin_name]; + if (clock.get_is_propagated()) { + timing_point.set_clock_arrival_map(timing_point.get_physical_clock_arrival_map()); + timing_point.set_clock_slew_map(timing_point.get_physical_clock_slew_map()); + timing_point.set_clock_predecessor_map(timing_point.get_physical_clock_predecessor_map()); + timing_point.set_clock_predecessor_arc_delay_map(timing_point.get_physical_clock_predecessor_arc_delay_map()); + timing_point.set_clock_predecessor_trans_type_map(timing_point.get_physical_clock_predecessor_trans_type_map()); + continue; + } + + for (AnalysisType analysis_type : {AnalysisType::kMax, AnalysisType::kMin}) { + for (TransType trans_type : {TransType::kRise, TransType::kFall}) { + timing_point.get_clock_arrival_map()[analysis_type][trans_type] = 0.0; + timing_point.get_clock_slew_map()[analysis_type][trans_type] = 0.0; + } + } } - TimingPoint& timing_point = database.get_timing_point_map()[clock_source]; - timing_point.get_clock_arrival_map()[AnalysisType::kMax][TransType::kRise] = 0.0; - timing_point.get_clock_arrival_map()[AnalysisType::kMax][TransType::kFall] = 0.0; - timing_point.get_clock_arrival_map()[AnalysisType::kMin][TransType::kRise] = 0.0; - timing_point.get_clock_arrival_map()[AnalysisType::kMin][TransType::kFall] = 0.0; - timing_point.get_clock_slew_map()[AnalysisType::kMax][TransType::kRise] = 0.0; - timing_point.get_clock_slew_map()[AnalysisType::kMax][TransType::kFall] = 0.0; - timing_point.get_clock_slew_map()[AnalysisType::kMin][TransType::kRise] = 0.0; - timing_point.get_clock_slew_map()[AnalysisType::kMin][TransType::kFall] = 0.0; } -void ClockPropagator::propagateClockSlewDelay() +void ClockPropagator::propagateClockSlewDelay(CPClock& clock) { Database& database = STADM.getDatabase(); for (std::string& pin_name : database.get_timing_order_list()) { TimingPoint& timing_point = database.get_timing_point_map()[pin_name]; - if (!timing_point.get_is_clock_point() || shouldStopClockPropagation(pin_name)) { + if (!timing_point.get_is_clock_point() || timing_point.get_clock_name() != clock.get_clock_name() || shouldStopClockPropagation(pin_name)) { continue; } for (std::size_t arc_idx : database.get_outgoing_arc_list_map()[pin_name]) { if (isDisableArc(database.get_arc_list()[arc_idx])) { continue; } - propagateClockSlewDelayArc(arc_idx, AnalysisType::kMax); - propagateClockSlewDelayArc(arc_idx, AnalysisType::kMin); + propagateClockSlewDelayArc(clock, arc_idx, AnalysisType::kMax); + propagateClockSlewDelayArc(clock, arc_idx, AnalysisType::kMin); } } } -void ClockPropagator::propagateClockSlewDelayArc(std::size_t arc_idx, AnalysisType analysis_type) +void ClockPropagator::propagateClockSlewDelayArc(CPClock& clock, std::size_t arc_idx, AnalysisType analysis_type) { - propagateClockSlewDelayArc(arc_idx, analysis_type, TransType::kRise); - propagateClockSlewDelayArc(arc_idx, analysis_type, TransType::kFall); + propagateClockSlewDelayArc(clock, arc_idx, analysis_type, TransType::kRise); + propagateClockSlewDelayArc(clock, arc_idx, analysis_type, TransType::kFall); } -void ClockPropagator::propagateClockSlewDelayArc(std::size_t arc_idx, AnalysisType analysis_type, TransType input_trans_type) +void ClockPropagator::propagateClockSlewDelayArc(CPClock& clock, std::size_t arc_idx, AnalysisType analysis_type, TransType input_trans_type) { Database& database = STADM.getDatabase(); Arc& arc = database.get_arc_list()[arc_idx]; TimingPoint& source_point = database.get_timing_point_map()[arc.get_source_pin()]; TimingPoint& sink_point = database.get_timing_point_map()[arc.get_sink_pin()]; - if (!source_point.get_is_clock_point() || !sink_point.get_is_clock_point()) { + if (!source_point.get_is_clock_point() || !sink_point.get_is_clock_point() || source_point.get_clock_name() != clock.get_clock_name() + || sink_point.get_clock_name() != clock.get_clock_name()) { return; } - if (source_point.get_clock_slew_map().count(analysis_type) == 0 - || source_point.get_clock_slew_map()[analysis_type].count(input_trans_type) == 0) { + if (source_point.get_physical_clock_slew_map().count(analysis_type) == 0 + || source_point.get_physical_clock_slew_map()[analysis_type].count(input_trans_type) == 0) { return; } for (TransType output_trans_type : getOutputTransTypeList(arc, analysis_type, input_trans_type)) { @@ -234,10 +268,10 @@ void ClockPropagator::propagateClockSlewDelayArc(std::size_t arc_idx, AnalysisTy } } -void ClockPropagator::updateClockSlewDelay(Arc& arc, TimingPoint& source_point, TimingPoint& sink_point, AnalysisType analysis_type, - TransType input_trans_type, TransType output_trans_type) +void ClockPropagator::updateClockSlewDelay(Arc& arc, TimingPoint& source_point, TimingPoint& sink_point, AnalysisType analysis_type, TransType input_trans_type, + TransType output_trans_type) { - double input_slew = source_point.get_clock_slew_map()[analysis_type][input_trans_type]; + double input_slew = source_point.get_physical_clock_slew_map()[analysis_type][input_trans_type]; DCTask dc_task; dc_task.set_proc_type(DCProcType::kCalculate); dc_task.set_arc(&arc); @@ -252,20 +286,20 @@ void ClockPropagator::updateClockSlewDelay(Arc& arc, TimingPoint& source_point, double arc_delay = dc_task.get_timing_result().get_delay(); double output_slew = dc_task.get_timing_result().get_slew(); updateGraphArcDelay(arc, analysis_type, input_trans_type, output_trans_type, arc_delay); - if (sink_point.get_clock_slew_map().count(analysis_type) == 0 - || sink_point.get_clock_slew_map()[analysis_type].count(output_trans_type) == 0 - || isBetterSlew(output_slew, sink_point.get_clock_slew_map()[analysis_type][output_trans_type], analysis_type)) { - sink_point.get_clock_slew_map()[analysis_type][output_trans_type] = output_slew; + if (sink_point.get_physical_clock_slew_map().count(analysis_type) == 0 + || sink_point.get_physical_clock_slew_map()[analysis_type].count(output_trans_type) == 0 + || isBetterSlew(output_slew, sink_point.get_physical_clock_slew_map()[analysis_type][output_trans_type], analysis_type)) { + sink_point.get_physical_clock_slew_map()[analysis_type][output_trans_type] = output_slew; } } -void ClockPropagator::propagateClockArrivalArc(std::size_t arc_idx, AnalysisType analysis_type) +void ClockPropagator::propagateClockArrivalArc(CPClock& clock, std::size_t arc_idx, AnalysisType analysis_type) { - propagateClockArrivalArc(arc_idx, analysis_type, TransType::kRise); - propagateClockArrivalArc(arc_idx, analysis_type, TransType::kFall); + propagateClockArrivalArc(clock, arc_idx, analysis_type, TransType::kRise); + propagateClockArrivalArc(clock, arc_idx, analysis_type, TransType::kFall); } -void ClockPropagator::propagateClockArrivalArc(std::size_t arc_idx, AnalysisType analysis_type, TransType input_trans_type) +void ClockPropagator::propagateClockArrivalArc(CPClock& clock, std::size_t arc_idx, AnalysisType analysis_type, TransType input_trans_type) { Database& database = STADM.getDatabase(); Arc& arc = database.get_arc_list()[arc_idx]; @@ -274,13 +308,15 @@ void ClockPropagator::propagateClockArrivalArc(std::size_t arc_idx, AnalysisType } TimingPoint& source_point = database.get_timing_point_map()[arc.get_source_pin()]; TimingPoint& sink_point = database.get_timing_point_map()[arc.get_sink_pin()]; - if (!source_point.get_is_clock_point() || !sink_point.get_is_clock_point()) { + if (!source_point.get_is_clock_point() || !sink_point.get_is_clock_point() || source_point.get_clock_name() != clock.get_clock_name() + || sink_point.get_clock_name() != clock.get_clock_name()) { return; } if (!hasClockArrival(source_point, analysis_type, input_trans_type)) { return; } - if (source_point.get_clock_slew_map().count(analysis_type) == 0 || source_point.get_clock_slew_map()[analysis_type].count(input_trans_type) == 0) { + if (source_point.get_physical_clock_slew_map().count(analysis_type) == 0 + || source_point.get_physical_clock_slew_map()[analysis_type].count(input_trans_type) == 0) { return; } @@ -289,8 +325,8 @@ void ClockPropagator::propagateClockArrivalArc(std::size_t arc_idx, AnalysisType } } -void ClockPropagator::updateClockPathState(Arc& arc, TimingPoint& source_point, TimingPoint& sink_point, AnalysisType analysis_type, - TransType input_trans_type, TransType output_trans_type) +void ClockPropagator::updateClockPathState(Arc& arc, TimingPoint& source_point, TimingPoint& sink_point, AnalysisType analysis_type, TransType input_trans_type, + TransType output_trans_type) { double arc_delay = getArcDelay(arc, analysis_type, input_trans_type, output_trans_type); double candidate_arrival = roundTime(getClockArrival(source_point, analysis_type, input_trans_type) + arc_delay); @@ -303,7 +339,8 @@ void ClockPropagator::updateClockPathState(Arc& arc, TimingPoint& source_point, bool ClockPropagator::hasClockArrival(TimingPoint& timing_point, AnalysisType analysis_type, TransType trans_type) { - return timing_point.get_clock_arrival_map().count(analysis_type) > 0 && timing_point.get_clock_arrival_map()[analysis_type].count(trans_type) > 0; + return timing_point.get_physical_clock_arrival_map().count(analysis_type) > 0 + && timing_point.get_physical_clock_arrival_map()[analysis_type].count(trans_type) > 0; } double ClockPropagator::getClockArrival(TimingPoint& timing_point, AnalysisType analysis_type, TransType trans_type) @@ -311,27 +348,27 @@ double ClockPropagator::getClockArrival(TimingPoint& timing_point, AnalysisType if (!hasClockArrival(timing_point, analysis_type, trans_type)) { return 0.0; } - return timing_point.get_clock_arrival_map()[analysis_type][trans_type]; + return timing_point.get_physical_clock_arrival_map()[analysis_type][trans_type]; } void ClockPropagator::updateClockArrival(TimingPoint& timing_point, AnalysisType analysis_type, TransType trans_type, double clock_arrival) { - timing_point.get_clock_arrival_map()[analysis_type][trans_type] = clock_arrival; + timing_point.get_physical_clock_arrival_map()[analysis_type][trans_type] = clock_arrival; } void ClockPropagator::updateClockPredecessor(TimingPoint& timing_point, AnalysisType analysis_type, TransType trans_type, TransType predecessor_trans_type, - Arc& arc, double arc_delay) + Arc& arc, double arc_delay) { - timing_point.get_clock_predecessor_map()[analysis_type][trans_type] = arc.get_source_pin(); - timing_point.get_clock_predecessor_arc_delay_map()[analysis_type][trans_type] = arc_delay; - timing_point.get_clock_predecessor_trans_type_map()[analysis_type][trans_type] = predecessor_trans_type; + timing_point.get_physical_clock_predecessor_map()[analysis_type][trans_type] = arc.get_source_pin(); + timing_point.get_physical_clock_predecessor_arc_delay_map()[analysis_type][trans_type] = arc_delay; + timing_point.get_physical_clock_predecessor_trans_type_map()[analysis_type][trans_type] = predecessor_trans_type; } bool ClockPropagator::shouldStopClockPropagation(std::string& pin_name) { Database& database = STADM.getDatabase(); Pin& pin = database.get_pin_map()[pin_name]; - if (pin.get_is_port() || database.get_instance_map().count(pin.get_instance_name()) == 0) { + if (pin.get_is_port() || !database.get_instance_map().contains(pin.get_instance_name())) { return false; } Instance& instance = database.get_instance_map()[pin.get_instance_name()]; diff --git a/src/operation/iSTA/source/module/clock_propagator/ClockPropagator.hpp b/src/operation/iSTA/source/module/clock_propagator/ClockPropagator.hpp index a186ccdf0..b0e6f8f6c 100644 --- a/src/operation/iSTA/source/module/clock_propagator/ClockPropagator.hpp +++ b/src/operation/iSTA/source/module/clock_propagator/ClockPropagator.hpp @@ -47,23 +47,24 @@ class ClockPropagator void buildClockSourceList(CPModel& cp_model); void initTimingPointList(); void markClockPointList(CPModel& cp_model); - void markClockPoint(std::string& clock_source); + void markClockPoint(CPClock& clock); void propagateClockArrival(CPModel& cp_model); - void seedClockArrival(std::string& clock_source); - void propagateClockSlewDelay(); - void propagateClockSlewDelayArc(std::size_t arc_idx, AnalysisType analysis_type); - void propagateClockSlewDelayArc(std::size_t arc_idx, AnalysisType analysis_type, TransType input_trans_type); - void updateClockSlewDelay(Arc& arc, TimingPoint& source_point, TimingPoint& sink_point, AnalysisType analysis_type, - TransType input_trans_type, TransType output_trans_type); - void propagateClockArrivalArc(std::size_t arc_idx, AnalysisType analysis_type); - void propagateClockArrivalArc(std::size_t arc_idx, AnalysisType analysis_type, TransType input_trans_type); - void updateClockPathState(Arc& arc, TimingPoint& source_point, TimingPoint& sink_point, AnalysisType analysis_type, - TransType input_trans_type, TransType output_trans_type); + void seedPhysicalClockState(CPClock& clock); + void updateEffectiveClockState(CPClock& clock); + void propagateClockSlewDelay(CPClock& clock); + void propagateClockSlewDelayArc(CPClock& clock, std::size_t arc_idx, AnalysisType analysis_type); + void propagateClockSlewDelayArc(CPClock& clock, std::size_t arc_idx, AnalysisType analysis_type, TransType input_trans_type); + void updateClockSlewDelay(Arc& arc, TimingPoint& source_point, TimingPoint& sink_point, AnalysisType analysis_type, TransType input_trans_type, + TransType output_trans_type); + void propagateClockArrivalArc(CPClock& clock, std::size_t arc_idx, AnalysisType analysis_type); + void propagateClockArrivalArc(CPClock& clock, std::size_t arc_idx, AnalysisType analysis_type, TransType input_trans_type); + void updateClockPathState(Arc& arc, TimingPoint& source_point, TimingPoint& sink_point, AnalysisType analysis_type, TransType input_trans_type, + TransType output_trans_type); bool hasClockArrival(TimingPoint& timing_point, AnalysisType analysis_type, TransType trans_type); double getClockArrival(TimingPoint& timing_point, AnalysisType analysis_type, TransType trans_type); void updateClockArrival(TimingPoint& timing_point, AnalysisType analysis_type, TransType trans_type, double clock_arrival); - void updateClockPredecessor(TimingPoint& timing_point, AnalysisType analysis_type, TransType trans_type, TransType predecessor_trans_type, - Arc& arc, double arc_delay); + void updateClockPredecessor(TimingPoint& timing_point, AnalysisType analysis_type, TransType trans_type, TransType predecessor_trans_type, Arc& arc, + double arc_delay); bool shouldStopClockPropagation(std::string& pin_name); void updateGraphArcDelay(Arc& arc, AnalysisType analysis_type, TransType input_trans_type, TransType output_trans_type, double arc_delay); bool isBetterDelay(double candidate_delay, double current_delay, AnalysisType analysis_type); @@ -74,6 +75,14 @@ class ClockPropagator double getArcDelay(Arc& arc, AnalysisType analysis_type, TransType input_trans_type, TransType output_trans_type); bool isBetterArrival(double candidate_arrival, double current_arrival, AnalysisType analysis_type); bool isFinite(double value); + static bool is_clock_tree_overlap(const TimingPoint& timing_point, const CPClock& clock) + { + if (timing_point.get_clock_name().empty()) + return false; + if (timing_point.get_clock_name() == clock.get_clock_name()) + return false; + return true; + } }; } // namespace ista diff --git a/src/operation/iSTA/source/module/clock_propagator/cp_data_manager/CPModel.hpp b/src/operation/iSTA/source/module/clock_propagator/cp_data_manager/CPModel.hpp index f7efe1cb3..135b8876b 100644 --- a/src/operation/iSTA/source/module/clock_propagator/cp_data_manager/CPModel.hpp +++ b/src/operation/iSTA/source/module/clock_propagator/cp_data_manager/CPModel.hpp @@ -17,21 +17,42 @@ #include "STAHeader.hpp" +#include +#include +#include + namespace ista { -class CPModel +class CPClock { public: - CPModel() = default; - ~CPModel() = default; - // getter - std::vector& get_clock_source_list() { return _clock_source_list; } - // setter - void set_clock_source_list(const std::vector& clock_source_list) { _clock_source_list = clock_source_list; } - // function + CPClock() = default; + ~CPClock() = default; + CPClock(std::string_view clock_name, const std::vector& source_list, bool is_propagated) + : _clock_name(clock_name), _source_list(source_list), _is_propagated(is_propagated) + { + } + + [[nodiscard]] std::string_view get_clock_name() const { return _clock_name; } + [[nodiscard]] const std::vector& get_source_list() const { return _source_list; } + [[nodiscard]] bool get_is_propagated() const { return _is_propagated; } + [[nodiscard]] const std::vector& get_clock_point_list() const { return _clock_point_list; } + + + void set_clock_name(const std::string& clock_name) { _clock_name = clock_name; } + void set_is_propagated(bool is_propagated) { _is_propagated = is_propagated; } + void add_clock_pin(std::string_view clock_name) { _clock_point_list.emplace_back(clock_name); } private: - std::vector _clock_source_list; + std::string _clock_name; + const std::vector& _source_list; + std::vector _clock_point_list; + bool _is_propagated = false; +}; + +struct CPModel +{ + std::vector clock_list; }; } // namespace ista diff --git a/src/operation/iSTA/source/module/sdf_writer/SDFWriter.cpp b/src/operation/iSTA/source/module/sdf_writer/SDFWriter.cpp index a148e545e..c91e18445 100644 --- a/src/operation/iSTA/source/module/sdf_writer/SDFWriter.cpp +++ b/src/operation/iSTA/source/module/sdf_writer/SDFWriter.cpp @@ -50,11 +50,16 @@ void SDFWriter::destroyInst() // function void SDFWriter::write() +{ + write(getSDFFilePath()); +} + +void SDFWriter::write(const std::string_view file_path) { Monitor monitor; STALOG.info(Loc::current(), "Starting..."); - outputSDF(); + outputSDF(file_path); STALOG.info(Loc::current(), "Completed", monitor.getStatsInfo()); } @@ -63,10 +68,9 @@ void SDFWriter::write() SDFWriter* SDFWriter::_sw_instance = nullptr; -void SDFWriter::outputSDF() +void SDFWriter::outputSDF(const std::string_view file_path) { - std::string sdf_file_path = getSDFFilePath(); - std::ofstream* sdf_file = STAUTIL.getOutputFileStream(sdf_file_path); + std::ofstream* sdf_file = STAUTIL.getOutputFileStream(file_path); outputSDFHeader(sdf_file); outputSDFInterconnect(sdf_file); outputSDFCellList(sdf_file); diff --git a/src/operation/iSTA/source/module/sdf_writer/SDFWriter.hpp b/src/operation/iSTA/source/module/sdf_writer/SDFWriter.hpp index afeb78fbe..f82f2dd0d 100644 --- a/src/operation/iSTA/source/module/sdf_writer/SDFWriter.hpp +++ b/src/operation/iSTA/source/module/sdf_writer/SDFWriter.hpp @@ -33,6 +33,7 @@ class SDFWriter static void destroyInst(); // function void write(); + void write(std::string_view file_path); private: struct SDFCellArcKey @@ -56,7 +57,7 @@ class SDFWriter SDFWriter& operator=(const SDFWriter& other) = delete; SDFWriter& operator=(SDFWriter&& other) = delete; // function - void outputSDF(); + void outputSDF(const std::string_view file_path); std::string getSDFFilePath(); void outputSDFHeader(std::ofstream* sdf_file); void outputSDFInterconnect(std::ofstream* sdf_file); diff --git a/src/operation/iSTA/source/module/timing_analyzer/TimingAnalyzer.cpp b/src/operation/iSTA/source/module/timing_analyzer/TimingAnalyzer.cpp index e9f5448c3..80fbbd3d6 100644 --- a/src/operation/iSTA/source/module/timing_analyzer/TimingAnalyzer.cpp +++ b/src/operation/iSTA/source/module/timing_analyzer/TimingAnalyzer.cpp @@ -347,7 +347,7 @@ double TimingAnalyzer::getEndPointRequired(std::string& start_point, std::string Instance& instance = database.get_instance_map()[pin.get_instance_name()]; TimingCheckArc* timing_check_arc = getEndPointCheckArc(end_point, analysis_type); if (instance.get_is_sequential() && timing_check_arc != nullptr && isMatchCheckTransType(*timing_check_arc, data_trans_type)) { - std::string clock_name = getClockName(end_point); + const auto& clock_name = getClockName(end_point); double check_time = getEndPointCheckTime(end_point, *timing_check_arc, analysis_type, data_trans_type, data_slew); std::string common_pin_name; double cppr = getClockReconvergencePessimism(start_point, end_point, analysis_type, common_pin_name); @@ -359,7 +359,7 @@ double TimingAnalyzer::getEndPointRequired(std::string& start_point, std::string return default_required_time; } -std::string TimingAnalyzer::getClockName(std::string& pin_name) +std::string_view TimingAnalyzer::getClockName(std::string& pin_name) { Database& database = STADM.getDatabase(); TimingClock* timing_clock = getStartPointClock(pin_name); @@ -367,6 +367,21 @@ std::string TimingAnalyzer::getClockName(std::string& pin_name) return timing_clock->get_clock_name(); } Pin& pin = database.get_pin_map()[pin_name]; + if (!pin.get_is_port() && database.get_instance_map().count(pin.get_instance_name()) > 0) { + Instance& instance = database.get_instance_map()[pin.get_instance_name()]; + if (instance.get_is_sequential() && database.get_timing_point_map().count(instance.get_clock_pin_name()) > 0) { + const auto& propagated_clock_name = database.get_timing_point_map()[instance.get_clock_pin_name()].get_clock_name(); + if (!propagated_clock_name.empty()) { + return propagated_clock_name; + } + } + } + if (database.get_timing_point_map().count(pin_name) > 0) { + TimingPoint& timing_point = database.get_timing_point_map()[pin_name]; + if (timing_point.get_is_clock_point() && !timing_point.get_clock_name().empty()) { + return timing_point.get_clock_name(); + } + } std::map& clock_map = database.get_timing_constraint().get_clock_map(); if (pin.get_is_port()) { std::map& port_constraint_map = database.get_timing_constraint().get_port_constraint_map(); @@ -481,7 +496,7 @@ double TimingAnalyzer::getEndPointCaptureTime(std::string& end_point, AnalysisTy TimingCheckArc* timing_check_arc = getEndPointCheckArc(end_point, analysis_type); TransType clock_trans_type = timing_check_arc == nullptr ? TransType::kRise : getClockTransType(*timing_check_arc); if (analysis_type == AnalysisType::kMax) { - std::string clock_name = getClockName(end_point); + const auto& clock_name = getClockName(end_point); return getClockPeriod(clock_name) + getEndPointClockArrival(end_point, capture_analysis_type, clock_trans_type); } return getEndPointClockArrival(end_point, capture_analysis_type, clock_trans_type); @@ -872,12 +887,12 @@ bool TimingAnalyzer::isMatchCheckType(TimingCheckArc& timing_check_arc, Analysis return timing_check_arc.get_check_type() == TimingCheckType::kSetup || timing_check_arc.get_check_type() == TimingCheckType::kRecovery; } -double TimingAnalyzer::getClockPeriod(std::string& clock_name) +double TimingAnalyzer::getClockPeriod(std::string_view clock_name) { Database& database = STADM.getDatabase(); std::map& clock_map = database.get_timing_constraint().get_clock_map(); - if (clock_map.count(clock_name) > 0) { - return clock_map[clock_name].get_period(); + if (const std::string _name{clock_name}; clock_map.contains(_name)) { + return clock_map[_name].get_period(); } if (!clock_map.empty()) { return clock_map.begin()->second.get_period(); diff --git a/src/operation/iSTA/source/module/timing_analyzer/TimingAnalyzer.hpp b/src/operation/iSTA/source/module/timing_analyzer/TimingAnalyzer.hpp index 03b48e9ca..68bba047c 100644 --- a/src/operation/iSTA/source/module/timing_analyzer/TimingAnalyzer.hpp +++ b/src/operation/iSTA/source/module/timing_analyzer/TimingAnalyzer.hpp @@ -73,7 +73,7 @@ class TimingAnalyzer double data_slew); double getEndPointRequired(std::string& start_point, std::string& end_point, double default_required_time, AnalysisType analysis_type, TransType data_trans_type, double data_slew); - std::string getClockName(std::string& pin_name); + std::string_view getClockName(std::string& pin_name); TimingClock* getStartPointClock(std::string& start_point); double getEndPointRequired(TimingPathState& end_path_state, std::string& end_point, double default_required_time, AnalysisType analysis_type); @@ -111,7 +111,7 @@ class TimingAnalyzer double getClockCommonPathArrival(std::pair& common_pin, AnalysisType analysis_type); TimingCheckArc* getEndPointCheckArc(std::string& end_point, AnalysisType analysis_type); bool isMatchCheckType(TimingCheckArc& timing_check_arc, AnalysisType analysis_type); - double getClockPeriod(std::string& clock_name); + double getClockPeriod(std::string_view clock_name); void propagateRequiredArc(Arc& arc); void updateSlack(); void analyzeEndPointList(TAModel& ta_model); diff --git a/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.cpp b/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.cpp index bee0ccdaa..db4653ca4 100644 --- a/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.cpp +++ b/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.cpp @@ -90,6 +90,14 @@ bool TimingPropagator::shouldStopDataPropagation(Arc& arc) return arc.get_type() == ArcType::kNet && isSequentialClockPin(arc.get_sink_pin()); } +bool TimingPropagator::shouldStopDataSlewPropagation(Arc& arc) +{ + if (arc.get_type() != ArcType::kNet || !isSequentialClockPin(arc.get_sink_pin())) { + return false; + } + return STADM.getDatabase().get_timing_point_map()[arc.get_sink_pin()].get_is_clock_point(); +} + bool TimingPropagator::isSequentialClockPin(std::string& pin_name) { Database& database = STADM.getDatabase(); @@ -232,8 +240,9 @@ void TimingPropagator::propagateDataSlewDelayArc(std::size_t arc_idx, AnalysisTy if (isDisableArc(arc)) { return; } - // Slew must reach a sequential CK for C2Q delay and timing checks. Arrival - // and path-state propagation still stop at this boundary. + if (shouldStopDataSlewPropagation(arc)) { + return; + } TimingPoint& source_point = database.get_timing_point_map()[arc.get_source_pin()]; if (!hasDataSlew(source_point, analysis_type, input_trans_type)) { return; @@ -505,7 +514,7 @@ TransType TimingPropagator::getClockTransType(TimingCellArc& timing_cell_arc) return TransType::kRise; } -std::string TimingPropagator::getClockName(std::string& pin_name) +std::string_view TimingPropagator::getClockName(std::string& pin_name) { Database& database = STADM.getDatabase(); TimingClock* timing_clock = getStartPointClock(pin_name); @@ -513,6 +522,21 @@ std::string TimingPropagator::getClockName(std::string& pin_name) return timing_clock->get_clock_name(); } Pin& pin = database.get_pin_map()[pin_name]; + if (!pin.get_is_port() && database.get_instance_map().count(pin.get_instance_name()) > 0) { + Instance& instance = database.get_instance_map()[pin.get_instance_name()]; + if (instance.get_is_sequential() && database.get_timing_point_map().count(instance.get_clock_pin_name()) > 0) { + const auto& propagated_clock_name = database.get_timing_point_map()[instance.get_clock_pin_name()].get_clock_name(); + if (!propagated_clock_name.empty()) { + return propagated_clock_name; + } + } + } + if (database.get_timing_point_map().count(pin_name) > 0) { + TimingPoint& timing_point = database.get_timing_point_map()[pin_name]; + if (timing_point.get_is_clock_point() && !timing_point.get_clock_name().empty()) { + return timing_point.get_clock_name(); + } + } std::map& clock_map = database.get_timing_constraint().get_clock_map(); if (pin.get_is_port()) { std::map& port_constraint_map = database.get_timing_constraint().get_port_constraint_map(); diff --git a/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.hpp b/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.hpp index 0354ee169..7c807d923 100644 --- a/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.hpp +++ b/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.hpp @@ -44,6 +44,7 @@ class TimingPropagator // function bool isDisableArc(Arc& arc); bool shouldStopDataPropagation(Arc& arc); + bool shouldStopDataSlewPropagation(Arc& arc); bool isSequentialClockPin(std::string& pin_name); bool hasIncomingPhysicalSlewArc(std::string& pin_name); TPModel initTPModel(); @@ -79,7 +80,7 @@ class TimingPropagator std::string getStartPointCrprClockPin(std::string& start_point); TransType getStartPointCrprClockTransType(std::string& start_point); TransType getClockTransType(TimingCellArc& timing_cell_arc); - std::string getClockName(std::string& pin_name); + std::string_view getClockName(std::string& pin_name); std::string getPathStateStartPoint(std::string& start_point); void seedPathState(std::string& start_point, AnalysisType analysis_type); PathSourceType getStartPointSourceType(std::string& start_point, AnalysisType analysis_type); From 753448cf1e5834784233e42089243364e85b7c25 Mon Sep 17 00:00:00 2001 From: Wenz-jam Date: Fri, 7 Aug 2026 10:52:42 +0800 Subject: [PATCH 9/9] fix(ista): bound timing path state by launch clock (cherry picked from commit 9379e80628e04f0025d00a54c17bbd114908476c) --- .../data_manager/advance/TimingPoint.hpp | 1 + .../module/timing_analyzer/TimingAnalyzer.cpp | 13 ++-- .../timing_propagator/TimingPropagator.cpp | 64 +++++++++---------- .../timing_propagator/TimingPropagator.hpp | 2 +- 4 files changed, 39 insertions(+), 41 deletions(-) diff --git a/src/operation/iSTA/source/data_manager/advance/TimingPoint.hpp b/src/operation/iSTA/source/data_manager/advance/TimingPoint.hpp index 0a0f621bf..f6e8b8cb1 100644 --- a/src/operation/iSTA/source/data_manager/advance/TimingPoint.hpp +++ b/src/operation/iSTA/source/data_manager/advance/TimingPoint.hpp @@ -60,6 +60,7 @@ class TimingPoint return _physical_clock_predecessor_trans_type_map; } std::map>& get_data_slew_map() { return _data_slew_map; } + // The innermost key is the launch clock name, not the individual startpoint. std::map>>>& get_path_state_map() { return _path_state_map; diff --git a/src/operation/iSTA/source/module/timing_analyzer/TimingAnalyzer.cpp b/src/operation/iSTA/source/module/timing_analyzer/TimingAnalyzer.cpp index 80fbbd3d6..79e2a4a92 100644 --- a/src/operation/iSTA/source/module/timing_analyzer/TimingAnalyzer.cpp +++ b/src/operation/iSTA/source/module/timing_analyzer/TimingAnalyzer.cpp @@ -190,9 +190,10 @@ std::map& TimingAnalyzer::getPathStateMap(TimingPo } TimingPathState& TimingAnalyzer::getPathState(TimingPoint& timing_point, AnalysisType analysis_type, PathSourceType source_type, TransType trans_type, - std::string& start_point) + std::string& start_point) { - return timing_point.get_path_state_map()[analysis_type][source_type][trans_type][start_point]; + std::string path_state_tag{getClockName(start_point)}; + return timing_point.get_path_state_map()[analysis_type][source_type][trans_type][path_state_tag]; } TimingPathState* TimingAnalyzer::getWorstPathState(TimingPoint& timing_point, AnalysisType analysis_type, PathSourceType source_type) @@ -1133,15 +1134,15 @@ bool TimingAnalyzer::updateDiversionPathState(std::string& pin_name, AnalysisTyp { Database& database = STADM.getDatabase(); TimingPoint& timing_point = database.get_timing_point_map()[pin_name]; - std::string& start_point = source_path_state.get_start_point(); + const std::string& path_state_tag = source_path_state.get_clock_name(); std::map& path_state_map = getPathStateMap(timing_point, analysis_type, source_type, trans_type); - if (path_state_map.count(start_point) > 0 && !isBetterArrival(arrival, path_state_map[start_point].get_arrival(), analysis_type)) { + if (path_state_map.count(path_state_tag) > 0 && !isBetterArrival(arrival, path_state_map[path_state_tag].get_arrival(), analysis_type)) { return false; } - TimingPathState& path_state = path_state_map[start_point]; + TimingPathState& path_state = path_state_map[path_state_tag]; path_state.set_arrival(arrival); path_state.set_slew(getDataSlew(timing_point, analysis_type, trans_type)); - path_state.set_start_point(start_point); + path_state.set_start_point(source_path_state.get_start_point()); path_state.set_predecessor(predecessor); path_state.set_predecessor_arc_idx(predecessor_arc_idx); path_state.set_predecessor_arc_delay(predecessor_arc_delay); diff --git a/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.cpp b/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.cpp index db4653ca4..a2eccd9a3 100644 --- a/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.cpp +++ b/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.cpp @@ -572,34 +572,29 @@ void TimingPropagator::seedPathState(std::string& start_point, AnalysisType anal return; } std::string path_state_start_point = getPathStateStartPoint(start_point); - TimingPathState& rise_path_state - = database.get_timing_point_map()[start_point].get_path_state_map()[analysis_type][source_type][TransType::kRise][path_state_start_point]; - rise_path_state.set_arrival(getStartPointArrival(start_point, analysis_type, TransType::kRise)); - rise_path_state.set_slew(getStartPointSlew(start_point, analysis_type, TransType::kRise)); - rise_path_state.set_launch_time(getStartPointLaunchTime(start_point, analysis_type, TransType::kRise)); - rise_path_state.set_start_point(path_state_start_point); - rise_path_state.set_clock_name(getClockName(start_point)); - rise_path_state.set_crpr_clock_pin(getStartPointCrprClockPin(start_point)); - rise_path_state.get_predecessor().clear(); - rise_path_state.set_predecessor_arc_idx(std::numeric_limits::max()); - rise_path_state.set_predecessor_arc_delay(0.0); - rise_path_state.set_trans_type(TransType::kRise); - rise_path_state.set_predecessor_trans_type(TransType::kNone); - rise_path_state.set_crpr_clock_trans_type(getStartPointCrprClockTransType(start_point)); - TimingPathState& fall_path_state - = database.get_timing_point_map()[start_point].get_path_state_map()[analysis_type][source_type][TransType::kFall][path_state_start_point]; - fall_path_state.set_arrival(getStartPointArrival(start_point, analysis_type, TransType::kFall)); - fall_path_state.set_slew(getStartPointSlew(start_point, analysis_type, TransType::kFall)); - fall_path_state.set_launch_time(getStartPointLaunchTime(start_point, analysis_type, TransType::kFall)); - fall_path_state.set_start_point(path_state_start_point); - fall_path_state.set_clock_name(getClockName(start_point)); - fall_path_state.set_crpr_clock_pin(getStartPointCrprClockPin(start_point)); - fall_path_state.get_predecessor().clear(); - fall_path_state.set_predecessor_arc_idx(std::numeric_limits::max()); - fall_path_state.set_predecessor_arc_delay(0.0); - fall_path_state.set_trans_type(TransType::kFall); - fall_path_state.set_predecessor_trans_type(TransType::kNone); - fall_path_state.set_crpr_clock_trans_type(getStartPointCrprClockTransType(start_point)); + std::string path_state_tag{getClockName(start_point)}; + TimingPoint& timing_point = database.get_timing_point_map()[start_point]; + for (TransType trans_type : {TransType::kRise, TransType::kFall}) { + double arrival = getStartPointArrival(start_point, analysis_type, trans_type); + std::map& path_state_map = getPathStateMap(timing_point, analysis_type, source_type, trans_type); + if (path_state_map.count(path_state_tag) > 0 && !isBetterArrival(arrival, path_state_map[path_state_tag].get_arrival(), analysis_type)) { + continue; + } + + TimingPathState& path_state = path_state_map[path_state_tag]; + path_state.set_arrival(arrival); + path_state.set_slew(getStartPointSlew(start_point, analysis_type, trans_type)); + path_state.set_launch_time(getStartPointLaunchTime(start_point, analysis_type, trans_type)); + path_state.set_start_point(path_state_start_point); + path_state.set_clock_name(path_state_tag); + path_state.set_crpr_clock_pin(getStartPointCrprClockPin(start_point)); + path_state.get_predecessor().clear(); + path_state.set_predecessor_arc_idx(std::numeric_limits::max()); + path_state.set_predecessor_arc_delay(0.0); + path_state.set_trans_type(trans_type); + path_state.set_predecessor_trans_type(TransType::kNone); + path_state.set_crpr_clock_trans_type(getStartPointCrprClockTransType(start_point)); + } } PathSourceType TimingPropagator::getStartPointSourceType(std::string& start_point, AnalysisType analysis_type) @@ -721,13 +716,14 @@ void TimingPropagator::propagatePathStateArc(std::size_t arc_idx, AnalysisType a } double arc_delay = getArcDelay(arc, analysis_type, input_trans_type, output_trans_type); double candidate_arrival = roundTime(source_path_state.get_arrival() + arc_delay); - std::string& start_point = source_path_state.get_start_point(); + const std::string& path_state_tag = source_path_state.get_clock_name(); std::map& sink_path_state_map = getPathStateMap(sink_point, analysis_type, source_type, output_trans_type); - if (sink_path_state_map.count(start_point) == 0 || isBetterArrival(candidate_arrival, sink_path_state_map[start_point].get_arrival(), analysis_type)) { - TimingPathState& sink_path_state = sink_path_state_map[start_point]; + if (sink_path_state_map.count(path_state_tag) == 0 + || isBetterArrival(candidate_arrival, sink_path_state_map[path_state_tag].get_arrival(), analysis_type)) { + TimingPathState& sink_path_state = sink_path_state_map[path_state_tag]; sink_path_state.set_arrival(candidate_arrival); sink_path_state.set_slew(getDataSlew(sink_point, analysis_type, output_trans_type)); - sink_path_state.set_start_point(start_point); + sink_path_state.set_start_point(source_path_state.get_start_point()); sink_path_state.set_predecessor(arc.get_source_pin()); sink_path_state.set_predecessor_arc_idx(arc_idx); sink_path_state.set_predecessor_arc_delay(arc_delay); @@ -797,9 +793,9 @@ std::map& TimingPropagator::getPathStateMap(Timing } TimingPathState& TimingPropagator::getPathState(TimingPoint& timing_point, AnalysisType analysis_type, PathSourceType source_type, TransType trans_type, - std::string& start_point) + std::string& path_state_tag) { - return timing_point.get_path_state_map()[analysis_type][source_type][trans_type][start_point]; + return timing_point.get_path_state_map()[analysis_type][source_type][trans_type][path_state_tag]; } TimingPathState* TimingPropagator::getWorstPathState(TimingPoint& timing_point, AnalysisType analysis_type, PathSourceType source_type) diff --git a/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.hpp b/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.hpp index 7c807d923..c516f7c22 100644 --- a/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.hpp +++ b/src/operation/iSTA/source/module/timing_propagator/TimingPropagator.hpp @@ -101,7 +101,7 @@ class TimingPropagator std::map& getPathStateMap(TimingPoint& timing_point, AnalysisType analysis_type, PathSourceType source_type, TransType trans_type); TimingPathState& getPathState(TimingPoint& timing_point, AnalysisType analysis_type, PathSourceType source_type, TransType trans_type, - std::string& start_point); + std::string& path_state_tag); TimingPathState* getWorstPathState(TimingPoint& timing_point, AnalysisType analysis_type, PathSourceType source_type); TimingPathState* getWorstPathState(TimingPoint& timing_point, AnalysisType analysis_type, PathSourceType source_type, TransType trans_type); TransType getEndPointTransType(TimingPoint& timing_point, AnalysisType analysis_type, PathSourceType source_type);