diff --git a/src/operation/iSTA/interface/CMakeLists.txt b/src/operation/iSTA/interface/CMakeLists.txt index 8c9dd3dd48..a3ee896d73 100644 --- a/src/operation/iSTA/interface/CMakeLists.txt +++ b/src/operation/iSTA/interface/CMakeLists.txt @@ -5,6 +5,7 @@ add_library(ista_interface target_link_libraries(ista_interface PRIVATE ista_source + ista_sdc_command idm liberty spef diff --git a/src/operation/iSTA/interface/STAInterface.cpp b/src/operation/iSTA/interface/STAInterface.cpp index 6ed6a065fb..6b3d9cc0ff 100644 --- a/src/operation/iSTA/interface/STAInterface.cpp +++ b/src/operation/iSTA/interface/STAInterface.cpp @@ -31,6 +31,8 @@ #include "PowerPropagator.hpp" #include "PowerReporter.hpp" #include "SDFWriter.hpp" +#include "SdcCommand.hpp" +#include "SdcCommands.hpp" #include "STAHeader.hpp" #include "TCModel.hpp" #include "TimingAnalyzer.hpp" @@ -88,6 +90,21 @@ void STAInterface::initSTA(std::map config_map) DataManager::initInst(); STADM.input(config_map); DelayCalculator::initInst(); + SdcCommand::initInst({ + {"set_case_analysis", sdc::executeTclCommand}, + {"set_input_delay", sdc::executeTclCommand}, + {"set_output_delay", sdc::executeTclCommand}, + {"set_input_transition", sdc::executeTclCommand}, + {"set_load", sdc::executeTclCommand}, + {"set_clock_uncertainty", sdc::executeTclCommand}, + {"get_clock", sdc::executeTclCommand}, + {"get_clocks", sdc::executeTclCommand}, + {"get_port", sdc::executeTclCommand}, + {"get_ports", sdc::executeTclCommand}, + {"create_clock", sdc::executeTclCommand}, + {"set_propagated_clock", sdc::executeTclCommand}, + }); + STADM.readConstraint(); STALOG.info(Loc::current(), "Completed", monitor.getStatsInfo()); } @@ -180,6 +197,7 @@ void STAInterface::destroySTA() STADC.destroy(); DelayCalculator::destroyInst(); STADM.output(); + SdcCommand::destroyInst(); DataManager::destroyInst(); STALOG.info(Loc::current(), "Completed", monitor.getStatsInfo()); diff --git a/src/operation/iSTA/source/data_manager/CMakeLists.txt b/src/operation/iSTA/source/data_manager/CMakeLists.txt index 62f59fdab7..b0cc8041fb 100644 --- a/src/operation/iSTA/source/data_manager/CMakeLists.txt +++ b/src/operation/iSTA/source/data_manager/CMakeLists.txt @@ -5,6 +5,7 @@ add_library(ista_data_manager target_link_libraries(ista_data_manager PUBLIC ista_toolkit + ista_sdc_command ) target_include_directories(ista_data_manager @@ -12,4 +13,5 @@ target_include_directories(ista_data_manager ${ISTA_DATA_MANAGER}/advance ${ISTA_DATA_MANAGER}/basic ${ISTA_DATA_MANAGER} + ${ISTA_MODULE}/sdc_command/sdc_commands ) diff --git a/src/operation/iSTA/source/data_manager/DataManager.cpp b/src/operation/iSTA/source/data_manager/DataManager.cpp index 265fcae59a..e81b736c4e 100644 --- a/src/operation/iSTA/source/data_manager/DataManager.cpp +++ b/src/operation/iSTA/source/data_manager/DataManager.cpp @@ -18,6 +18,7 @@ #include "Logger.hpp" #include "Monitor.hpp" +#include "SdcCommand.hpp" #include "STAInterface.hpp" #include "Utility.hpp" @@ -148,7 +149,6 @@ void DataManager::buildDatabase() buildInstanceList(); buildNetList(); buildInstanceTimingInfo(); - readConstraint(); } void DataManager::buildInstanceList() @@ -313,666 +313,14 @@ void DataManager::readConstraint() return; } - std::vector> command_list = readCommandList(sdc_file_path); - for (std::vector& token_list : command_list) { - parseCommand(token_list); - } -} - -std::vector> DataManager::readCommandList(std::string& sdc_file_path) -{ - std::ifstream sdc_file(sdc_file_path); - std::string content; - std::string line; - while (std::getline(sdc_file, line)) { - std::string command_line = removeComment(line); - bool is_continue = !command_line.empty() && command_line.back() == '\\'; - if (is_continue) { - command_line.pop_back(); - } - content += command_line; - content += is_continue ? " " : "\n"; - } - - std::vector token_list = tokenizeSdc(content); - std::vector> command_list; - std::vector command_token_list; - for (std::string& token : token_list) { - if (token == "\n" || token == ";") { - if (!command_token_list.empty()) { - command_list.push_back(command_token_list); - command_token_list.clear(); - } - } else { - command_token_list.push_back(token); - } - } - if (!command_token_list.empty()) { - command_list.push_back(command_token_list); - } - return resolveCommandList(command_list); -} - -std::vector> DataManager::resolveCommandList(std::vector>& command_list) -{ - std::map variable_map; - std::vector> resolved_command_list; - for (std::vector& token_list : command_list) { - if (token_list.empty()) { - continue; - } - if (token_list.front() == "set") { - updateVariableMap(token_list, variable_map); - continue; - } - std::vector resolved_token_list = resolveCommandTokenList(token_list, variable_map); - if (!resolved_token_list.empty()) { - resolved_command_list.push_back(resolved_token_list); - } - } - return resolved_command_list; -} - -std::vector DataManager::resolveCommandTokenList(std::vector& token_list, std::map& variable_map) -{ - std::vector resolved_token_list; - for (std::size_t i = 0; i < token_list.size(); i++) { - if (token_list[i].empty()) { - continue; - } - if (token_list[i].front() == '[') { - resolved_token_list.push_back(resolveBracketCommand(token_list, i, variable_map)); - continue; - } - resolved_token_list.push_back(resolveVariableToken(token_list[i], variable_map)); - } - return resolved_token_list; -} - -void DataManager::updateVariableMap(std::vector& token_list, std::map& variable_map) -{ - if (token_list.size() < 3) { - return; - } - std::string variable_name = token_list[1]; - std::string variable_value; - if (!token_list[2].empty() && token_list[2].front() == '[') { - std::size_t token_idx = 2; - variable_value = resolveBracketCommand(token_list, token_idx, variable_map); - } else { - variable_value = getTokenListString(token_list, 2); - variable_value = resolveVariableToken(variable_value, variable_map); - } - variable_map[variable_name] = variable_value; -} - -std::string DataManager::resolveBracketCommand(std::vector& token_list, std::size_t& token_idx, std::map& variable_map) -{ - std::vector bracket_token_list = getBracketTokenList(token_list, token_idx, variable_map); - if (bracket_token_list.empty()) { - return ""; - } - if (bracket_token_list.front() == "expr") { - return evalExpr(bracket_token_list); - } - if (bracket_token_list.front() == "get_ports" || bracket_token_list.front() == "get_pins" || bracket_token_list.front() == "get_clocks") { - return getTokenListString(bracket_token_list, 1); - } - return getTokenListString(bracket_token_list, 0); -} - -std::vector DataManager::getBracketTokenList(std::vector& token_list, std::size_t& token_idx, - std::map& variable_map) -{ - std::vector bracket_token_list; - for (std::size_t i = token_idx; i < token_list.size(); i++) { - std::string token = token_list[i]; - std::size_t left_bracket_num = std::count(token.begin(), token.end(), '['); - std::size_t right_bracket_num = std::count(token.begin(), token.end(), ']'); - bool is_end = right_bracket_num > left_bracket_num; - if (i == token_idx && !token.empty() && token.front() == '[') { - token.erase(token.begin()); - } - if (is_end) { - token.pop_back(); - } - if (!token.empty()) { - bracket_token_list.push_back(resolveVariableToken(token, variable_map)); - } - token_idx = i; - if (is_end) { - break; - } - } - return bracket_token_list; -} - -std::string DataManager::evalExpr(std::vector& expr_token_list) -{ - return getExprValueString(calcExprValue(expr_token_list)); -} - -double DataManager::calcExprValue(std::vector& expr_token_list) -{ - std::vector value_list; - std::vector operator_list; - for (std::size_t i = 1; i < expr_token_list.size(); i++) { - if (isExprOperator(expr_token_list[i])) { - operator_list.push_back(expr_token_list[i]); - } else { - value_list.push_back(std::stod(expr_token_list[i])); - } - } - calcExprMulDiv(value_list, operator_list); - if (value_list.empty()) { - return 0.0; - } - double result = value_list.front(); - for (std::size_t i = 0; i < operator_list.size() && i + 1 < value_list.size(); i++) { - if (operator_list[i] == "+") { - result += value_list[i + 1]; - } else if (operator_list[i] == "-") { - result -= value_list[i + 1]; - } - } - return result; -} - -void DataManager::calcExprMulDiv(std::vector& value_list, std::vector& operator_list) -{ - for (std::size_t i = 0; i < operator_list.size() && i + 1 < value_list.size();) { - if (operator_list[i] == "*" || operator_list[i] == "/") { - if (operator_list[i] == "*") { - value_list[i] *= value_list[i + 1]; - } else { - value_list[i] /= value_list[i + 1]; - } - value_list.erase(value_list.begin() + i + 1); - operator_list.erase(operator_list.begin() + i); - continue; - } - i++; - } -} - -std::string DataManager::getExprValueString(const double value) -{ - std::ostringstream oss; - oss << std::setprecision(15) << value; - return oss.str(); -} - -bool DataManager::isExprOperator(std::string& token) -{ - return token == "+" || token == "-" || token == "*" || token == "/"; -} - -std::string DataManager::resolveVariableToken(std::string token, std::map& variable_map) -{ - if (token.empty()) { - return token; - } - std::string resolved_token; - for (std::size_t i = 0; i < token.size(); i++) { - if (token[i] != '$') { - resolved_token.push_back(token[i]); - continue; - } - std::string variable_name; - i++; - while (i < token.size() && (std::isalnum(static_cast(token[i])) || token[i] == '_')) { - variable_name.push_back(token[i]); - i++; - } - i--; - if (variable_map.count(variable_name) > 0) { - resolved_token += variable_map[variable_name]; - } else { - resolved_token += "$" + variable_name; + auto& sdc_command{Singleton::getInst()}; + if (sdc_command.evalScriptFile(sdc_file_path) != TCL_OK) { + for (const SdcError& error : sdc_command.getErrors()) { + STALOG.warn(Loc::current(), "SDC command failed in '", sdc_file_path, "' at line ", error.line_number, ": ", error.message); } } - return resolved_token; } -std::string DataManager::getTokenListString(std::vector& token_list, std::size_t begin_idx) -{ - std::string token_list_string; - for (std::size_t i = begin_idx; i < token_list.size(); i++) { - if (!token_list_string.empty()) { - token_list_string += " "; - } - token_list_string += token_list[i]; - } - return token_list_string; -} - -std::vector DataManager::tokenizeSdc(std::string& content) -{ - std::vector token_list; - std::string token; - bool in_brace = false; - bool in_quote = false; - for (char ch : content) { - if (in_brace) { - if (ch == '}') { - token_list.push_back(token); - token.clear(); - in_brace = false; - } else { - token.push_back(ch); - } - continue; - } - if (in_quote) { - if (ch == '"') { - token_list.push_back(token); - token.clear(); - in_quote = false; - } else { - token.push_back(ch); - } - continue; - } - if (ch == '{') { - if (!token.empty()) { - token_list.push_back(token); - token.clear(); - } - in_brace = true; - } else if (ch == '"') { - if (!token.empty()) { - token_list.push_back(token); - token.clear(); - } - in_quote = true; - } else if (std::isspace(static_cast(ch)) || ch == ';') { - if (!token.empty()) { - token_list.push_back(token); - token.clear(); - } - if (ch == '\n' || ch == ';') { - token_list.emplace_back(ch == '\n' ? "\n" : ";"); - } - } else { - token.push_back(ch); - } - } - if (!token.empty()) { - token_list.push_back(token); - } - return token_list; -} - -std::string DataManager::removeComment(std::string& line) -{ - std::string result; - bool in_brace = false; - bool in_quote = false; - for (char ch : line) { - if (ch == '{' && !in_quote) { - in_brace = true; - } else if (ch == '}' && !in_quote) { - in_brace = false; - } else if (ch == '"' && !in_brace) { - in_quote = !in_quote; - } - if (ch == '#' && !in_brace && !in_quote) { - break; - } - result.push_back(ch); - } - return result; -} - -void DataManager::parseCommand(std::vector& token_list) -{ - if (token_list.empty()) { - return; - } - if (token_list.front() == "create_clock") { - parseCreateClock(token_list); - } else if (token_list.front() == "set_case_analysis") { - parseSetCaseAnalysis(token_list); - } else if (token_list.front() == "set_input_delay") { - parseSetInputDelay(token_list); - } else if (token_list.front() == "set_output_delay") { - parseSetOutputDelay(token_list); - } else if (token_list.front() == "set_input_transition") { - parseSetInputTransition(token_list); - } else if (token_list.front() == "set_load") { - parseSetLoad(token_list); - } else if (token_list.front() == "set_clock_uncertainty") { - parseSetClockUncertainty(token_list); - } -} - -void DataManager::parseSetCaseAnalysis(std::vector& token_list) -{ - if (token_list.size() < 2 || (token_list[1] != "0" && token_list[1] != "1")) { - return; - } - bool case_value = token_list[1] == "1"; - std::vector object_list = getObjectList(token_list); - for (std::string& pin_name : resolveObjectList(object_list)) { - _database.get_timing_constraint().get_case_analysis_map()[pin_name] = case_value; - } -} - -void DataManager::parseCreateClock(std::vector& token_list) -{ - TimingClock timing_clock; - timing_clock.set_clock_name(getOptionValue(token_list, "-name")); - timing_clock.set_period(getOptionDoubleValue(token_list, "-period", 0.0)); - std::vector object_list = getObjectList(token_list); - std::vector source_list = resolveObjectList(object_list); - if (timing_clock.get_clock_name().empty() && !source_list.empty()) { - timing_clock.set_clock_name(source_list.front()); - } - timing_clock.set_source_list(source_list); - timing_clock.set_rise_edge(0.0); - timing_clock.set_fall_edge(timing_clock.get_period() / 2.0); - updateClock(timing_clock); -} - -void DataManager::parseSetInputDelay(std::vector& token_list) -{ - const double delay_value = getCommandDoubleValue(token_list); - const bool set_min = hasOption(token_list, "-min"); - const bool set_max = hasOption(token_list, "-max"); - std::string clock_name = getClockName(token_list); - std::vector object_list = getObjectList(token_list); - for (std::string& port_name : resolveObjectList(object_list)) { - TimingPortConstraint& port_constraint = getPortConstraint(port_name); - port_constraint.set_clock_name(clock_name); - if (set_min && !set_max) { - port_constraint.set_input_delay_min(delay_value); - port_constraint.set_has_input_delay_min(true); - } else if (set_max && !set_min) { - port_constraint.set_input_delay_max(delay_value); - port_constraint.set_has_input_delay_max(true); - } else { - port_constraint.set_input_delay_min(delay_value); - port_constraint.set_input_delay_max(delay_value); - port_constraint.set_has_input_delay_min(true); - port_constraint.set_has_input_delay_max(true); - } - } -} - -void DataManager::parseSetOutputDelay(std::vector& token_list) -{ - const double delay_value = getCommandDoubleValue(token_list); - const bool set_min = hasOption(token_list, "-min"); - const bool set_max = hasOption(token_list, "-max"); - std::string clock_name = getClockName(token_list); - std::vector object_list = getObjectList(token_list); - for (std::string& port_name : resolveObjectList(object_list)) { - TimingPortConstraint& port_constraint = getPortConstraint(port_name); - port_constraint.set_clock_name(clock_name); - if (set_min && !set_max) { - port_constraint.set_output_delay_min(delay_value); - port_constraint.set_has_output_delay_min(true); - } else if (set_max && !set_min) { - port_constraint.set_output_delay_max(delay_value); - port_constraint.set_has_output_delay_max(true); - } else { - port_constraint.set_output_delay_min(delay_value); - port_constraint.set_output_delay_max(delay_value); - port_constraint.set_has_output_delay_min(true); - port_constraint.set_has_output_delay_max(true); - } - } -} - -void DataManager::parseSetInputTransition(std::vector& token_list) -{ - const double transition_value = getCommandDoubleValue(token_list); - std::vector object_list = getObjectList(token_list); - for (std::string& port_name : resolveObjectList(object_list)) { - TimingPortConstraint& port_constraint = getPortConstraint(port_name); - port_constraint.set_input_transition(transition_value); - port_constraint.set_has_input_transition(true); - } -} - -void DataManager::parseSetLoad(std::vector& token_list) -{ - const double load_value = getCommandDoubleValue(token_list); - std::vector object_list = getObjectList(token_list); - for (std::string& port_name : resolveObjectList(object_list)) { - TimingPortConstraint& port_constraint = getPortConstraint(port_name); - port_constraint.set_load(load_value); - port_constraint.set_has_load(true); - } -} - -void DataManager::parseSetClockUncertainty(std::vector& token_list) -{ - const double uncertainty = getCommandDoubleValue(token_list); - if (!std::isfinite(uncertainty) || uncertainty < 0.0) { - return; - } - - std::vector clock_list = getObjectList(token_list); - if (clock_list.empty()) { - return; - } - auto& clock_map = _database.get_timing_constraint().get_clock_map(); - auto clock_it = clock_map.find(clock_list.front()); - if (clock_it == clock_map.end()) { - return; - } - - const bool setup = hasOption(token_list, "-setup"); - const bool hold = hasOption(token_list, "-hold"); - if (!hold || setup) { - clock_it->second.set_setup_uncertainty(uncertainty); - } - if (!setup || hold) { - clock_it->second.set_hold_uncertainty(uncertainty); - } -} - -double DataManager::getCommandDoubleValue(std::vector& token_list) -{ - for (size_t i = 1; i < token_list.size(); i++) { - if (token_list[i].empty() || token_list[i].front() == '-') { - if (token_list[i] == "-clock" || token_list[i] == "-name") { - i++; - } - continue; - } - char* end = nullptr; - double value = std::strtod(token_list[i].c_str(), &end); - if (end != token_list[i].c_str() && *end == '\0') { - return value; - } - } - return 0.0; -} - -std::string DataManager::getOptionValue(std::vector& token_list, const std::string& option) -{ - for (size_t i = 0; i + 1 < token_list.size(); i++) { - if (token_list[i] == option) { - return token_list[i + 1]; - } - } - return ""; -} - -double DataManager::getOptionDoubleValue(std::vector& token_list, const std::string& option, double default_value) -{ - std::string option_value = getOptionValue(token_list, option); - if (option_value.empty()) { - return default_value; - } - return std::stod(option_value); -} - -bool DataManager::hasOption(std::vector& token_list, const std::string& option) -{ - return STAUTIL.exist(token_list, option); -} - -std::string DataManager::getClockName(std::vector& token_list) -{ - for (std::size_t i = 0; i + 1 < token_list.size(); i++) { - if (token_list[i] != "-clock") { - continue; - } - if (isClockCollectionCommand(token_list[i + 1])) { - return getCollectionName(token_list, i + 1); - } - std::string clock_name = token_list[i + 1]; - if (!clock_name.empty() && clock_name.back() == ']') { - clock_name.pop_back(); - } - return clock_name; - } - return ""; -} - -std::string DataManager::getCollectionName(std::vector& token_list, std::size_t collection_idx) -{ - std::vector name_list; - for (std::size_t i = collection_idx + 1; i < token_list.size(); i++) { - std::string object_name = token_list[i]; - bool is_end = false; - if (object_name == "]") { - break; - } - pushObjectName(name_list, object_name); - if (is_end) { - break; - } - } - return name_list.empty() ? "" : name_list.front(); -} - -std::vector DataManager::getObjectList(std::vector& token_list) -{ - for (std::size_t i = 1; i < token_list.size(); i++) { - if (!isCollectionCommand(token_list[i])) { - continue; - } - - std::vector object_list; - for (std::size_t j = i + 1; j < token_list.size(); j++) { - std::string object_name = token_list[j]; - bool is_end = false; - if (object_name == "]") { - break; - } - pushObjectName(object_list, object_name); - if (is_end) { - break; - } - } - return object_list; - } - - for (auto iter = token_list.rbegin(); iter != token_list.rend(); ++iter) { - std::size_t token_idx = std::distance(iter, token_list.rend()) - 1; - if (!iter->empty() && iter->front() != '-' && !isCommandOptionValue(token_list, token_idx)) { - std::vector object_list; - pushObjectName(object_list, *iter); - return object_list; - } - } - return {}; -} - -void DataManager::pushObjectName(std::vector& object_list, std::string object_name) -{ - std::istringstream iss(object_name); - std::string split_object_name; - while (iss >> split_object_name) { - object_list.push_back(getObjectName(split_object_name)); - } -} - -std::string DataManager::getObjectName(std::string& object_name) -{ - if (!object_name.empty() && object_name.front() == '\\') { - object_name.erase(object_name.begin()); - } - return object_name; -} - -bool DataManager::isCollectionCommand(std::string& token) -{ - return token == "[get_ports" || token == "get_ports" || token == "[get_pins" || token == "get_pins"; -} - -bool DataManager::isClockCollectionCommand(std::string& token) -{ - return token == "[get_clocks" || token == "get_clocks"; -} - -bool DataManager::isCommandOptionValue(std::vector& token_list, std::size_t token_idx) -{ - if (token_idx == 0 || token_idx >= token_list.size()) { - return false; - } - std::string& prev_token = token_list[token_idx - 1]; - if (prev_token == "-name" || prev_token == "-clock" || prev_token == "-period") { - return true; - } - char* end = nullptr; - std::strtod(token_list[token_idx].c_str(), &end); - return end != token_list[token_idx].c_str() && *end == '\0'; -} - -std::vector DataManager::resolveObjectList(std::vector& object_list) -{ - Database& database = _database; - std::vector resolved_object_list; - for (std::string& object_name : object_list) { - std::string resolved_object_name = object_name; - if (resolved_object_name.rfind("[get_ports", 0) == 0) { - resolved_object_name = resolved_object_name.substr(10); - } - if (resolved_object_name.rfind("[get_pins", 0) == 0) { - resolved_object_name = resolved_object_name.substr(9); - std::replace(resolved_object_name.begin(), resolved_object_name.end(), '/', ':'); - } - if (database.get_pin_map().count(resolved_object_name) > 0) { - resolved_object_list.push_back(resolved_object_name); - continue; - } - if (!resolved_object_name.empty() && resolved_object_name.back() == ']') { - std::string trim_object_name = resolved_object_name; - trim_object_name.pop_back(); - if (database.get_pin_map().count(trim_object_name) > 0) { - resolved_object_list.push_back(trim_object_name); - continue; - } - } - std::replace(resolved_object_name.begin(), resolved_object_name.end(), '/', ':'); - if (database.get_pin_map().count(resolved_object_name) > 0) { - resolved_object_list.push_back(resolved_object_name); - } - } - return resolved_object_list; -} - -void DataManager::updateClock(TimingClock& timing_clock) -{ - Database& database = _database; - if (timing_clock.get_clock_name().empty()) { - return; - } - database.get_timing_constraint().get_clock_map()[timing_clock.get_clock_name()] = timing_clock; -} - -TimingPortConstraint& DataManager::getPortConstraint(const std::string& port_name) -{ - Database& database = _database; - TimingPortConstraint& port_constraint = database.get_timing_constraint().get_port_constraint_map()[port_name]; - port_constraint.set_port_name(port_name); - return port_constraint; -} void DataManager::printConfig() { diff --git a/src/operation/iSTA/source/data_manager/DataManager.hpp b/src/operation/iSTA/source/data_manager/DataManager.hpp index 7d91501979..a66b352dcb 100644 --- a/src/operation/iSTA/source/data_manager/DataManager.hpp +++ b/src/operation/iSTA/source/data_manager/DataManager.hpp @@ -32,6 +32,7 @@ class DataManager // function void input(std::map& config_map); void output(); + void readConstraint(); Config& getConfig() { return _config; } Database& getDatabase() { return _database; } @@ -66,46 +67,6 @@ class DataManager void buildNetList(); void makeNetList(); void makeNet(const std::string& net_name, Net& net); - void readConstraint(); - std::vector> readCommandList(std::string& sdc_file_path); - std::vector> resolveCommandList(std::vector>& command_list); - std::vector resolveCommandTokenList(std::vector& token_list, std::map& variable_map); - void updateVariableMap(std::vector& token_list, std::map& variable_map); - std::string resolveBracketCommand(std::vector& token_list, std::size_t& token_idx, std::map& variable_map); - std::vector getBracketTokenList(std::vector& token_list, std::size_t& token_idx, - std::map& variable_map); - std::string evalExpr(std::vector& expr_token_list); - double calcExprValue(std::vector& expr_token_list); - void calcExprMulDiv(std::vector& value_list, std::vector& operator_list); - std::string getExprValueString(const double value); - bool isExprOperator(std::string& token); - std::string resolveVariableToken(std::string token, std::map& variable_map); - std::string getTokenListString(std::vector& token_list, std::size_t begin_idx); - std::vector tokenizeSdc(std::string& content); - std::string removeComment(std::string& line); - void parseCommand(std::vector& token_list); - void parseSetCaseAnalysis(std::vector& token_list); - void parseCreateClock(std::vector& token_list); - void parseSetInputDelay(std::vector& token_list); - void parseSetOutputDelay(std::vector& token_list); - void parseSetInputTransition(std::vector& token_list); - void parseSetLoad(std::vector& token_list); - void parseSetClockUncertainty(std::vector& token_list); - double getCommandDoubleValue(std::vector& token_list); - std::string getOptionValue(std::vector& token_list, const std::string& option); - double getOptionDoubleValue(std::vector& token_list, const std::string& option, double default_value); - bool hasOption(std::vector& token_list, const std::string& option); - std::string getClockName(std::vector& token_list); - std::string getCollectionName(std::vector& token_list, std::size_t collection_idx); - std::vector getObjectList(std::vector& token_list); - void pushObjectName(std::vector& object_list, std::string object_name); - std::string getObjectName(std::string& object_name); - bool isCollectionCommand(std::string& token); - bool isClockCollectionCommand(std::string& token); - bool isCommandOptionValue(std::vector& token_list, std::size_t token_idx); - std::vector resolveObjectList(std::vector& object_list); - void updateClock(TimingClock& timing_clock); - TimingPortConstraint& getPortConstraint(const std::string& port_name); void printConfig(); void printDatabase(); #endif diff --git a/src/operation/iSTA/source/module/CMakeLists.txt b/src/operation/iSTA/source/module/CMakeLists.txt index dfc9f32402..8b0554bdc3 100644 --- a/src/operation/iSTA/source/module/CMakeLists.txt +++ b/src/operation/iSTA/source/module/CMakeLists.txt @@ -9,6 +9,7 @@ add_subdirectory(${ISTA_MODULE}/timing_characterizer) add_subdirectory(${ISTA_MODULE}/timing_reporter) add_subdirectory(${ISTA_MODULE}/power_reporter) add_subdirectory(${ISTA_MODULE}/sdf_writer) +add_subdirectory(${ISTA_MODULE}/sdc_command) add_library(ista_module INTERFACE) @@ -25,4 +26,5 @@ target_link_libraries(ista_module ista_timing_reporter ista_power_reporter ista_sdf_writer + ista_sdc_command ) diff --git a/src/operation/iSTA/source/module/sdc_command/CMakeLists.txt b/src/operation/iSTA/source/module/sdc_command/CMakeLists.txt new file mode 100644 index 0000000000..048fd13b19 --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/CMakeLists.txt @@ -0,0 +1,29 @@ +add_library(ista_sdc_command + ${ISTA_MODULE}/sdc_command/SdcCommand.cpp + ${ISTA_MODULE}/sdc_command/sdc_commands/SdcTclCmd.cpp + ${ISTA_MODULE}/sdc_command/sdc_commands/SdcCommandUtils.cpp + ${ISTA_MODULE}/sdc_command/sdc_commands/CreateClock.cpp + ${ISTA_MODULE}/sdc_command/sdc_commands/GetClocks.cpp + ${ISTA_MODULE}/sdc_command/sdc_commands/GetPorts.cpp + ${ISTA_MODULE}/sdc_command/sdc_commands/SetCaseAnalysis.cpp + ${ISTA_MODULE}/sdc_command/sdc_commands/SetInputDelay.cpp + ${ISTA_MODULE}/sdc_command/sdc_commands/SetOutputDelay.cpp + ${ISTA_MODULE}/sdc_command/sdc_commands/SetInputTransition.cpp + ${ISTA_MODULE}/sdc_command/sdc_commands/SetLoad.cpp + ${ISTA_MODULE}/sdc_command/sdc_commands/SetClockUncertainty.cpp + ${ISTA_MODULE}/sdc_command/sdc_commands/SetPropagatedClock.cpp +) + +target_link_libraries(ista_sdc_command + PUBLIC + tcl + PRIVATE + ista_data_manager +) + +target_include_directories(ista_sdc_command + PUBLIC + ${ISTA_MODULE}/sdc_command + ${ISTA_MODULE}/sdc_command/sdc_commands + ${ISTA_TOOLKIT}/utility +) diff --git a/src/operation/iSTA/source/module/sdc_command/SdcCommand.cpp b/src/operation/iSTA/source/module/sdc_command/SdcCommand.cpp new file mode 100644 index 0000000000..0c7b541545 --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/SdcCommand.cpp @@ -0,0 +1,151 @@ +// *************************************************************************************** +// 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 "SdcCommand.hpp" + +#include +#include +#include +#include + +namespace ista { + +void SdcCommand::initInst() +{ + Singleton::initInst(); +} + +void SdcCommand::initInst(std::initializer_list commands) +{ + Singleton::initInst(commands); +} + +SdcCommand& SdcCommand::getInst() +{ + return Singleton::getInst(); +} + +void SdcCommand::destroyInst() +{ + Singleton::destroyInst(); +} + +SdcCommand::SdcCommand() +{ + _interp = Tcl_CreateInterp(); +} + +SdcCommand::~SdcCommand() +{ + if (_interp != nullptr) { + Tcl_DeleteInterp(_interp); + } +} + +SdcCommand::SdcCommand(std::initializer_list commands) : SdcCommand() +{ + registerCommands(commands); +} + +bool SdcCommand::isInitialized() +{ + return Singleton::isInitialized(); +} + +Tcl_Command SdcCommand::createCmd(const char* cmd_name, Tcl_ObjCmdProc* proc, ClientData client_data, Tcl_CmdDeleteProc* delete_proc) +{ + return Tcl_CreateObjCommand(_interp, cmd_name, proc, client_data, delete_proc); +} + +void SdcCommand::registerCommands(std::initializer_list commands) +{ + for (const Command& command : commands) { + createCmd(command.name, command.proc, command.client_data, command.delete_proc); + } +} + +int SdcCommand::evalScriptFile(const std::string& file_name) +{ + clearErrors(); + std::ifstream script_file(file_name); + if (!script_file.is_open()) { + addError(0, "failed to open SDC file '" + file_name + "'"); + return TCL_ERROR; + } + + const std::string script((std::istreambuf_iterator(script_file)), std::istreambuf_iterator()); + return evalScript(script); +} + +int SdcCommand::evalString(const std::string& command) +{ + clearErrors(); + const int result = Tcl_EvalEx(_interp, command.c_str(), static_cast(command.size()), TCL_EVAL_GLOBAL); + if (result != TCL_OK) { + addError(Tcl_GetErrorLine(_interp), Tcl_GetStringResult(_interp)); + } + return result; +} + +int SdcCommand::evalScript(const std::string& script) +{ + clearErrors(); + int result = TCL_OK; + std::size_t offset = 0; + unsigned line_number = 1; + + while (offset < script.size()) { + Tcl_Parse parse{}; + const char* command_start = script.data() + offset; + const int parse_result = Tcl_ParseCommand(_interp, command_start, static_cast(script.size() - offset), 0, &parse); + const char* command_end = parse.commandStart + parse.commandSize; + std::size_t consumed = static_cast(command_end - command_start); + if (consumed == 0) { + consumed = script.size() - offset; + } + + const unsigned command_line = line_number + static_cast(std::count(command_start, parse.commandStart, '\n')); + if (parse_result != TCL_OK) { + addError(command_line, Tcl_GetStringResult(_interp)); + Tcl_ResetResult(_interp); + Tcl_FreeParse(&parse); + return TCL_ERROR; + } + + if (parse.numWords > 0) { + Tcl_SetErrorLine(_interp, static_cast(command_line)); + const int command_result = Tcl_EvalEx(_interp, parse.commandStart, parse.commandSize, TCL_EVAL_GLOBAL); + if (command_result != TCL_OK) { + addError(command_line, Tcl_GetStringResult(_interp)); + Tcl_ResetResult(_interp); + result = TCL_ERROR; + } + } + + line_number += static_cast(std::count(command_start, command_start + consumed, '\n')); + offset += consumed; + Tcl_FreeParse(&parse); + } + + return result; +} + +void SdcCommand::addError(unsigned line_number, std::string message) +{ + _errors.emplace_back(SdcError{line_number, std::move(message)}); +} + +} // namespace ista diff --git a/src/operation/iSTA/source/module/sdc_command/SdcCommand.hpp b/src/operation/iSTA/source/module/sdc_command/SdcCommand.hpp new file mode 100644 index 0000000000..b683a9e392 --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/SdcCommand.hpp @@ -0,0 +1,83 @@ +// *************************************************************************************** +// 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 + +#if __has_include() + #include +#else + #include +#endif + +#include +#include +#include + +#include "Singleton.hpp" + +namespace ista { + +struct SdcError +{ + unsigned line_number = 0; + std::string message; +}; + +class SdcCommand +{ + public: + struct Command + { + const char* name; + Tcl_ObjCmdProc* proc; + ClientData client_data = nullptr; + Tcl_CmdDeleteProc* delete_proc = nullptr; + }; + + static void initInst(); + static void initInst(std::initializer_list commands); + static SdcCommand& getInst(); + static void destroyInst(); + static bool isInitialized(); + + SdcCommand(); + SdcCommand(std::initializer_list commands); + ~SdcCommand(); + + Tcl_Interp* getInterp() const { return _interp; } + + Tcl_Command createCmd(const char* cmd_name, Tcl_ObjCmdProc* proc, ClientData client_data = nullptr, + Tcl_CmdDeleteProc* delete_proc = nullptr); + void registerCommands(std::initializer_list commands); + + int evalScriptFile(const std::string& file_name); + int evalString(const std::string& command); + + const std::vector& getErrors() const { return _errors; } + void clearErrors() { _errors.clear(); } + + private: + SdcCommand(const SdcCommand&) = delete; + SdcCommand& operator=(const SdcCommand&) = delete; + + int evalScript(const std::string& script); + void addError(unsigned line_number, std::string message); + + Tcl_Interp* _interp = nullptr; + std::vector _errors; +}; + +} // namespace ista diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/CreateClock.cpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/CreateClock.cpp new file mode 100644 index 0000000000..4282f1ab77 --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/CreateClock.cpp @@ -0,0 +1,109 @@ +// *************************************************************************************** +// 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 +#include + +#include "DataManager.hpp" +#include "Logger.hpp" +#include "SdcCommands.hpp" + +namespace ista::sdc { + +TclCreateClock::TclCreateClock(const char* cmd_name, ClientData client_data) : SdcTclCmd(cmd_name, client_data) +{ + addOption(new ecc::TclStringOption("-name", 0)); + addOption(new ecc::TclDoubleOption("-period", 0)); + addOption(new ecc::TclDoubleListOption("-waveform", 0)); + addOption(new ecc::TclStringListOption("objects", 1)); +} + +unsigned TclCreateClock::exec() +{ + ecc::TclOption* name_option = getOptionOrArg("-name"); + ecc::TclOption* period_option = getOptionOrArg("-period"); + ecc::TclOption* waveform_option = getOptionOrArg("-waveform"); + ecc::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; + } + if (std::string(name_option->getStringVal()).empty()) { + setTclError("create_clock requires a non-empty -name"); + 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::isfinite(period) || period <= 0.0) { + setTclError("create_clock -period must be a positive finite value"); + return 0; + } + if (!std::isfinite(rise_edge) || !std::isfinite(fall_edge) || rise_edge < 0.0 || fall_edge < 0.0 || rise_edge >= fall_edge || fall_edge >= period) { + setTclError("create_clock -waveform must satisfy 0 <= rise < fall < period"); + return 0; + } + + const std::vector source_list = object_option->getStringList(); + if (source_list.empty()) { + setTclError("create_clock requires at least one source port"); + return 0; + } + + Database& database = STADM.getDatabase(); + auto& clock_map = database.get_timing_constraint().get_clock_map(); + const std::string clock_name = name_option->getStringVal(); + 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 std::string& source_name : source_list) { + const auto pin_it = database.get_pin_map().find(source_name); + if (pin_it == database.get_pin_map().end() || !pin_it->second.get_is_port()) { + STALOG.warn(Loc::current(), "clock source '", source_name, "' is not a top-level port"); + setTclError("clock source is not a top-level port"); + return 0; + } + 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] = std::move(timing_clock); + return 1; +} + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/GetClocks.cpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/GetClocks.cpp new file mode 100644 index 0000000000..3cceb702c8 --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/GetClocks.cpp @@ -0,0 +1,56 @@ +// *************************************************************************************** +// 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 "DataManager.hpp" +#include "Logger.hpp" +#include "SdcCommands.hpp" + +namespace ista::sdc { + +TclGetClocks::TclGetClocks(const char* cmd_name, ClientData client_data) : SdcTclCmd(cmd_name, client_data) +{ + addOption(new ecc::TclStringListOption("clocks", 1)); +} + +unsigned TclGetClocks::exec() +{ + ecc::TclOption* clock_option = getOptionOrArg("clocks"); + if (!clock_option->is_set_val()) { + setTclError("get_clocks requires a clock list"); + return 0; + } + const std::vector clock_name_list = clock_option->getStringList(); + if (clock_name_list.empty()) { + setTclError("get_clocks requires at least one clock name"); + return 0; + } + + auto& clock_map = STADM.getDatabase().get_timing_constraint().get_clock_map(); + std::vector resolved_clocks; + resolved_clocks.reserve(clock_name_list.size()); + 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"); + setTclError("clock does not exist"); + return 0; + } + resolved_clocks.push_back(clock_name); + } + setResult(std::move(resolved_clocks)); + return 1; +} + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/GetPorts.cpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/GetPorts.cpp new file mode 100644 index 0000000000..1ca45c4347 --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/GetPorts.cpp @@ -0,0 +1,57 @@ +// *************************************************************************************** +// 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 "DataManager.hpp" +#include "Logger.hpp" +#include "SdcCommands.hpp" + +namespace ista::sdc { + +TclGetPorts::TclGetPorts(const char* cmd_name, ClientData client_data) : SdcTclCmd(cmd_name, client_data) +{ + addOption(new ecc::TclStringListOption("ports", 1)); +} + +unsigned TclGetPorts::exec() +{ + ecc::TclOption* port_option = getOptionOrArg("ports"); + if (!port_option->is_set_val()) { + setTclError("get_ports requires a port list"); + return 0; + } + const std::vector port_name_list = port_option->getStringList(); + if (port_name_list.empty()) { + setTclError("get_ports requires at least one port name"); + return 0; + } + + Database& database = STADM.getDatabase(); + std::vector resolved_ports; + resolved_ports.reserve(port_name_list.size()); + for (const std::string& port_name : port_name_list) { + const auto pin_it = database.get_pin_map().find(port_name); + if (pin_it == database.get_pin_map().end() || !pin_it->second.get_is_port()) { + STALOG.warn(Loc::current(), "port '", port_name, "' does not exist"); + setTclError("port does not exist"); + return 0; + } + resolved_ports.push_back(port_name); + } + setResult(std::move(resolved_ports)); + return 1; +} + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcCommandUtils.cpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcCommandUtils.cpp new file mode 100644 index 0000000000..f0963ac73e --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcCommandUtils.cpp @@ -0,0 +1,65 @@ +// *************************************************************************************** +// 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 "SdcCommandUtils.hpp" + +#include + +namespace ista::sdc { + +std::vector resolveObjectList(Database& database, const std::vector& object_list) +{ + std::vector resolved_object_list; + for (const std::string& object_name : object_list) { + std::string resolved_object_name = object_name; + if (!resolved_object_name.empty() && resolved_object_name.front() == '\\') { + resolved_object_name.erase(resolved_object_name.begin()); + } + if (resolved_object_name.rfind("[get_ports", 0) == 0) { + resolved_object_name = resolved_object_name.substr(10); + } + if (resolved_object_name.rfind("[get_pins", 0) == 0) { + resolved_object_name = resolved_object_name.substr(9); + std::replace(resolved_object_name.begin(), resolved_object_name.end(), '/', ':'); + } + if (database.get_pin_map().count(resolved_object_name) > 0) { + resolved_object_list.push_back(resolved_object_name); + continue; + } + if (!resolved_object_name.empty() && resolved_object_name.back() == ']') { + std::string trimmed_object_name = resolved_object_name; + trimmed_object_name.pop_back(); + if (database.get_pin_map().count(trimmed_object_name) > 0) { + resolved_object_list.push_back(trimmed_object_name); + continue; + } + } + std::replace(resolved_object_name.begin(), resolved_object_name.end(), '/', ':'); + if (database.get_pin_map().count(resolved_object_name) > 0) { + resolved_object_list.push_back(resolved_object_name); + } + } + return resolved_object_list; +} + +TimingPortConstraint& getPortConstraint(Database& database, const std::string& port_name) +{ + TimingPortConstraint& port_constraint = database.get_timing_constraint().get_port_constraint_map()[port_name]; + port_constraint.set_port_name(port_name); + return port_constraint; +} + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcCommandUtils.hpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcCommandUtils.hpp new file mode 100644 index 0000000000..1043c7c62f --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcCommandUtils.hpp @@ -0,0 +1,28 @@ +// *************************************************************************************** +// 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 +#include + +#include "Database.hpp" +namespace ista::sdc { + +std::vector resolveObjectList(Database& database, const std::vector& object_list); +TimingPortConstraint& getPortConstraint(Database& database, const std::string& port_name); + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcCommands.hpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcCommands.hpp new file mode 100644 index 0000000000..2fff392410 --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcCommands.hpp @@ -0,0 +1,103 @@ +// *************************************************************************************** +// 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 "SdcTclCmd.hpp" + +namespace ista::sdc { + +class TclSetCaseAnalysis : public SdcTclCmd +{ + public: + TclSetCaseAnalysis(const char* cmd_name, ClientData client_data); + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclSetInputDelay : public SdcTclCmd +{ + public: + TclSetInputDelay(const char* cmd_name, ClientData client_data); + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclSetOutputDelay : public SdcTclCmd +{ + public: + TclSetOutputDelay(const char* cmd_name, ClientData client_data); + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclSetInputTransition : public SdcTclCmd +{ + public: + TclSetInputTransition(const char* cmd_name, ClientData client_data); + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclSetLoad : public SdcTclCmd +{ + public: + TclSetLoad(const char* cmd_name, ClientData client_data); + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclSetClockUncertainty : public SdcTclCmd +{ + public: + TclSetClockUncertainty(const char* cmd_name, ClientData client_data); + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclGetPorts : public SdcTclCmd +{ + public: + TclGetPorts(const char* cmd_name, ClientData client_data); + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclGetClocks : public SdcTclCmd +{ + public: + TclGetClocks(const char* cmd_name, ClientData client_data); + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclCreateClock : public SdcTclCmd +{ + public: + TclCreateClock(const char* cmd_name, ClientData client_data); + unsigned check() override { return 1; } + unsigned exec() override; +}; + +class TclSetPropagatedClock : public SdcTclCmd +{ + public: + TclSetPropagatedClock(const char* cmd_name, ClientData client_data); + unsigned check() override { return 1; } + unsigned exec() override; +}; + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcTclCmd.cpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcTclCmd.cpp new file mode 100644 index 0000000000..4e3f4f866f --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcTclCmd.cpp @@ -0,0 +1,123 @@ +// *************************************************************************************** +// 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 "SdcTclCmd.hpp" + +namespace ista::sdc { + +SdcTclCmd::SdcTclCmd(const char* cmd_name, ClientData client_data) : ecc::TclCmd(cmd_name), _client_data(client_data) +{ +} + +int SdcTclCmd::execute(Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) +{ + resetExecutionState(); + + bool next_is_option_value = false; + ecc::TclOption* current_option = nullptr; + int argument_index = 0; + for (int index = 1; index < objc; ++index) { + const char* value = Tcl_GetString(objv[index]); + if (next_is_option_value) { + current_option->setVal(value); + next_is_option_value = false; + continue; + } + + ecc::TclOption* option = getOptionOrArg(value); + if (option != nullptr) { + current_option = option; + if (option->isSwitchOption()) { + option->setVal(nullptr); + } else { + next_is_option_value = true; + } + continue; + } + + ecc::TclOption* argument = getArg(argument_index++); + if (argument == nullptr) { + setTclError(std::string("unexpected argument '") + value + "'"); + setInterpreterError(interp); + return TCL_ERROR; + } + argument->setVal(value); + } + + if (next_is_option_value) { + setTclError(std::string("option '") + current_option->get_option_name() + "' requires a value"); + setInterpreterError(interp); + return TCL_ERROR; + } + if (!check()) { + setTclError(std::string("invalid ") + get_cmd_name() + " command"); + setInterpreterError(interp); + return TCL_ERROR; + } + if (!exec()) { + if (_error_message.empty()) { + setTclError(std::string(get_cmd_name()) + " failed"); + } + setInterpreterError(interp); + return TCL_ERROR; + } + if (!_error_message.empty()) { + setInterpreterError(interp); + return TCL_ERROR; + } + + if (_has_list_result) { + Tcl_Obj* list = Tcl_NewListObj(0, nullptr); + for (const std::string& value : _list_result) { + Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj(value.c_str(), static_cast(value.size()))); + } + Tcl_SetObjResult(interp, list); + } else if (_has_result) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(_result.c_str(), static_cast(_result.size()))); + } + return TCL_OK; +} + +void SdcTclCmd::setResult(std::string result) +{ + _result = std::move(result); + _has_result = true; + _has_list_result = false; +} + +void SdcTclCmd::setResult(std::vector result) +{ + _list_result = std::move(result); + _has_list_result = true; + _has_result = false; +} + +void SdcTclCmd::resetExecutionState() +{ + resetOptionArgValue(); + _error_message.clear(); + _result.clear(); + _list_result.clear(); + _has_result = false; + _has_list_result = false; +} + +void SdcTclCmd::setInterpreterError(Tcl_Interp* interp) const +{ + Tcl_SetObjResult(interp, Tcl_NewStringObj(_error_message.c_str(), static_cast(_error_message.size()))); +} + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcTclCmd.hpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcTclCmd.hpp new file mode 100644 index 0000000000..54b91b5ece --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SdcTclCmd.hpp @@ -0,0 +1,65 @@ +// *************************************************************************************** +// 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 +#include + +#include "ScriptEngine.hh" + +namespace ista::sdc { + +class SdcTclCmd : public ecc::TclCmd +{ + public: + SdcTclCmd(const char* cmd_name, ClientData client_data); + ~SdcTclCmd() override = default; + + int execute(Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]); + + protected: + void setTclError(std::string error_message) { _error_message = std::move(error_message); } + void setResult(std::string result); + void setResult(std::vector result); + + ClientData getClientData() const { return _client_data; } + + private: + void resetExecutionState(); + void setInterpreterError(Tcl_Interp* interp) const; + + ClientData _client_data = nullptr; + std::string _error_message; + std::string _result; + std::vector _list_result; + bool _has_result = false; + bool _has_list_result = false; +}; + +template +int executeTclCommand(ClientData client_data, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) +{ + if (objc == 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("empty Tcl command", -1)); + return TCL_ERROR; + } + + Command command(Tcl_GetString(objv[0]), client_data); + return command.execute(interp, objc, objv); +} + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetCaseAnalysis.cpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetCaseAnalysis.cpp new file mode 100644 index 0000000000..f346f1dc7a --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetCaseAnalysis.cpp @@ -0,0 +1,54 @@ +// *************************************************************************************** +// 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 "DataManager.hpp" +#include "SdcCommandUtils.hpp" +#include "SdcCommands.hpp" + +namespace ista::sdc { + +TclSetCaseAnalysis::TclSetCaseAnalysis(const char* cmd_name, ClientData client_data) : SdcTclCmd(cmd_name, client_data) +{ + addOption(new ecc::TclStringOption("value", 1)); + addOption(new ecc::TclStringListOption("objects", 1)); +} + +unsigned TclSetCaseAnalysis::exec() +{ + auto& data_manager = DataManager::getInst(); + + ecc::TclOption* value_option = getOptionOrArg("value"); + ecc::TclOption* object_option = getOptionOrArg("objects"); + if (!value_option->is_set_val() || !object_option->is_set_val()) { + setTclError("set_case_analysis requires a value and an object collection"); + return 0; + } + + const std::string value = value_option->getStringVal(); + if (value != "0" && value != "1") { + setTclError("set_case_analysis value must be 0 or 1"); + return 0; + } + + const bool case_value = value == "1"; + auto& case_analysis_map = data_manager.getDatabase().get_timing_constraint().get_case_analysis_map(); + for (const std::string& pin_name : resolveObjectList(data_manager.getDatabase(), object_option->getStringList())) { + case_analysis_map[pin_name] = case_value; + } + return 1; +} + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetClockUncertainty.cpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetClockUncertainty.cpp new file mode 100644 index 0000000000..4302d3b04e --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetClockUncertainty.cpp @@ -0,0 +1,73 @@ +// *************************************************************************************** +// 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 + +#include "DataManager.hpp" +#include "SdcCommandUtils.hpp" +#include "SdcCommands.hpp" + +namespace ista::sdc { + +TclSetClockUncertainty::TclSetClockUncertainty(const char* cmd_name, ClientData client_data) : SdcTclCmd(cmd_name, client_data) +{ + addOption(new ecc::TclSwitchOption("-setup")); + addOption(new ecc::TclSwitchOption("-hold")); + addOption(new ecc::TclDoubleOption("uncertainty", 1)); + addOption(new ecc::TclStringListOption("clocks", 1)); +} + +unsigned TclSetClockUncertainty::exec() +{ + auto& data_manager = DataManager::getInst(); + + ecc::TclOption* uncertainty_option = getOptionOrArg("uncertainty"); + ecc::TclOption* clock_option = getOptionOrArg("clocks"); + if (!uncertainty_option->is_set_val() || !clock_option->is_set_val()) { + setTclError("set_clock_uncertainty requires an uncertainty and a clock collection"); + return 0; + } + + const double uncertainty = uncertainty_option->getDoubleVal(); + if (!std::isfinite(uncertainty) || uncertainty < 0.0) { + setTclError("set_clock_uncertainty must be non-negative"); + return 0; + } + + const std::vector clock_list = clock_option->getStringList(); + if (clock_list.empty()) { + setTclError("set_clock_uncertainty requires a clock collection"); + return 0; + } + auto& clock_map = data_manager.getDatabase().get_timing_constraint().get_clock_map(); + const auto clock_it = clock_map.find(clock_list.front()); + if (clock_it == clock_map.end()) { + setTclError("clock '" + clock_list.front() + "' does not exist"); + return 0; + } + + const bool setup = getOptionOrArg("-setup")->is_set_val(); + const bool hold = getOptionOrArg("-hold")->is_set_val(); + if (!hold || setup) { + clock_it->second.set_setup_uncertainty(uncertainty); + } + if (!setup || hold) { + clock_it->second.set_hold_uncertainty(uncertainty); + } + return 1; +} + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetInputDelay.cpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetInputDelay.cpp new file mode 100644 index 0000000000..fb4aca3a2f --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetInputDelay.cpp @@ -0,0 +1,67 @@ +// *************************************************************************************** +// 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 "DataManager.hpp" +#include "SdcCommandUtils.hpp" +#include "SdcCommands.hpp" + +namespace ista::sdc { + +TclSetInputDelay::TclSetInputDelay(const char* cmd_name, ClientData client_data) : SdcTclCmd(cmd_name, client_data) +{ + addOption(new ecc::TclStringOption("-clock", 0)); + addOption(new ecc::TclSwitchOption("-min")); + addOption(new ecc::TclSwitchOption("-max")); + addOption(new ecc::TclDoubleOption("delay", 1)); + addOption(new ecc::TclStringListOption("objects", 1)); +} + +unsigned TclSetInputDelay::exec() +{ + auto& data_manager = DataManager::getInst(); + + ecc::TclOption* delay_option = getOptionOrArg("delay"); + ecc::TclOption* object_option = getOptionOrArg("objects"); + if (!delay_option->is_set_val() || !object_option->is_set_val()) { + setTclError("set_input_delay requires a delay and an object collection"); + return 0; + } + + ecc::TclOption* clock_option = getOptionOrArg("-clock"); + const double delay_value = delay_option->getDoubleVal(); + const bool set_min = getOptionOrArg("-min")->is_set_val(); + const bool set_max = getOptionOrArg("-max")->is_set_val(); + const std::string clock_name = clock_option->is_set_val() ? clock_option->getStringVal() : std::string{}; + for (const std::string& port_name : resolveObjectList(data_manager.getDatabase(), object_option->getStringList())) { + TimingPortConstraint& port_constraint = getPortConstraint(data_manager.getDatabase(), port_name); + port_constraint.set_clock_name(clock_name); + if (set_min && !set_max) { + port_constraint.set_input_delay_min(delay_value); + port_constraint.set_has_input_delay_min(true); + } else if (set_max && !set_min) { + port_constraint.set_input_delay_max(delay_value); + port_constraint.set_has_input_delay_max(true); + } else { + port_constraint.set_input_delay_min(delay_value); + port_constraint.set_input_delay_max(delay_value); + port_constraint.set_has_input_delay_min(true); + port_constraint.set_has_input_delay_max(true); + } + } + return 1; +} + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetInputTransition.cpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetInputTransition.cpp new file mode 100644 index 0000000000..91079314c3 --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetInputTransition.cpp @@ -0,0 +1,49 @@ +// *************************************************************************************** +// 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 "DataManager.hpp" +#include "SdcCommandUtils.hpp" +#include "SdcCommands.hpp" + +namespace ista::sdc { + +TclSetInputTransition::TclSetInputTransition(const char* cmd_name, ClientData client_data) : SdcTclCmd(cmd_name, client_data) +{ + addOption(new ecc::TclDoubleOption("transition", 1)); + addOption(new ecc::TclStringListOption("objects", 1)); +} + +unsigned TclSetInputTransition::exec() +{ + auto& data_manager = DataManager::getInst(); + + ecc::TclOption* transition_option = getOptionOrArg("transition"); + ecc::TclOption* object_option = getOptionOrArg("objects"); + if (!transition_option->is_set_val() || !object_option->is_set_val()) { + setTclError("set_input_transition requires a transition and an object collection"); + return 0; + } + + const double transition_value = transition_option->getDoubleVal(); + for (const std::string& port_name : resolveObjectList(data_manager.getDatabase(), object_option->getStringList())) { + TimingPortConstraint& port_constraint = getPortConstraint(data_manager.getDatabase(), port_name); + port_constraint.set_input_transition(transition_value); + port_constraint.set_has_input_transition(true); + } + return 1; +} + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetLoad.cpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetLoad.cpp new file mode 100644 index 0000000000..9db1d6537a --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetLoad.cpp @@ -0,0 +1,49 @@ +// *************************************************************************************** +// 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 "DataManager.hpp" +#include "SdcCommandUtils.hpp" +#include "SdcCommands.hpp" + +namespace ista::sdc { + +TclSetLoad::TclSetLoad(const char* cmd_name, ClientData client_data) : SdcTclCmd(cmd_name, client_data) +{ + addOption(new ecc::TclDoubleOption("load", 1)); + addOption(new ecc::TclStringListOption("objects", 1)); +} + +unsigned TclSetLoad::exec() +{ + auto& data_manager = DataManager::getInst(); + + ecc::TclOption* load_option = getOptionOrArg("load"); + ecc::TclOption* object_option = getOptionOrArg("objects"); + if (!load_option->is_set_val() || !object_option->is_set_val()) { + setTclError("set_load requires a load and an object collection"); + return 0; + } + + const double load_value = load_option->getDoubleVal(); + for (const std::string& port_name : resolveObjectList(data_manager.getDatabase(), object_option->getStringList())) { + TimingPortConstraint& port_constraint = getPortConstraint(data_manager.getDatabase(), port_name); + port_constraint.set_load(load_value); + port_constraint.set_has_load(true); + } + return 1; +} + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetOutputDelay.cpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetOutputDelay.cpp new file mode 100644 index 0000000000..362483bb1b --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetOutputDelay.cpp @@ -0,0 +1,67 @@ +// *************************************************************************************** +// 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 "DataManager.hpp" +#include "SdcCommandUtils.hpp" +#include "SdcCommands.hpp" + +namespace ista::sdc { + +TclSetOutputDelay::TclSetOutputDelay(const char* cmd_name, ClientData client_data) : SdcTclCmd(cmd_name, client_data) +{ + addOption(new ecc::TclStringOption("-clock", 0)); + addOption(new ecc::TclSwitchOption("-min")); + addOption(new ecc::TclSwitchOption("-max")); + addOption(new ecc::TclDoubleOption("delay", 1)); + addOption(new ecc::TclStringListOption("objects", 1)); +} + +unsigned TclSetOutputDelay::exec() +{ + auto& data_manager = DataManager::getInst(); + + ecc::TclOption* delay_option = getOptionOrArg("delay"); + ecc::TclOption* object_option = getOptionOrArg("objects"); + if (!delay_option->is_set_val() || !object_option->is_set_val()) { + setTclError("set_output_delay requires a delay and an object collection"); + return 0; + } + + ecc::TclOption* clock_option = getOptionOrArg("-clock"); + const double delay_value = delay_option->getDoubleVal(); + const bool set_min = getOptionOrArg("-min")->is_set_val(); + const bool set_max = getOptionOrArg("-max")->is_set_val(); + const std::string clock_name = clock_option->is_set_val() ? clock_option->getStringVal() : std::string{}; + for (const std::string& port_name : resolveObjectList(data_manager.getDatabase(), object_option->getStringList())) { + TimingPortConstraint& port_constraint = getPortConstraint(data_manager.getDatabase(), port_name); + port_constraint.set_clock_name(clock_name); + if (set_min && !set_max) { + port_constraint.set_output_delay_min(delay_value); + port_constraint.set_has_output_delay_min(true); + } else if (set_max && !set_min) { + port_constraint.set_output_delay_max(delay_value); + port_constraint.set_has_output_delay_max(true); + } else { + port_constraint.set_output_delay_min(delay_value); + port_constraint.set_output_delay_max(delay_value); + port_constraint.set_has_output_delay_min(true); + port_constraint.set_has_output_delay_max(true); + } + } + return 1; +} + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetPropagatedClock.cpp b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetPropagatedClock.cpp new file mode 100644 index 0000000000..d0b2f30ce4 --- /dev/null +++ b/src/operation/iSTA/source/module/sdc_command/sdc_commands/SetPropagatedClock.cpp @@ -0,0 +1,55 @@ +// *************************************************************************************** +// 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 "DataManager.hpp" +#include "Logger.hpp" +#include "SdcCommands.hpp" + +namespace ista::sdc { + +TclSetPropagatedClock::TclSetPropagatedClock(const char* cmd_name, ClientData client_data) : SdcTclCmd(cmd_name, client_data) +{ + addOption(new ecc::TclStringListOption("clocks", 1)); +} + +unsigned TclSetPropagatedClock::exec() +{ + ecc::TclOption* clock_option = getOptionOrArg("clocks"); + if (!clock_option->is_set_val()) { + setTclError("set_propagated_clock requires a clock collection"); + return 0; + } + const std::vector clock_name_list = clock_option->getStringList(); + if (clock_name_list.empty()) { + setTclError("set_propagated_clock requires at least one clock"); + return 0; + } + + 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"); + setTclError("clock does not exist"); + return 0; + } + } + for (const std::string& clock_name : clock_name_list) { + clock_map[clock_name].set_is_propagated(true); + } + return 1; +} + +} // namespace ista::sdc diff --git a/src/operation/iSTA/source/toolkit/utility/Singleton.hpp b/src/operation/iSTA/source/toolkit/utility/Singleton.hpp new file mode 100644 index 0000000000..46cc2c8a3d --- /dev/null +++ b/src/operation/iSTA/source/toolkit/utility/Singleton.hpp @@ -0,0 +1,71 @@ +// *************************************************************************************** +// 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 +#include +#include + +namespace ista { + +template +class Singleton +{ + public: + Singleton() = delete; + ~Singleton() = delete; + + template + static void initInst(Args&&... args) + { + std::lock_guard lock{mutex_}; + instance_.emplace(std::forward(args)...); + } + + static T& getInst() + { + std::lock_guard lock{mutex_}; + if (!instance_.has_value()) { + instance_.emplace(); + } + return *instance_; + } + + static void destroyInst() + { + std::lock_guard lock{mutex_}; + instance_.reset(); + } + + static bool isInitialized() + { + std::lock_guard lock{mutex_}; + return instance_.has_value(); + } + + private: + static std::optional instance_; + static std::mutex mutex_; +}; + +template +std::optional Singleton::instance_; + +template +std::mutex Singleton::mutex_; + +} // namespace ista