mirror of
https://github.com/YosysHQ/yosys
synced 2025-06-02 20:31:22 +00:00
sdc: collect strictly matching objects
This commit is contained in:
parent
bfc2dfe698
commit
c615b61f9f
2 changed files with 146 additions and 101 deletions
|
@ -9,6 +9,117 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
struct SdcObjects {
|
||||
std::vector<std::string> design_ports;
|
||||
std::vector<std::pair<std::string, Cell*>> design_cells;
|
||||
std::vector<std::pair<std::string, Cell*>> design_pins;
|
||||
std::vector<std::pair<std::string, Wire*>> design_nets;
|
||||
|
||||
pool<std::string> constrained_ports;
|
||||
pool<std::pair<std::string, Cell*>> constrained_cells;
|
||||
pool<std::pair<std::string, Cell*>> constrained_pins;
|
||||
pool<std::pair<std::string, Wire*>> constrained_nets;
|
||||
|
||||
void sniff_module(std::list<std::string>& hierarchy, Module* mod) {
|
||||
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;
|
||||
design_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;
|
||||
design_cells.push_back(std::make_pair(path, cell));
|
||||
for (auto pin : cell->connections()) {
|
||||
std::string pin_name = path + "/" + pin.first.str().substr(1);
|
||||
design_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) {
|
||||
design_ports.push_back(port.str().substr(1));
|
||||
}
|
||||
std::list<std::string> hierarchy{};
|
||||
sniff_module(hierarchy, top);
|
||||
}
|
||||
~SdcObjects() = default;
|
||||
void dump() {
|
||||
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());
|
||||
}
|
||||
log("Constrained cells:\n");
|
||||
for (auto [name, cell] : constrained_cells) {
|
||||
(void)cell;
|
||||
log("\t%s\n", name.c_str());
|
||||
}
|
||||
log("Constrained pins:\n");
|
||||
for (auto [name, pin] : constrained_pins) {
|
||||
(void)pin;
|
||||
log("\t%s\n", name.c_str());
|
||||
}
|
||||
log("Constrained nets:\n");
|
||||
for (auto [name, net] : constrained_nets) {
|
||||
(void)net;
|
||||
log("\t%s\n", name.c_str());
|
||||
}
|
||||
log("\n");
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static bool parse_flag(char* arg, const char* flag_name, T& flag_var) {
|
||||
std::string expected = std::string("-") + flag_name;
|
||||
|
@ -23,9 +134,10 @@ static bool parse_flag(char* arg, const char* flag_name, T& flag_var) {
|
|||
// TODO vectors
|
||||
// TODO cell arrays?
|
||||
|
||||
static int sdc_get_pins_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[])
|
||||
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;
|
||||
bool regexp_flag = false;
|
||||
|
@ -53,9 +165,16 @@ static int sdc_get_pins_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* c
|
|||
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());
|
||||
bool found = false;
|
||||
for (auto [name, pin] : objects->design_pins) {
|
||||
if (name == pat) {
|
||||
found = true;
|
||||
objects->constrained_pins.insert(std::make_pair(name, pin));
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
log_warning("No matches in design for pattern %s\n", pat.c_str());
|
||||
}
|
||||
(void)hierarchical_flag;
|
||||
(void)regexp_flag;
|
||||
|
@ -68,9 +187,10 @@ static int sdc_get_pins_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* c
|
|||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int sdc_get_ports_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[])
|
||||
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;
|
||||
std::vector<std::string> patterns;
|
||||
|
@ -84,20 +204,29 @@ static int sdc_get_ports_cmd(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj*
|
|||
for (; i < objc; i++) {
|
||||
patterns.push_back(Tcl_GetString(objv[i]));
|
||||
}
|
||||
log("get_ports patterns:\n");
|
||||
for (auto pat : patterns) {
|
||||
log("\t%s\n", pat.c_str());
|
||||
bool found = false;
|
||||
for (auto name : objects->design_ports) {
|
||||
if (name == pat) {
|
||||
found = true;
|
||||
objects->constrained_ports.insert(name);
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
log_warning("No matches in design for pattern %s\n", pat.c_str());
|
||||
}
|
||||
(void)regexp_flag;
|
||||
(void)nocase_flag;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
class SDCInterpreter
|
||||
{
|
||||
private:
|
||||
Tcl_Interp* interp = nullptr;
|
||||
public:
|
||||
std::unique_ptr<SdcObjects> objects;
|
||||
~SDCInterpreter() {
|
||||
if (interp)
|
||||
Tcl_DeleteInterp(interp);
|
||||
|
@ -106,105 +235,21 @@ public:
|
|||
static SDCInterpreter instance;
|
||||
return instance;
|
||||
}
|
||||
Tcl_Interp* fresh_interp() {
|
||||
Tcl_Interp* fresh_interp(Design* design) {
|
||||
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);
|
||||
log_error("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno()));
|
||||
|
||||
objects = std::make_unique<SdcObjects>(design);
|
||||
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);
|
||||
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("Dumping detected design objects visible to SDC constraints\n");
|
||||
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
|
||||
|
@ -213,16 +258,16 @@ struct SdcPass : public Pass {
|
|||
if (args.size() < 2)
|
||||
log_cmd_error("Missing SDC file.\n");
|
||||
// TODO optional extra stub file
|
||||
Tcl_Interp *interp = SDCInterpreter::get().fresh_interp();
|
||||
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);
|
||||
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));
|
||||
if (Tcl_EvalFile(interp, args[1].c_str()) != TCL_OK)
|
||||
log_cmd_error("SDC interpreter returned an error: %s\n", Tcl_GetStringResult(interp));
|
||||
sdc.objects->dump();
|
||||
Tcl_Release(interp);
|
||||
}
|
||||
} SdcPass;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
puts "SDC constraints file says hello from arbitrary Tcl execution"
|
||||
set_false_path -from [get_pins s1/sa] -to [get_pins s1/sb]
|
||||
puts "This should print something:"
|
||||
puts [get_ports {slink_clk_o slink_*_o}]
|
||||
puts [get_ports a b]
|
||||
puts "Did it?"
|
Loading…
Add table
Add a link
Reference in a new issue