mirror of
https://github.com/YosysHQ/yosys
synced 2025-10-09 17:31:59 +00:00
sdc: functional graph
This commit is contained in:
parent
2eb842e8b4
commit
c81145ec31
2 changed files with 129 additions and 54 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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]
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue