mirror of
https://github.com/YosysHQ/yosys
synced 2025-06-07 06:33:24 +00:00
sdc: collect design objects
This commit is contained in:
parent
83ff7e0f9e
commit
56e0da9d17
2 changed files with 166 additions and 42 deletions
|
@ -3,6 +3,7 @@
|
||||||
#include "kernel/rtlil.h"
|
#include "kernel/rtlil.h"
|
||||||
#include "kernel/log.h"
|
#include "kernel/log.h"
|
||||||
#include <tcl.h>
|
#include <tcl.h>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
|
@ -18,9 +19,12 @@ static bool parse_flag(char* arg, const char* flag_name, T& flag_var) {
|
||||||
return false;
|
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[])
|
static int sdc_get_pins_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[])
|
||||||
{
|
{
|
||||||
(void)interp;
|
(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 hierarchical_flag = false;
|
||||||
bool regexp_flag = false;
|
bool regexp_flag = false;
|
||||||
bool nocase_flag = false;
|
bool nocase_flag = false;
|
||||||
|
@ -29,7 +33,8 @@ static int sdc_get_pins_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* c
|
||||||
std::vector<std::string> patterns;
|
std::vector<std::string> patterns;
|
||||||
int i = 1;
|
int i = 1;
|
||||||
for (; i < objc; i++) {
|
for (; i < objc; i++) {
|
||||||
if (parse_flag(Tcl_GetString(objv[i]), "hierarchical", hierarchical_flag))
|
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]), "regexp", regexp_flag)) continue;
|
||||||
if (parse_flag(Tcl_GetString(objv[i]), "nocase", nocase_flag)) continue;
|
if (parse_flag(Tcl_GetString(objv[i]), "nocase", nocase_flag)) continue;
|
||||||
if (!strcmp(Tcl_GetString(objv[i]), "-hsc")) {
|
if (!strcmp(Tcl_GetString(objv[i]), "-hsc")) {
|
||||||
|
@ -53,8 +58,36 @@ static int sdc_get_pins_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* c
|
||||||
(void)hierarchical_flag;
|
(void)hierarchical_flag;
|
||||||
(void)regexp_flag;
|
(void)regexp_flag;
|
||||||
(void)nocase_flag;
|
(void)nocase_flag;
|
||||||
(void)separator;
|
|
||||||
(void)of_objects;
|
(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<std::string> 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;
|
return TCL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,29 +111,111 @@ public:
|
||||||
if (Tcl_Init(interp)!=TCL_OK)
|
if (Tcl_Init(interp)!=TCL_OK)
|
||||||
log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno()));
|
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_pins", sdc_get_pins_cmd, NULL, NULL);
|
||||||
|
Tcl_CreateObjCommand(interp, "get_ports", sdc_get_ports_cmd, NULL, NULL);
|
||||||
return interp;
|
return interp;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SdcObjects {
|
||||||
|
std::vector<std::string> ports;
|
||||||
|
std::vector<std::pair<std::string, Cell*>> cells;
|
||||||
|
std::vector<std::pair<std::string, Cell*>> pins;
|
||||||
|
std::vector<std::pair<std::string, Wire*>> nets;
|
||||||
|
|
||||||
|
void sniff_module(std::list<std::string>& 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<std::string> 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
|
// Also see TclPass
|
||||||
struct SdcPass : public Pass {
|
struct SdcPass : public Pass {
|
||||||
|
// TODO help
|
||||||
SdcPass() : Pass("sdc", "sniff at some SDC") { }
|
SdcPass() : Pass("sdc", "sniff at some SDC") { }
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *) override {
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
|
||||||
if (args.size() < 2)
|
if (args.size() < 2)
|
||||||
log_cmd_error("Missing script file.\n");
|
log_cmd_error("Missing SDC file.\n");
|
||||||
|
// TODO optional extra stub file
|
||||||
std::vector<Tcl_Obj*> script_args;
|
|
||||||
for (auto it = args.begin() + 2; it != args.end(); ++it)
|
|
||||||
script_args.push_back(Tcl_NewStringObj((*it).c_str(), (*it).size()));
|
|
||||||
|
|
||||||
Tcl_Interp *interp = SDCInterpreter::get().fresh_interp();
|
Tcl_Interp *interp = SDCInterpreter::get().fresh_interp();
|
||||||
Tcl_Preserve(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";
|
std::string stub_path = "+/sdc/stubs.sdc";
|
||||||
rewrite_filename(stub_path);
|
rewrite_filename(stub_path);
|
||||||
|
SdcObjects objects(design);
|
||||||
|
objects.dump();
|
||||||
if (Tcl_EvalFile(interp, stub_path.c_str()) != TCL_OK)
|
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)
|
if (Tcl_EvalFile(interp, args[1].c_str()) != TCL_OK)
|
||||||
|
|
|
@ -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 stub {function_name} {
|
||||||
proc $function_name {args} {}
|
proc $function_name {args} {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# OpenROAD
|
||||||
|
proc get_name {thing} {
|
||||||
|
return thing
|
||||||
|
}
|
||||||
|
proc get_full_name {thing} {
|
||||||
|
return thing
|
||||||
|
}
|
||||||
|
|
||||||
# Vivado UG903
|
# Vivado UG903
|
||||||
stub add_cells_to_pblock
|
stub add_cells_to_pblock
|
||||||
stub add_to_power_rail
|
stub add_to_power_rail
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue