From c9488c4fd0c398ee68333936563a00d6646175f3 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 10 Jun 2025 16:34:04 +0200 Subject: [PATCH] sdc: simple mode, remove per-tool stubs --- passes/cmds/sdc.cc | 180 +++++++++++++++++++++++++------ passes/cmds/stubs.sdc | 241 +++--------------------------------------- 2 files changed, 160 insertions(+), 261 deletions(-) diff --git a/passes/cmds/sdc.cc b/passes/cmds/sdc.cc index dcbd60ce5..b01cbe025 100644 --- a/passes/cmds/sdc.cc +++ b/passes/cmds/sdc.cc @@ -5,6 +5,7 @@ #include #include #include +#include USING_YOSYS_NAMESPACE @@ -12,6 +13,11 @@ PRIVATE_NAMESPACE_BEGIN struct SdcObjects { + enum CollectMode { + SimpleGetter, + FullGetter, + FullConstraint, + } collect_mode; std::vector design_ports; std::vector> design_cells; std::vector> design_pins; @@ -86,26 +92,26 @@ struct SdcObjects { constrained_cells.sort(); constrained_pins.sort(); constrained_nets.sort(); - log("Design ports:\n"); - for (auto name : design_ports) { - log("\t%s\n", name.c_str()); - } - log("Design cells:\n"); - for (auto [name, cell] : design_cells) { - (void)cell; - log("\t%s\n", name.c_str()); - } - log("Design pins:\n"); - for (auto [name, pin] : design_pins) { - (void)pin; - log("\t%s\n", name.c_str()); - } - log("Design nets:\n"); - for (auto [name, net] : design_nets) { - (void)net; - log("\t%s\n", name.c_str()); - } - log("\n"); + // log("Design ports:\n"); + // for (auto name : design_ports) { + // log("\t%s\n", name.c_str()); + // } + // log("Design cells:\n"); + // for (auto [name, cell] : design_cells) { + // (void)cell; + // log("\t%s\n", name.c_str()); + // } + // log("Design pins:\n"); + // for (auto [name, pin] : design_pins) { + // (void)pin; + // log("\t%s\n", name.c_str()); + // } + // log("Design nets:\n"); + // for (auto [name, net] : design_nets) { + // (void)net; + // log("\t%s\n", name.c_str()); + // } + // log("\n"); log("Constrained ports:\n"); for (auto name : constrained_ports) { log("\t%s\n", name.c_str()); @@ -158,12 +164,13 @@ struct MatchConfig { }; static bool matches(std::string name, const std::string& pat, const MatchConfig& config) { + (void)config; + // TODO implement full mode return name == pat; } static int sdc_get_pins_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[]) { - (void)interp; auto* objects = (SdcObjects*)data; // When this flag is present, the search for the pattern is made in all positions in the hierarchy. bool hierarchical_flag = false; @@ -192,6 +199,14 @@ static int sdc_get_pins_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_O for (; i < objc; i++) { patterns.push_back(Tcl_GetString(objv[i])); } + if (objects->collect_mode == SdcObjects::CollectMode::SimpleGetter) { + if (regexp_flag || hierarchical_flag || nocase_flag || separator != "/" || of_objects) { + log_error("get_pins got unexpected flags in simple mode\n"); + } + if (patterns.size() != 1) { + log_error("get_pins got unexpected number of patterns in simple mode\n"); + } + } MatchConfig config(regexp_flag, nocase_flag, hierarchical_flag); std::vector> resolved; @@ -209,12 +224,9 @@ static int sdc_get_pins_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_O Tcl_Obj *result = Tcl_NewListObj(resolved.size(), nullptr); for (auto obj : resolved) { Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(obj.first.c_str(), obj.first.size())); - objects->constrained_pins.insert(obj); + if (objects->collect_mode != SdcObjects::CollectMode::FullConstraint) + objects->constrained_pins.insert(obj); } - (void)hierarchical_flag; - (void)regexp_flag; - (void)nocase_flag; - (void)of_objects; if (separator != "/") { Tcl_SetResult(interp, (char *)"Only '/' accepted as separator", TCL_STATIC); @@ -227,7 +239,6 @@ static int sdc_get_pins_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_O static int sdc_get_ports_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[]) { - (void)interp; auto* objects = (SdcObjects*)data; bool regexp_flag = false; bool nocase_flag = false; @@ -242,6 +253,14 @@ static int sdc_get_ports_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_ for (; i < objc; i++) { patterns.push_back(Tcl_GetString(objv[i])); } + if (objects->collect_mode == SdcObjects::CollectMode::SimpleGetter) { + if (regexp_flag || nocase_flag) { + log_error("get_ports got unexpected flags in simple mode\n"); + } + if (patterns.size() != 1) { + log_error("get_ports got unexpected number of patterns in simple mode\n"); + } + } MatchConfig config(regexp_flag, nocase_flag, false); std::vector resolved; for (auto pat : patterns) { @@ -258,14 +277,89 @@ static int sdc_get_ports_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_ Tcl_Obj *result = Tcl_NewListObj(resolved.size(), nullptr); for (auto obj : resolved) { Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(obj.c_str(), obj.size())); - objects->constrained_ports.insert(obj); + if (objects->collect_mode != SdcObjects::CollectMode::FullConstraint) + objects->constrained_ports.insert(obj); } - (void)regexp_flag; - (void)nocase_flag; + Tcl_SetObjResult(interp, result); return TCL_OK; } +std::optional> split_at(std::string s) +{ + size_t pos = s.find('@'); + if (pos == std::string::npos) + return std::nullopt; + return std::make_tuple(s.substr(0, pos), s.substr(pos + 1)); +} + +// Whether string or list of strings, apply op to each string +void apply_args(Tcl_Interp *interp, std::function op, Tcl_Obj* obj) +{ + int length; + Tcl_Obj **value_list; + if (Tcl_ListObjGetElements(interp, obj, &length, &value_list) == TCL_OK) { + for (int i = 0; i < length; i++) { + op(Tcl_GetString(value_list[i])); + } + } else { + op(Tcl_GetString(obj)); + } +} + +static int ys_track_typed_key_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[]) +{ + log("ys_track_typed_key_cmd\n"); + auto* objects = (SdcObjects*)data; + if (objc != 5) + log_error("ys_track_typed_key: Unexpected number of arguments: %d (expected 5)\n", objc); + + if (objects->collect_mode != SdcObjects::CollectMode::FullConstraint) + return TCL_OK; + + std::string key_name = Tcl_GetString(objv[1]); + Tcl_Obj* key_value = objv[2]; + std::string key_expect_type = Tcl_GetString(objv[3]); + std::string proc_name = Tcl_GetString(objv[4]); + + auto track_typed = [key_expect_type, objects, proc_name, key_name](const char* str) -> void { + auto split = split_at(str); + if (!split) + log_error("%s: key %s should be a typed SDC object, but is something weird: %s\n", + proc_name.c_str(), key_name.c_str(), str); + + if (key_expect_type == "pin") { + log("PIN! %s\n", str); + bool found = false; + for (auto [name, pin] : objects->design_pins) { + if (name + "/" + pin->name.str() == str) { + found = true; + objects->constrained_pins.insert(std::make_pair(name, pin)); + break; // resolved, expected unique + } + } + if (!found) + log_error("%s: pin %s not found\n", proc_name.c_str(), str); + } else if (key_expect_type == "port") { + bool found = false; + for (auto name : objects->design_ports) { + if (name == str) { + found = true; + objects->constrained_ports.insert(name); + break; // resolved, expected unique + } + } + if (!found) + log_error("%s: port %s not found\n", proc_name.c_str(), str); + } else { + // TODO + log_warning("%s: unsupported type %s\n", proc_name.c_str(), key_expect_type.c_str()); + } + }; + apply_args(interp, track_typed, key_value); + return TCL_OK; +} + class SDCInterpreter { @@ -290,8 +384,10 @@ public: log_error("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno())); objects = std::make_unique(design); + objects->collect_mode = SdcObjects::CollectMode::FullConstraint; Tcl_CreateObjCommand(interp, "get_pins", sdc_get_pins_cmd, (ClientData) objects.get(), NULL); Tcl_CreateObjCommand(interp, "get_ports", sdc_get_ports_cmd, (ClientData) objects.get(), NULL); + Tcl_CreateObjCommand(interp, "ys_track_typed_key", ys_track_typed_key_cmd, (ClientData) objects.get(), NULL); return interp; } }; @@ -301,17 +397,33 @@ struct SdcPass : public Pass { // TODO help SdcPass() : Pass("sdc", "sniff at some SDC") { } void execute(std::vector args, RTLIL::Design *design) override { - if (args.size() < 2) - log_cmd_error("Missing SDC file.\n"); + // if (args.size() < 2) + // log_cmd_error("Missing SDC file.\n"); // TODO optional extra stub file + size_t argidx; + std::vector opensta_stubs_paths; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-stubs" && argidx+1 < args.size()) { + opensta_stubs_paths.push_back(args[++argidx]); + continue; + } + break; + } + if (argidx >= args.size()) + log_cmd_error("Missing SDC file.\n"); + + std::string sdc_path = args[argidx]; SDCInterpreter& sdc = SDCInterpreter::get(); Tcl_Interp *interp = sdc.fresh_interp(design); Tcl_Preserve(interp); std::string stub_path = "+/sdc/stubs.sdc"; rewrite_filename(stub_path); if (Tcl_EvalFile(interp, stub_path.c_str()) != TCL_OK) - log_cmd_error("SDC interpreter returned an error in stub file: %s\n", Tcl_GetStringResult(interp)); - if (Tcl_EvalFile(interp, args[1].c_str()) != TCL_OK) + log_cmd_error("SDC interpreter returned an error in stub preamble file: %s\n", Tcl_GetStringResult(interp)); + for (auto path : opensta_stubs_paths) + if (Tcl_EvalFile(interp, path.c_str()) != TCL_OK) + log_cmd_error("SDC interpreter returned an error in OpenSTA stub file %s: %s\n", path.c_str(), Tcl_GetStringResult(interp)); + if (Tcl_EvalFile(interp, sdc_path.c_str()) != TCL_OK) log_cmd_error("SDC interpreter returned an error: %s\n", Tcl_GetStringResult(interp)); sdc.objects->dump(); Tcl_Release(interp); diff --git a/passes/cmds/stubs.sdc b/passes/cmds/stubs.sdc index ea1600139..3466981c9 100644 --- a/passes/cmds/stubs.sdc +++ b/passes/cmds/stubs.sdc @@ -1,234 +1,21 @@ # with Tcl's eager evaluation, we will still eval args if they're unused by a stub proc stub {function_name} { - proc $function_name {args} {} + proc $function_name {args} "puts \"stubbed $function_name\"" } -# OpenROAD -proc get_name {thing} { - return thing -} -proc get_full_name {thing} { - return thing +proc is_suppressed {args} { + return 0 +} + +proc create_clock {args} { + return "CLOCK@" +} +proc get_clocks {args} { + return "CLOCK@" } -# Vivado UG903 -stub add_cells_to_pblock -stub add_to_power_rail -stub all_clocks -stub all_cpus -stub all_dsps -stub all_fanin -stub all_fanout -stub all_ffs -stub all_hsios -stub all_inputs -stub all_latches -stub all_outputs -stub all_rams -stub all_registers -stub connect_debug_port -stub create_clock -stub create_debug_core -stub create_debug_port -stub create_generated_clock -stub create_macro -stub create_pblock -stub create_power_rail -stub create_property -stub create_waiver stub current_design -stub current_instance -stub delete_macros -stub delete_pblock -stub delete_power_rails -stub endgroup -stub get_bel_pins -stub get_bels -stub get_clocks -stub get_debug_cores -stub get_debug_ports -stub get_generated_clocks -stub get_hierarchy_separator -stub get_iobanks -stub get_macros -stub get_nodes -stub get_package_pins -stub get_path_groups -stub get_pblocks -stub get_pips -stub get_pkgpin_bytegroups -stub get_pkgpin_nibbles -stub get_power_rails -stub get_property -stub get_site_pins -stub get_site_pips -stub get_sites -stub get_slrs -stub get_speed_models -stub get_tiles -stub get_timing_arcs -stub group_path -stub make_diff_pair_ports -stub remove_cells_from_pblock -stub remove_from_power_rail -stub reset_operating_conditions -stub reset_switching_activity -stub resize_pblock -stub set_bus_skew -stub set_case_analysis -stub set_clock_groups -stub set_clock_latency -stub set_clock_sense -stub set_clock_uncertainty -stub set_data_check -stub set_disable_timing -stub set_external_delay -stub set_false_path -stub set_hierarchy_separator -stub set_input_delay -stub set_input_jitter -stub set_load -stub set_logic_dc -stub set_logic_one -stub set_logic_unconnected -stub set_logic_zero -stub set_max_delay -stub set_max_time_borrow -stub set_min_delay -stub set_multicycle_path -stub set_operating_conditions -stub set_output_delay -stub set_package_pin_val -stub set_power_opt -stub set_propagated_clock -stub set_property -stub set_switching_activity -stub set_system_jitter -stub set_units -stub startgroup -stub update_macro - -# OpenSTA -stub source_ -stub write_sdc -stub current_instance -stub set_hierarchy_separator -stub check_path_divider -stub set_units -stub check_unit -stub unit_prefix_scale -stub check_unit_scale -stub all_clocks -stub all_inputs -stub all_outputs -stub all_registers -stub current_design -stub filter_objs -stub check_nocase_flag -stub find_liberty_libraries_matching -stub create_clock -stub delete_clock -stub create_generated_clock -stub delete_generated_clock -stub remove_gclk_cmd -stub group_path -stub check_exception_pins -stub set_clock_gating_check -stub set_clock_gating_check1 -stub set_clock_groups -stub unset_clock_groups -stub unset_clk_groups_cmd -stub set_clock_latency -stub unset_clock_latency -stub unset_clk_latency_cmd -stub set_sense -stub set_clock_sense -stub set_clock_sense_cmd1 -stub set_clock_transition -stub unset_clock_transition -stub set_clock_uncertainty -stub unset_clock_uncertainty -stub unset_clk_uncertainty_cmd -stub set_data_check -stub unset_data_check -stub unset_data_checks_cmd -stub set_disable_timing -stub set_disable_timing_instance -stub parse_disable_inst_ports -stub port_members -stub set_disable_timing_cell -stub parse_disable_cell_ports -stub unset_disable_timing -stub unset_disable_cmd -stub unset_disable_timing_cell -stub unset_disable_timing_instance -stub set_false_path -stub set_ideal_latency -stub set_ideal_network -stub set_ideal_transition -stub set_input_delay -stub set_port_delay -stub unset_input_delay -stub set_max_delay -stub set_path_delay -stub set_max_time_borrow -stub set_min_delay -stub set_min_pulse_width -stub set_multicycle_path -stub unset_path_exceptions -stub unset_path_exceptions_cmd -stub set_output_delay -stub unset_output_delay -stub unset_port_delay -stub set_propagated_clock -stub unset_propagated_clock -stub set_case_analysis -stub unset_case_analysis -stub set_drive -stub set_driving_cell -stub port_direction_any_output -stub set_fanout_load -stub set_input_transition -stub set_load -stub set_logic_dc -stub set_logic_value -stub set_logic_one -stub set_logic_zero -stub set_max_area -stub set_max_capacitance -stub set_capacitance_limit -stub set_max_fanout -stub set_fanout_limit -stub set_max_transition -stub set_port_fanout_number -stub set_resistance -stub set_timing_derate -stub unset_timing_derate -stub parse_from_arg -stub parse_thrus_arg -stub parse_to_arg -stub parse_to_arg1 -stub delete_from_thrus_to -stub parse_comment_key -stub set_min_capacitance -stub set_operating_conditions -stub parse_op_cond -stub parse_op_cond_analysis_type -stub set_wire_load_min_block_size -stub set_wire_load_mode -stub set_wire_load_model -stub set_wire_load_selection_group -stub set_voltage -stub create_voltage_area -stub set_level_shifter_strategy -stub set_level_shifter_threshold -stub set_max_dynamic_power -stub set_max_leakage_power -stub define_corners -stub set_pvt -stub set_pvt_min_max -stub default_operating_conditions -stub cell_regexp -stub cell_regexp_hsc -stub port_regexp -stub port_regexp_hsc \ No newline at end of file +#stub ys_track_typed_key +stub ys_track_untyped_key +stub ys_err_key +stub ys_err_flag