From 56e0da9d17206391ab169d5b0ee36345a6ca135f Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 27 May 2025 17:14:23 +0200 Subject: [PATCH] sdc: collect design objects --- passes/cmds/sdc.cc | 199 +++++++++++++++++++++++++++++++++--------- passes/cmds/stubs.sdc | 9 ++ 2 files changed, 166 insertions(+), 42 deletions(-) diff --git a/passes/cmds/sdc.cc b/passes/cmds/sdc.cc index 539b349f7..28a32faff 100644 --- a/passes/cmds/sdc.cc +++ b/passes/cmds/sdc.cc @@ -3,6 +3,7 @@ #include "kernel/rtlil.h" #include "kernel/log.h" #include +#include USING_YOSYS_NAMESPACE @@ -10,17 +11,20 @@ PRIVATE_NAMESPACE_BEGIN 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; + std::string expected = std::string("-") + flag_name; + if (expected == arg) { + flag_var = true; + return true; + } + return false; } +// TODO return values like json_to_tcl on result.json + static int sdc_get_pins_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[]) { (void)interp; + // When this flag is present, the search for the pattern is made in all positions in the hierarchy. bool hierarchical_flag = false; bool regexp_flag = false; bool nocase_flag = false; @@ -29,9 +33,10 @@ static int sdc_get_pins_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* c std::vector patterns; int i = 1; for (; i < objc; i++) { - if (parse_flag(Tcl_GetString(objv[i]), "hierarchical", hierarchical_flag)) - if (parse_flag(Tcl_GetString(objv[i]), "regexp", regexp_flag)) continue; - if (parse_flag(Tcl_GetString(objv[i]), "nocase", nocase_flag)) continue; + 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; @@ -53,56 +58,166 @@ static int sdc_get_pins_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* c (void)hierarchical_flag; (void)regexp_flag; (void)nocase_flag; - (void)separator; (void)of_objects; + if (separator != "/") { + Tcl_SetObjResult(interp, Tcl_NewStringObj("Only '/' accepted as separator", -1)); + return TCL_ERROR; + } + return TCL_OK; +} + +static int sdc_get_ports_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[]) +{ + (void)interp; + 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])); + } + log("get_pins patterns:\n"); + for (auto pat : patterns) { + log("\t%s\n", pat.c_str()); + } + (void)regexp_flag; + (void)nocase_flag; return TCL_OK; } class SDCInterpreter { private: - Tcl_Interp* interp = nullptr; + Tcl_Interp* interp = nullptr; public: - ~SDCInterpreter() { - if (interp) - Tcl_DeleteInterp(interp); - } - static SDCInterpreter& get() { - static SDCInterpreter instance; - return instance; - } - Tcl_Interp* fresh_interp() { - if (interp) - Tcl_DeleteInterp(interp); - interp = Tcl_CreateInterp(); - if (Tcl_Init(interp)!=TCL_OK) - log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno())); - Tcl_CreateObjCommand(interp, "get_pins", sdc_get_pins_cmd, NULL, NULL); - return interp; - } + ~SDCInterpreter() { + if (interp) + Tcl_DeleteInterp(interp); + } + static SDCInterpreter& get() { + static SDCInterpreter instance; + return instance; + } + Tcl_Interp* fresh_interp() { + if (interp) + Tcl_DeleteInterp(interp); + interp = Tcl_CreateInterp(); + if (Tcl_Init(interp)!=TCL_OK) + log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno())); + Tcl_CreateObjCommand(interp, "get_pins", sdc_get_pins_cmd, NULL, NULL); + Tcl_CreateObjCommand(interp, "get_ports", sdc_get_ports_cmd, NULL, NULL); + return interp; + } +}; + +struct SdcObjects { + std::vector ports; + std::vector> cells; + std::vector> pins; + std::vector> nets; + + void sniff_module(std::list& hierarchy, Module* mod) { + log_debug("sniffing module %s\n", mod->name.c_str()); + + std::string prefix; + for (auto mod_name : hierarchy) { + if (prefix.length()) + prefix += "/"; // TODO seperator? + prefix += mod_name; + } + + for (auto* wire : mod->wires()) { + std::string name = wire->name.str(); + log_assert(name.length()); + // TODO: really skip internal wires? + if (name[0] == '$') + continue; + name = name.substr(1); + std::string path = prefix; + if (path.length()) + path += "/"; + path += name; + nets.push_back(std::make_pair(path, wire)); + } + + for (auto* cell : mod->cells()) { + std::string name = cell->name.str(); + // TODO: really skip internal cells? + if (name[0] == '$') + continue; + name = name.substr(1); + std::string path = prefix; + if (path.length()) + path += "/"; + path += name; + cells.push_back(std::make_pair(path, cell)); + for (auto pin : cell->connections()) { + std::string pin_name = path + "/" + pin.first.str().substr(1); + pins.push_back(std::make_pair(pin_name, cell)); + } + if (auto sub_mod = mod->design->module(cell->type)) { + hierarchy.push_back(name); + sniff_module(hierarchy, sub_mod); + hierarchy.pop_back(); + } + } + } + SdcObjects(Design* design) { + Module* top = design->top_module(); + if (!top) + log_error("Top module couldn't be determined. Check 'top' attribute usage"); + for (auto port : top->ports) { + ports.push_back(port.str().substr(1)); + } + std::list hierarchy{}; + sniff_module(hierarchy, top); + } + void dump() { + log("Ports:\n"); + for (auto name : ports) { + log("\t%s\n", name.c_str()); + } + log("Cells:\n"); + for (auto [name, cell] : cells) { + (void)cell; + log("\t%s\n", name.c_str()); + } + log("Pins:\n"); + for (auto [name, pin] : pins) { + (void)pin; + log("\t%s\n", name.c_str()); + } + log("Nets:\n"); + for (auto [name, net] : nets) { + (void)net; + log("\t%s\n", name.c_str()); + } + log("\n"); + } }; // Also see TclPass struct SdcPass : public Pass { + // TODO help SdcPass() : Pass("sdc", "sniff at some SDC") { } - void execute(std::vector args, RTLIL::Design *) override { + void execute(std::vector args, RTLIL::Design *design) override { if (args.size() < 2) - log_cmd_error("Missing script file.\n"); - - std::vector script_args; - for (auto it = args.begin() + 2; it != args.end(); ++it) - script_args.push_back(Tcl_NewStringObj((*it).c_str(), (*it).size())); - + log_cmd_error("Missing SDC file.\n"); + // TODO optional extra stub file Tcl_Interp *interp = SDCInterpreter::get().fresh_interp(); Tcl_Preserve(interp); - Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argc", 4), NULL, Tcl_NewIntObj(script_args.size()), 0); - Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv", 4), NULL, Tcl_NewListObj(script_args.size(), script_args.data()), 0); - Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv0", 5), NULL, Tcl_NewStringObj(args[1].c_str(), args[1].size()), 0); - - std::string stub_path = "+/sdc/stubs.sdc"; - rewrite_filename(stub_path); + std::string stub_path = "+/sdc/stubs.sdc"; + rewrite_filename(stub_path); + SdcObjects objects(design); + objects.dump(); 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)); + 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: %s\n", Tcl_GetStringResult(interp)); Tcl_Release(interp); diff --git a/passes/cmds/stubs.sdc b/passes/cmds/stubs.sdc index 9f5d03b87..c219e77ea 100644 --- a/passes/cmds/stubs.sdc +++ b/passes/cmds/stubs.sdc @@ -1,7 +1,16 @@ +# 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} {} } +# OpenROAD +proc get_name {thing} { + return thing +} +proc get_full_name {thing} { + return thing +} + # Vivado UG903 stub add_cells_to_pblock stub add_to_power_rail