From 329a3783e2044a171f80089b1a14537a4b597466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emil=20Ji=C5=99=C3=AD=20Tywoniak?= Date: Mon, 4 Aug 2025 14:22:23 +0200 Subject: [PATCH] sdc: refactor more --- passes/cmds/sdc.cc | 474 ++++++++++++++++++++++++++------------------- 1 file changed, 270 insertions(+), 204 deletions(-) diff --git a/passes/cmds/sdc.cc b/passes/cmds/sdc.cc index 0c00a4466..9a2a5b1ed 100644 --- a/passes/cmds/sdc.cc +++ b/passes/cmds/sdc.cc @@ -1,10 +1,8 @@ #include "kernel/register.h" -#include "kernel/celltypes.h" #include "kernel/rtlil.h" #include "kernel/log.h" #include #include -#include #include #include @@ -12,6 +10,76 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +struct TclCall { + Tcl_Interp *interp; + int objc; + Tcl_Obj* const* objv; +}; + +static int redirect_unknown(TclCall call); +static size_t get_node_count(Tcl_Interp* interp); + +struct BitSelection { + bool all = false; + std::vector bits = {}; + void set_all() { + bits.clear(); + all = true; + } + void clear() { + bits.clear(); + all = false; + } + void set(size_t idx) { + if (all) + return; + if (idx >= bits.size()) + bits.resize(idx + 1); + bits[idx] = true; + } + void merge(const BitSelection& other) { + if (all) + return; + if (other.all) { + set_all(); + return; + } + if (other.bits.size() > bits.size()) + bits.resize(other.bits.size()); + for (size_t other_idx = 0; other_idx < other.bits.size(); other_idx++) { + bool other_bit = other.bits[other_idx]; + if (other_bit) + set(other_idx); + } + } + void dump() { + if (!all) { + for (size_t i = 0; i < bits.size(); i++) + if (bits[i]) + log("\t\t [%zu]\n", i); + } else { + log("\t\t FULL\n"); + } + } + bool is_set(size_t idx) const { + if (all) + return true; + if (idx >= bits.size()) + return false; + return bits[idx]; + } + // TODO actually use this + void compress(size_t size) { + if (bits.size() < size) + return; + for (size_t i = 0; i < size; i++) + if (!bits[i]) + return; + bits.clear(); + bits.shrink_to_fit(); + all = true; + } +}; struct SdcObjects { enum CollectMode { @@ -28,74 +96,19 @@ struct SdcObjects { // return a new graph node assuming unknown is overridden Graph, } value_mode; - struct BitSelection { - bool all = false; - std::vector bits = {}; - void set_all() { - bits.clear(); - all = true; - } - void clear() { - bits.clear(); - all = false; - } - void set(size_t idx) { - if (all) - return; - if (idx >= bits.size()) - bits.resize(idx + 1); - bits[idx] = true; - } - void merge(const BitSelection& other) { - if (all) - return; - if (other.all) { - set_all(); - return; - } - if (other.bits.size() > bits.size()) - bits.resize(other.bits.size()); - for (size_t other_idx = 0; other_idx < other.bits.size(); other_idx++) { - bool other_bit = other.bits[other_idx]; - if (other_bit) - set(other_idx); - } - } - void dump() { - if (!all) { - for (size_t i = 0; i < bits.size(); i++) - if (bits[i]) - log("\t\t [%zu]\n", i); - } else { - log("\t\t FULL\n"); - } - } - bool is_set(size_t idx) { - if (all) - return true; - if (idx >= bits.size()) - return false; - return bits[idx]; - } - // TODO actually use this - void compress(size_t size) { - if (bits.size() < size) - return; - for (size_t i = 0; i < size; i++) - if (!bits[i]) - return; - bits.clear(); - bits.shrink_to_fit(); - all = true; - } - }; using CellPin = std::pair; std::vector> design_ports; std::vector> design_cells; std::vector> design_pins; std::vector> design_nets; - dict constrained_ports; + using PortPattern = std::tuple; + using PinPattern = std::tuple; + std::vector> resolved_port_pattern_sets; + std::vector> resolved_pin_pattern_sets; + // TODO + + dict, BitSelection> constrained_ports; pool> constrained_cells; dict, BitSelection> constrained_pins; dict, BitSelection> constrained_nets; @@ -156,6 +169,30 @@ struct SdcObjects { sniff_module(hierarchy, top); } ~SdcObjects() = default; + + template + void build_normal_result(Tcl_Interp* interp, std::vector>&& resolved, U& tgt, std::function width, Tcl_Obj*& result) { + if (!result) + result = Tcl_NewListObj(resolved.size(), nullptr); + for (auto [name, obj, matching_bits] : resolved) { + for (size_t i = 0; i < width(obj); i++) + if (matching_bits.is_set(i)) { + Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(name.c_str(), name.size())); + break; + } + + } + size_t node_count = get_node_count(interp); + tgt.emplace_back(std::move(resolved)); + log("%zu %zu\n", node_count, tgt.size()); + log_assert(node_count == tgt.size()); + } + template + void merge_as_constrained(std::vector>&& resolved) { + for (auto [name, obj, matching_bits] : resolved) { + merge_or_init(std::make_pair(name, obj), constrained_pins, matching_bits); + } + } void dump() { std::sort(design_ports.begin(), design_ports.end()); std::sort(design_cells.begin(), design_cells.end()); @@ -186,7 +223,9 @@ struct SdcObjects { // } // log("\n"); log("Constrained ports:\n"); - for (auto [name, bits] : constrained_ports) { + for (auto [ref, bits] : constrained_ports) { + auto [name, port] = ref; + (void)port; log("\t%s\n", name.c_str()); bits.dump(); } @@ -213,16 +252,6 @@ struct SdcObjects { } }; -template -static bool parse_flag(char* arg, const char* flag_name, T& flag_var) { - std::string expected = std::string("-") + flag_name; - if (expected == arg) { - flag_var = true; - return true; - } - return false; -} - // TODO vectors // TODO cell arrays? struct MatchConfig { @@ -241,7 +270,7 @@ struct MatchConfig { hier(hierarchical_flag ? FLAT : TREE) { } }; -static std::pair matches(std::string name, const std::string& pat, const MatchConfig& config) { +static std::pair matches(std::string name, const std::string& pat, const MatchConfig& config) { (void)config; bool got_bit_index = false;; int bit_idx; @@ -258,7 +287,7 @@ static std::pair matches(std::string name, const bit_idx = std::stoi(bit_selector); } - SdcObjects::BitSelection bits = {}; + BitSelection bits = {}; if (name == pat_base) { if (got_bit_index) { bits.set(bit_idx); @@ -273,33 +302,38 @@ static std::pair matches(std::string name, const } } +static int graph_node(TclCall call) { + // TODO is that it? + return redirect_unknown(call); +} -static int redirect_unknown(Tcl_Interp *interp, int objc, Tcl_Obj* const objv[]) { - Tcl_Obj *newCmd = Tcl_NewStringObj("unknown", -1); - auto newObjc = objc + 1; - Tcl_Obj **newObjv = new Tcl_Obj*[newObjc]; - newObjv[0] = newCmd; - for (int i = 1; i < newObjc; i++) { - newObjv[i] = objv[i - 1]; - log("newObjv %s\n", Tcl_GetString(newObjv[i])); - } - int result = Tcl_EvalObjv(interp, newObjc, newObjv, 0); - Tcl_DecrRefCount(newCmd); - delete[] newObjv; +static int redirect_unknown(TclCall call) { + // TODO redirect to different command + Tcl_Obj *newCmd = Tcl_NewStringObj("unknown", -1); + auto newObjc = call.objc + 1; + Tcl_Obj **newObjv = new Tcl_Obj*[newObjc]; + newObjv[0] = newCmd; + for (int i = 1; i < newObjc; i++) { + newObjv[i] = call.objv[i - 1]; + } + int result = Tcl_EvalObjv(call.interp, newObjc, newObjv, 0); + Tcl_DecrRefCount(newCmd); + delete[] newObjv; return result; } + struct SdcGraphNode { - using Child = std::variant; - std::vector children; - SdcGraphNode() = default; - void addChild(SdcGraphNode* child) { - children.push_back(child); - } - void addChild(std::string tcl_string) { - children.push_back(tcl_string); - } - void dump(std::ostream& os) const { + using Child = std::variant; + std::vector children; + SdcGraphNode() = default; + void addChild(SdcGraphNode* child) { + children.push_back(child); + } + void addChild(std::string tcl_string) { + children.push_back(tcl_string); + } + void dump(std::ostream& os) const { bool first = true; for (auto child : children) { if (first) { @@ -317,13 +351,20 @@ struct SdcGraphNode { log_assert(false); } } - } + } }; +static size_t get_node_count(Tcl_Interp* interp) { + const char* idx_raw = Tcl_GetVar(interp, "sdc_call_index", TCL_GLOBAL_ONLY); + log_assert(idx_raw); + std::string idx(idx_raw); + for (auto c : idx) + if (!std::isdigit(c)) + log_error("sdc_call_index non-numeric value %s\n", idx.c_str()); + return std::stoi(idx); +} + std::vector> gather_nested_calls(Tcl_Interp* interp) { - const char* idx_s = Tcl_GetVar(interp, "sdc_call_index", TCL_GLOBAL_ONLY); - log_assert(idx_s); - size_t node_count = std::stoi(idx_s); Tcl_Obj* listObj = Tcl_GetVar2Ex(interp, "sdc_calls", nullptr, TCL_GLOBAL_ONLY); int listLength; @@ -354,7 +395,7 @@ std::vector> gather_nested_calls(Tcl_Interp* interp) { sdc_calls.push_back(subList); } } - log_assert(sdc_calls.size() == node_count); + log_assert(sdc_calls.size() == get_node_count(interp)); return sdc_calls; } @@ -412,11 +453,13 @@ void inspect_globals(Tcl_Interp* interp) { std::vector graph = build_graph(sdc_calls); dump_sdc_graph(graph, node_ownership(graph)); } + +// patterns -> (pattern-object-bit)s template -std::vector> +std::vector> find_matching(U objects, const MatchConfig& config, const std::vector &patterns, const char* obj_type) { - std::vector> resolved; + std::vector> resolved; for (auto pat : patterns) { bool found = false; for (auto [name, obj] : objects) { @@ -424,6 +467,7 @@ find_matching(U objects, const MatchConfig& config, const std::vector patterns; - int i = 1; - for (; i < objc; i++) { - if (parse_flag(Tcl_GetString(objv[i]), "hierarchical", hierarchical_flag)) continue; - if (parse_flag(Tcl_GetString(objv[i]), "hier", hierarchical_flag)) continue; - if (parse_flag(Tcl_GetString(objv[i]), "regexp", regexp_flag)) continue; - if (parse_flag(Tcl_GetString(objv[i]), "nocase", nocase_flag)) continue; - if (!strcmp(Tcl_GetString(objv[i]), "-hsc")) { - separator = Tcl_GetString(objv[++i]); - continue; + std::vector patterns = {}; + std::initializer_list legals; + const char* name; + GetterOpts(const char* name, std::initializer_list legals) : legals(legals), name(name) {} + bool parse_opt(Tcl_Obj* obj, const char* opt_name) { + char* arg = Tcl_GetString(obj); + std::string expected = std::string("-") + opt_name; + if (expected == arg) { + if (!std::find_if(legals.begin(), legals.end(), + [&opt_name](const char* str) { return opt_name == str; })) + log_cmd_error("Illegal argument %s for %s.\n", expected.c_str(), name); + return true; } - if (!strcmp(Tcl_GetString(objv[i]), "-of_objects")) { - of_objects = objv[++i]; - continue; + return false; + } + template + bool parse_flag(Tcl_Obj* obj, const char* flag_name, T& flag_var) { + bool ret = parse_opt(obj, flag_name); + if (ret) + flag_var = true; + return ret; + } + void parse(int objc, Tcl_Obj* const objv[]) { + int i = 1; + for (; i < objc; i++) { + if (parse_flag(objv[i], "hierarchical", hierarchical_flag)) continue; + if (parse_flag(objv[i], "hier", hierarchical_flag)) continue; + if (parse_flag(objv[i], "regexp", regexp_flag)) continue; + if (parse_flag(objv[i], "nocase", nocase_flag)) continue; + if (parse_opt(objv[i], "hsc")) { + log_assert(i + 1 < objc); + separator = Tcl_GetString(objv[++i]); + continue; + } + if (parse_opt(objv[i], "of_objects")) { + log_assert(i + 1 < objc); + of_objects = objv[++i]; + continue; + } + break; } - // Onto the next loop - break; - } - for (; i < objc; i++) { - patterns.push_back(Tcl_GetString(objv[i])); - } - if (objects->collect_mode == SdcObjects::CollectMode::SimpleGetter) { + for (; i < objc; i++) { + patterns.push_back(Tcl_GetString(objv[i])); + } + }; + void check_simple() { 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"); + log_error("%s got unexpected flags in simple mode\n", name); } + if (patterns.size() != 1) + log_error("%s got unexpected number of patterns in simple mode: %zu\n", name, patterns.size()); } + void check_simple_sep() { + if (separator != "/") + log_error("Only '/' accepted as separator"); + } +}; - MatchConfig config(regexp_flag, nocase_flag, hierarchical_flag); - std::vector> resolved; +// void build_normal_result(Tcl_Interp* interp, size_t list_len, size_t width, const std::string& name, Tcl_Obj*& result, const BitSelection& matching_bits) { +// if (!result) +// result = Tcl_NewListObj(list_len, nullptr); +// for (size_t i = 0; i < width; i++) +// if (matching_bits.is_set(i)) +// Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(name.c_str(), name.size())); +// } + +template +void merge_or_init(const T& key, dict& dst, const BitSelection& src) { + if (dst.count(key) == 0) { + dst[key] = src; + } else { + dst[key].merge(src); + } +} + +static int sdc_get_pins_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[]) +{ + auto* objects = (SdcObjects*)data; + GetterOpts opts("get_pins", {"hierarchical", "hier", "regexp", "nocase", "hsc", "of_objects"}); + opts.parse(objc, objv); + if (objects->collect_mode == SdcObjects::CollectMode::SimpleGetter) + opts.check_simple(); + opts.check_simple_sep(); + + MatchConfig config(opts.regexp_flag, opts.nocase_flag, opts.hierarchical_flag); + std::vector> resolved; const auto& pins = objects->design_pins; - resolved = find_matching(pins, config, patterns, "pin"); - - if (separator != "/") { - Tcl_SetResult(interp, (char *)"Only '/' accepted as separator", TCL_STATIC); - return TCL_ERROR; - } + resolved = find_matching(pins, config, opts.patterns, "pin"); Tcl_Obj *result = nullptr; - for (auto [name, pin, matching_bits] : resolved) { - if (objects->value_mode == SdcObjects::ValueMode::Normal) { - if (!result) - result = Tcl_NewListObj(resolved.size(), nullptr); - size_t width = (size_t)pin.first->getPort(pin.second).size(); - for (size_t i = 0; i < width; i++) - if (matching_bits.is_set(i)) - Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(name.c_str(), name.size())); - } + if (objects->value_mode == SdcObjects::ValueMode::Normal) { + log_error("TODO normal\n"); + auto width = [](SdcObjects::CellPin& pin) -> size_t { + return (size_t)pin.first->getPort(pin.second).size(); + }; + objects->build_normal_resultresolved_pin_pattern_sets)>(interp, std::move(resolved), objects->resolved_pin_pattern_sets, width, result); + } else if (objects->value_mode == SdcObjects::ValueMode::Graph) + return graph_node(TclCall{interp, objc, objv}); - if (objects->collect_mode != SdcObjects::CollectMode::FullConstraint) - objects->constrained_pins[std::make_pair(name, pin)].merge(matching_bits); - } + if (objects->collect_mode != SdcObjects::CollectMode::FullConstraint) + objects->merge_as_constrained(std::move(resolved)); + // merge_or_init(std::make_pair(name, pin), objects->constrained_pins, matching_bits); + // TODO + // } + + + // return objects->graph_node(interp, objc, objv, std::move(resolved), objects->resolved_pin_pattern_sets); - if (objects->value_mode == SdcObjects::ValueMode::Graph) { - return redirect_unknown(interp, objc, objv); - } if (result) Tcl_SetObjResult(interp, result); return TCL_OK; @@ -506,57 +597,32 @@ 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[]) { auto* objects = (SdcObjects*)data; - bool regexp_flag = false; - bool nocase_flag = false; - std::vector patterns; - int i = 1; - for (; i < objc; i++) { - if (parse_flag(Tcl_GetString(objv[i]), "regexp", regexp_flag)) continue; - if (parse_flag(Tcl_GetString(objv[i]), "nocase", nocase_flag)) continue; - // Onto the next loop - break; - } - 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"); - } - } + GetterOpts opts("get_ports", {"regexp", "nocase"}); + opts.parse(objc, objv); + if (objects->collect_mode == SdcObjects::CollectMode::SimpleGetter) + opts.check_simple(); - MatchConfig config(regexp_flag, nocase_flag, false); - std::vector> resolved; + MatchConfig config(opts.regexp_flag, opts.nocase_flag, false); + std::vector> resolved; const auto& ports = objects->design_ports; - resolved = find_matching(ports, config, patterns, "port"); + resolved = find_matching(ports, config, opts.patterns, "port"); Tcl_Obj *result = nullptr; for (auto [name, wire, matching_bits] : resolved) { - if (objects->value_mode == SdcObjects::ValueMode::Normal) { - if (!result) - result = Tcl_NewListObj(resolved.size(), nullptr); - size_t width = wire->width; - for (size_t i = 0; i < width; i++) - if (matching_bits.is_set(i)) - Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(name.c_str(), name.size())); - } - if (objects->collect_mode != SdcObjects::CollectMode::FullConstraint) { - if (objects->constrained_ports.count(name) == 0) { - objects->constrained_ports[name] = matching_bits; - } else { - objects->constrained_ports[name].merge(matching_bits); - } - } + if (objects->value_mode == SdcObjects::ValueMode::Normal) + log_error("TODO normal\n"); + // objects->build_normal_result(interp, resolved.size(), wire->width, name, result, matching_bits); + + if (objects->collect_mode != SdcObjects::CollectMode::FullConstraint) + merge_or_init(std::make_pair(name, wire), objects->constrained_ports, matching_bits); } if (objects->value_mode == SdcObjects::ValueMode::Graph) { - return redirect_unknown(interp, objc, objv); + return graph_node(TclCall{interp, objc, objv}); + // return objects->graph_node(interp, objc, objv, std::move(resolved), objects->resolved_port_pattern_sets); } - - Tcl_SetObjResult(interp, result); + if (result) + Tcl_SetObjResult(interp, result); return TCL_OK; } @@ -592,10 +658,10 @@ static int ys_track_typed_key_cmd(ClientData data, Tcl_Interp *interp, int 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_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]); + 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); @@ -608,7 +674,7 @@ static int ys_track_typed_key_cmd(ClientData data, Tcl_Interp *interp, int objc, bool found = false; for (auto [name, pin] : objects->design_pins) { log_error("TODO temporarily disabled due to working on a different flow\n"); - // if (name + "/" + pin->name.str() == str) { + // if (name + "/" + pin.name.str() == str) { // found = true; // objects->constrained_pins.insert(std::make_pair(name, pin)); // break; // resolved, expected unique @@ -674,7 +740,7 @@ public: struct SdcPass : public Pass { // TODO help SdcPass() : Pass("sdc", "sniff at some SDC") { } -void execute(std::vector args, RTLIL::Design *design) override { + void execute(std::vector args, RTLIL::Design *design) override { log_header(design, "Executing SDC pass.\n"); size_t argidx; std::vector opensta_stubs_paths;