3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-10-09 17:31:59 +00:00

sdc: functional graph

This commit is contained in:
Emil J. Tywoniak 2025-08-01 16:33:10 +02:00
parent 2eb842e8b4
commit c81145ec31
2 changed files with 129 additions and 54 deletions

View file

@ -6,6 +6,7 @@
#include <list> #include <list>
#include <regex> #include <regex>
#include <optional> #include <optional>
#include <iostream>
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
@ -89,7 +90,7 @@ struct SdcObjects {
} }
}; };
using CellPin = std::pair<Cell*, IdString>; using CellPin = std::pair<Cell*, IdString>;
std::vector<std::string> design_ports; std::vector<std::pair<std::string, Wire*>> design_ports;
std::vector<std::pair<std::string, Cell*>> design_cells; std::vector<std::pair<std::string, Cell*>> design_cells;
std::vector<std::pair<std::string, CellPin>> design_pins; std::vector<std::pair<std::string, CellPin>> design_pins;
std::vector<std::pair<std::string, Wire*>> design_nets; std::vector<std::pair<std::string, Wire*>> design_nets;
@ -149,7 +150,7 @@ struct SdcObjects {
if (!top) if (!top)
log_error("Top module couldn't be determined. Check 'top' attribute usage"); log_error("Top module couldn't be determined. Check 'top' attribute usage");
for (auto port : top->ports) { for (auto port : top->ports) {
design_ports.push_back(port.str().substr(1)); design_ports.push_back(std::make_pair(port.str().substr(1), top->wire(port)));
} }
std::list<std::string> hierarchy{}; std::list<std::string> hierarchy{};
sniff_module(hierarchy, top); sniff_module(hierarchy, top);
@ -275,36 +276,59 @@ static std::pair<bool, SdcObjects::BitSelection> matches(std::string name, const
static int redirect_unknown(Tcl_Interp *interp, int objc, Tcl_Obj* const objv[]) { static int redirect_unknown(Tcl_Interp *interp, int objc, Tcl_Obj* const objv[]) {
Tcl_Obj *newCmd = Tcl_NewStringObj("unknown", -1); Tcl_Obj *newCmd = Tcl_NewStringObj("unknown", -1);
Tcl_Obj **newObjv = new Tcl_Obj*[objc+1]; auto newObjc = objc + 1;
Tcl_Obj **newObjv = new Tcl_Obj*[newObjc];
newObjv[0] = newCmd; newObjv[0] = newCmd;
for (int i = 1; i < objc + 1; i++) { for (int i = 1; i < newObjc; i++) {
newObjv[i] = objv[i - 1]; newObjv[i] = objv[i - 1];
log("newObjv %s\n", Tcl_GetString(newObjv[i]));
} }
int result = Tcl_EvalObjv(interp, objc, newObjv, 0); int result = Tcl_EvalObjv(interp, newObjc, newObjv, 0);
Tcl_DecrRefCount(newCmd); Tcl_DecrRefCount(newCmd);
delete[] newObjv; delete[] newObjv;
return result; return result;
} }
void inspect_globals(Tcl_Interp* interp) { struct SdcGraphNode {
auto get_var = [&](const char* name) -> const char* { using Child = std::variant<SdcGraphNode*, std::string>;
return Tcl_GetVar(interp, name, TCL_GLOBAL_ONLY); std::vector<Child> children;
}; SdcGraphNode() = default;
const char* idx_s = get_var("sdc_call_index"); 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) {
first = false;
} else {
os << " ";
}
if (auto* s = std::get_if<std::string>(&child))
os << *s;
else if (SdcGraphNode*& c = *std::get_if<SdcGraphNode*>(&child)) {
os << "[";
c->dump(os);
os << "]";
} else {
log_assert(false);
}
}
}
};
std::vector<std::vector<std::string>> gather_nested_calls(Tcl_Interp* interp) {
const char* idx_s = Tcl_GetVar(interp, "sdc_call_index", TCL_GLOBAL_ONLY);
log_assert(idx_s); log_assert(idx_s);
size_t node_count = std::stoi(idx_s); size_t node_count = std::stoi(idx_s);
// else
// printf("sdc_call_index: unset\n");
// if (auto calls = get_var("sdc_calls"))
// printf("sdc_calls: %s\n", calls);
// else
// printf("sdc_calls: unset\n");
Tcl_Obj* listObj = Tcl_GetVar2Ex(interp, "sdc_calls", nullptr, TCL_GLOBAL_ONLY); Tcl_Obj* listObj = Tcl_GetVar2Ex(interp, "sdc_calls", nullptr, TCL_GLOBAL_ONLY);
int listLength; int listLength;
// Get list length first std::vector<std::vector<std::string>> sdc_calls;
std::vector<std::vector<std::string>> nestedData;
if (Tcl_ListObjLength(interp, listObj, &listLength) == TCL_OK) { if (Tcl_ListObjLength(interp, listObj, &listLength) == TCL_OK) {
for (int i = 0; i < listLength; i++) { for (int i = 0; i < listLength; i++) {
Tcl_Obj* subListObj; Tcl_Obj* subListObj;
@ -314,7 +338,7 @@ void inspect_globals(Tcl_Interp* interp) {
} }
int subListLength; int subListLength;
if (Tcl_ListObjLength(interp, subListObj, &subListLength) == TCL_OK) { if (Tcl_ListObjLength(interp, subListObj, &subListLength) == TCL_OK) {
// It's a valid list - extract elements // Valid list - extract elements
for (int j = 0; j < subListLength; j++) { for (int j = 0; j < subListLength; j++) {
Tcl_Obj* elementObj; Tcl_Obj* elementObj;
if (Tcl_ListObjIndex(interp, subListObj, j, &elementObj) == TCL_OK) { if (Tcl_ListObjIndex(interp, subListObj, j, &elementObj) == TCL_OK) {
@ -323,32 +347,70 @@ void inspect_globals(Tcl_Interp* interp) {
} }
} }
} else { } else {
// It's a single element, not a list // Single element, not a list
const char* elementStr = Tcl_GetString(subListObj); const char* elementStr = Tcl_GetString(subListObj);
subList.push_back(std::string(elementStr)); subList.push_back(std::string(elementStr));
} }
nestedData.push_back(subList); sdc_calls.push_back(subList);
} }
} }
std::vector<bool> has_parent; log_assert(sdc_calls.size() == node_count);
log_assert(nestedData.size() == node_count); return sdc_calls;
has_parent.resize(node_count); }
std::vector<SdcGraphNode> build_graph(const std::vector<std::vector<std::string>>& sdc_calls) {
size_t node_count = sdc_calls.size();
std::vector<SdcGraphNode> graph(node_count);
for (size_t i = 0; i < node_count; i++) { for (size_t i = 0; i < node_count; i++) {
for (size_t j = 0; j < nestedData[i].size(); j++) { auto& new_node = graph[i];
auto arg = nestedData[i][j]; for (size_t j = 0; j < sdc_calls[i].size(); j++) {
auto pos = arg.find("YOSYS_SDC_MAGIC_NODE_"); auto arg = sdc_calls[i][j];
if (pos == std::string::npos) const std::string prefix = "YOSYS_SDC_MAGIC_NODE_";
continue; auto pos = arg.find(prefix);
std::string rest = arg.substr(pos); if (pos != std::string::npos) {
for (auto c : rest) std::string rest = arg.substr(pos + prefix.length());
if (!std::isdigit(c)) for (auto c : rest)
log_error("weird thing %s\n", rest.c_str()); if (!std::isdigit(c))
size_t arg_node_idx = std::stoi(rest); log_error("weird thing %s in %s\n", rest.c_str(), arg.c_str());
log("%zu %zu %s IDX %zu\n", i, j, arg.c_str(), arg_node_idx); size_t arg_node_idx = std::stoi(rest);
// log("%zu %zu %s\n", i, j, arg.c_str()); log_assert(arg_node_idx < graph.size());
new_node.addChild(&graph[arg_node_idx]);
} else {
new_node.addChild(arg);
}
} }
} }
return graph;
}
std::vector<bool> node_ownership(const std::vector<SdcGraphNode>& graph) {
std::vector<bool> has_parent(graph.size());
for (auto node : graph) {
for (auto child : node.children) {
if (SdcGraphNode** pp = std::get_if<SdcGraphNode*>(&child)) {
size_t idx = std::distance(&graph.front(), (const SdcGraphNode*)*pp);
log_assert(idx < has_parent.size());
has_parent[idx] = true;
}
}
}
return has_parent;
}
void dump_sdc_graph(const std::vector<SdcGraphNode>& graph, const std::vector<bool>& has_parent) {
for (size_t i = 0; i < graph.size(); i++) {
if (!has_parent[i]) {
graph[i].dump(std::cout);
std::cout << "\n";
}
}
}
void inspect_globals(Tcl_Interp* interp) {
std::vector<std::vector<std::string>> sdc_calls = gather_nested_calls(interp);
std::vector<SdcGraphNode> graph = build_graph(sdc_calls);
dump_sdc_graph(graph, node_ownership(graph));
} }
static int sdc_get_pins_cmd(ClientData data, 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[])
@ -458,31 +520,42 @@ static int sdc_get_ports_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_
} }
} }
MatchConfig config(regexp_flag, nocase_flag, false); MatchConfig config(regexp_flag, nocase_flag, false);
std::vector<std::tuple<std::string, SdcObjects::BitSelection>> resolved; std::vector<std::tuple<std::string, Wire*, SdcObjects::BitSelection>> resolved;
for (auto pat : patterns) { for (auto pat : patterns) {
bool found = false; bool found = false;
for (auto name : objects->design_ports) { for (auto [name, wire] : objects->design_ports) {
auto [does_match, matching_bits] = matches(name, pat, config); auto [does_match, matching_bits] = matches(name, pat, config);
if (does_match) { if (does_match) {
found = true; found = true;
resolved.push_back(std::make_tuple(name, matching_bits)); resolved.push_back(std::make_tuple(name, wire, matching_bits));
} }
} }
if (!found) if (!found)
log_warning("No matches in design for port %s\n", pat.c_str()); log_warning("No matches in design for port %s\n", pat.c_str());
} }
Tcl_Obj *result = Tcl_NewListObj(resolved.size(), nullptr); Tcl_Obj *result = nullptr;
for (auto [obj, matching_bits] : resolved) { for (auto [name, wire, matching_bits] : resolved) {
Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(obj.c_str(), obj.size())); 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->collect_mode != SdcObjects::CollectMode::FullConstraint) {
if (objects->constrained_ports.count(obj) == 0) { if (objects->constrained_ports.count(name) == 0) {
objects->constrained_ports[obj] = matching_bits; objects->constrained_ports[name] = matching_bits;
} else { } else {
objects->constrained_ports[obj].merge(matching_bits); objects->constrained_ports[name].merge(matching_bits);
} }
} }
} }
if (objects->value_mode == SdcObjects::ValueMode::Graph) {
return redirect_unknown(interp, objc, objv);
}
Tcl_SetObjResult(interp, result); Tcl_SetObjResult(interp, result);
return TCL_OK; return TCL_OK;
} }
@ -545,13 +618,14 @@ static int ys_track_typed_key_cmd(ClientData data, Tcl_Interp *interp, int objc,
log_error("%s: pin %s not found\n", proc_name.c_str(), str); log_error("%s: pin %s not found\n", proc_name.c_str(), str);
} else if (key_expect_type == "port") { } else if (key_expect_type == "port") {
bool found = false; bool found = false;
for (auto name : objects->design_ports) { log_error("TODO temporarily disabled due to working on a different flow\n");
if (name == str) { // for (auto [name, ] : objects->design_ports) {
found = true; // if (name == str) {
objects->constrained_ports.insert(name); // found = true;
break; // resolved, expected unique // objects->constrained_ports.insert(name);
} // break; // resolved, expected unique
} // }
// }
if (!found) if (!found)
log_error("%s: port %s not found\n", proc_name.c_str(), str); log_error("%s: port %s not found\n", proc_name.c_str(), str);
} else { } else {

View file

@ -23,11 +23,12 @@ proc unknown {args} {
if {![info exists sdc_calls]} { if {![info exists sdc_calls]} {
set sdc_calls {} set sdc_calls {}
} }
set ret "YOSYS_SDC_MAGIC_NODE_$sdc_call_index"
incr sdc_call_index incr sdc_call_index
lappend sdc_calls $args lappend sdc_calls $args
puts "unknown $args, returning YOSYS_SDC_MAGIC_NODE_$sdc_call_index" puts "unknown $args, returning YOSYS_SDC_MAGIC_NODE_$sdc_call_index"
return "YOSYS_SDC_MAGIC_NODE_$sdc_call_index" return $ret
} }
proc list {args} { proc list {args} {
unknown "list" $args return [unknown "list" {*}$args]
} }