17 #ifndef SC_INCLUDE_DYNAMIC_PROCESSES
18 #define SC_INCLUDE_DYNAMIC_PROCESSES
20 #include "hierarchy_dumper.h"
21 #include "configurer.h"
22 #include "perf_estimator.h"
29 #include <rapidjson/istreamwrapper.h>
30 #include <rapidjson/ostreamwrapper.h>
31 #include <rapidjson/prettywriter.h>
32 #include <rapidjson/rapidjson.h>
34 #include <scc/utilities.h>
37 #include <unordered_map>
38 #include <unordered_set>
39 #ifdef FMT_SPDLOG_INTERNAL
42 #include <fmt/format.h>
53 using namespace rapidjson;
54 using writer_type = PrettyWriter<OStreamWrapper>;
58 std::string demangle(
const char* name) {
61 std::unique_ptr<char, void (*)(
void*)> res{abi::__cxa_demangle(name, NULL, NULL, &status), std::free};
62 return (status == 0) ? res.get() : name;
66 std::string demangle(
const char* name) {
return name; }
68 std::string demangle(
const char* name);
70 template <
class T> std::string
type(
const T& t) {
return demangle(
typeid(t).name()); }
72 unsigned object_counter{0};
77 std::string
const fullname;
78 std::string
const name;
79 void const* port_if{
nullptr};
81 std::string
const type;
82 std::string
const sig_name;
83 std::string
const id{fmt::format(
"{}", ++object_counter)};
86 Port(std::string
const& fullname, std::string
const& name,
void const* ptr,
bool input, std::string
const& type, Module& owner,
87 std::string
const& sig_name =
"")
98 std::string
const fullname;
99 std::string
const name;
100 std::string
const type;
101 Module*
const parent;
102 std::string
const id{fmt::format(
"{}", ++object_counter)};
103 std::deque<Module> submodules;
104 std::deque<Port> ports;
106 Module(std::string
const& fullname, std::string
const& name, std::string
const& type, Module& parent)
111 Module(std::string
const& fullname, std::string
const& name, std::string
const& type)
118 const std::unordered_set<std::string> ignored_entities = {
"tlm_initiator_socket",
127 "sc_semaphore_ordered",
135 const std::unordered_set<std::string> module_entities = {
136 "sc_module",
"uvm::uvm_root",
"uvm::uvm_test",
"uvm::uvm_env",
"uvm::uvm_component",
137 "uvm::uvm_agent",
"uvm::uvm_monitor",
"uvm::uvm_scoreboard",
"uvm::uvm_driver",
"uvm::uvm_sequencer"};
139 std::string indent{
" "};
140 std::string operator*(std::string
const& str,
const unsigned int level) {
141 std::ostringstream ss;
142 for(
unsigned int i = 0; i < level; i++)
147 #if TLM_VERSION_MAJOR == 2 and TLM_VERSION_MINOR == 0
148 #if TLM_VERSION_PATCH == 6
149 #define GET_EXPORT_IF(tptr) tptr->get_base_export().get_interface()
150 #define GET_PORT_IF(tptr) tptr->get_base_port().get_interface()
151 #elif TLM_VERSION_PATCH == 5
152 #define GET_EXPORT_IF(tptr) tptr->get_export_base().get_interface()
153 #define GET_PORT_IF(tptr) tptr->get_port_base().get_interface()
155 #define NO_TLM_EXTRACT
159 std::vector<std::string> scan_object(sc_core::sc_object
const* obj, Module& currentModule,
unsigned const level) {
160 std::string name{obj->basename()};
161 if(name.substr(0, 3) ==
"$$$")
163 SCCDEBUG() << indent * level << obj->name() <<
"(" << obj->kind() <<
"), id=" << (object_counter + 1);
164 std::string kind{obj->kind()};
165 if(
auto const* mod =
dynamic_cast<sc_core::sc_module const*
>(obj)) {
166 currentModule.submodules.push_back(Module(obj->name(), name,
type(*obj), currentModule));
167 std::unordered_set<std::string> keep_outs;
168 for(
auto* child : mod->get_child_objects()) {
169 const std::string child_name{child->basename()};
170 if(child_name.substr(0, 3) ==
"$$$")
172 if(!keep_outs.empty()) {
173 auto it = std::find_if(std::begin(keep_outs), std::end(keep_outs), [&child_name](std::string
const& e) {
174 return child_name.size() > e.size() && child_name.substr(0, e.size()) == e;
176 if(it != std::end(keep_outs))
179 auto ks = scan_object(child, currentModule.submodules.back(), level + 1);
184 }
else if(kind ==
"sc_clock") {
185 sc_core::sc_prim_channel
const* prim_chan =
dynamic_cast<sc_core::sc_prim_channel const*
>(obj);
186 currentModule.submodules.push_back(Module(obj->name(), name,
type(*obj), currentModule));
187 currentModule.submodules.back().ports.push_back(Port(std::string(obj->name()) +
"." + name, name, prim_chan,
false, obj->kind(),
188 currentModule.submodules.back(), obj->basename()));
189 #ifndef NO_TLM_EXTRACT
191 }
else if(
auto const* tptr =
dynamic_cast<tlm::tlm_base_socket_if const*
>(obj)) {
192 auto cat = tptr->get_socket_category();
193 bool input = (cat & tlm::TLM_TARGET_SOCKET) == tlm::TLM_TARGET_SOCKET;
195 currentModule.ports.push_back(Port(obj->name(), name, GET_EXPORT_IF(tptr), input, kind, currentModule));
196 return {name +
"_port", name +
"_port_0"};
198 currentModule.ports.push_back(Port(obj->name(), name, GET_PORT_IF(tptr), input, kind, currentModule));
199 return {name +
"_export", name +
"_export_0"};
203 }
else if(
auto const* optr =
dynamic_cast<sc_core::sc_port_base const*
>(obj)) {
204 sc_core::sc_interface
const* if_ptr = optr->get_interface();
205 sc_core::sc_prim_channel
const* if_obj =
dynamic_cast<sc_core::sc_prim_channel const*
>(if_ptr);
206 bool is_input = kind ==
"sc_in" || kind ==
"sc_fifo_in";
207 currentModule.ports.push_back(Port(obj->name(), name, if_obj ?
static_cast<void const*
>(if_obj) :
static_cast<void const*
>(if_ptr),
208 is_input, obj->kind(), currentModule, if_obj ? if_obj->basename() :
""));
209 }
else if(
auto const* optr =
dynamic_cast<sc_core::sc_export_base const*
>(obj)) {
210 sc_core::sc_interface
const* pointer = optr->get_interface();
211 currentModule.ports.push_back(Port(obj->name(), name, pointer,
true, obj->kind(), currentModule));
212 #if defined(RECORD_UVM_ANALYSIS)
213 }
else if(kind ==
"sc_object" &&
dynamic_cast<sc_core::sc_interface const*
>(obj)) {
214 auto const* ifptr =
dynamic_cast<sc_core::sc_interface const*
>(obj);
215 currentModule.ports.push_back(Port(obj->name(), name, ifptr,
false, obj->kind(), currentModule));
217 }
else if(ignored_entities.find(kind) == ignored_entities.end()) {
218 SCCWARN() <<
"object not known (" << kind <<
")";
223 using stack_t = std::deque<Module const*>;
224 using registry_t = std::unordered_map<void const*, std::vector<Port*>>;
226 void collect_ports_rec(Module& m, registry_t& registry) {
227 for(
auto& port : m.ports)
228 registry[port.port_if].emplace_back(&port);
229 for(
auto& child : m.submodules)
230 collect_ports_rec(child, registry);
233 using breadcrumb_t = std::tuple<Module*, bool>;
234 bool get_path_to(Module
const* i, std::vector<breadcrumb_t>& bread_crumb, std::unordered_set<Module*>& visited) {
235 auto current_mod = std::get<0>(bread_crumb.back());
236 bool upwards = std::get<1>(bread_crumb.back());
239 if(visited.count(current_mod))
241 visited.insert(current_mod);
242 for(
auto& c : current_mod->submodules) {
243 bread_crumb.emplace_back(&c,
false);
244 if(get_path_to(i, bread_crumb, visited))
246 bread_crumb.pop_back();
248 if(upwards && current_mod->parent) {
249 bread_crumb.emplace_back(current_mod->parent,
true);
250 if(get_path_to(i, bread_crumb, visited))
252 bread_crumb.pop_back();
257 void infer_implicit_ports(Module& m) {
259 collect_ports_rec(m, registry);
260 for(
auto& entry : registry) {
261 auto& ports = entry.second;
262 if(ports.size() > 1) {
263 auto o = std::find_if(std::begin(ports), std::end(ports), [](Port
const* p) ->
bool {
return !p->input; });
264 auto i = std::find_if(std::begin(ports), std::end(ports), [](Port
const* p) ->
bool {
return p->input; });
265 while(o != std::end(ports)) {
266 Port* start_port = *o;
267 Module* start_mod = start_port->owner;
268 while(i != std::end(ports)) {
270 std::vector<breadcrumb_t> bread_crumb{std::make_tuple(start_mod,
true)};
271 std::unordered_set<Module*> visited;
272 if(get_path_to(end_port->owner, bread_crumb, visited) && bread_crumb.size() > 1) {
273 void const* port_if = end_port->port_if;
274 auto last_upwards =
false;
275 while(bread_crumb.size() > 1) {
276 auto mod = std::get<0>(bread_crumb.back());
277 auto upwards = std::get<1>(bread_crumb.back());
278 auto port_iter = std::find_if(std::begin(mod->ports), std::end(mod->ports),
279 [port_if](Port
const& p) ->
bool { return p.port_if == port_if; });
280 if(port_iter == std::end(mod->ports) && upwards == last_upwards) {
281 std::ostringstream oss;
282 auto ref_port = upwards ? start_port : end_port;
283 oss << mod->fullname <<
"." << ref_port->name;
284 mod->ports.push_back(Port(oss.str(), ref_port->name, port_if, !upwards, ref_port->type, *mod));
286 last_upwards = upwards;
287 bread_crumb.pop_back();
297 void generate_elk(std::ostream& e, Module
const& module,
unsigned level = 0) {
298 SCCDEBUG() << module.name;
299 unsigned num_in{0}, num_out{0};
300 for(
auto& port : module.ports)
305 if(!module.ports.size() && !module.submodules.size())
307 e << indent * level <<
"node " << module.name <<
" {"
310 e << indent * level <<
"layout [ size: 50, " << std::max(80U, std::max(num_in, num_out) * 20) <<
" ]\n";
311 e << indent * level <<
"portConstraints: FIXED_SIDE\n";
312 e << indent * level <<
"label \"" << module.name <<
"\"\n";
314 for(
auto& port : module.ports) {
315 SCCDEBUG() <<
" " << port.name <<
"\n";
316 auto side = port.input ?
"WEST" :
"EAST";
317 e << indent * level <<
"port " << port.name <<
" { ^port.side: " << side <<
" label '" << port.name <<
"' }\n";
320 for(
auto& m : module.submodules)
321 generate_elk(e, m, level);
323 for(
auto& srcport : module.ports) {
325 for(
auto& tgtmod : module.submodules) {
326 for(
auto& tgtport : tgtmod.ports) {
327 if(tgtport.port_if == srcport.port_if)
328 e << indent * level <<
"edge " << srcport.fullname <<
" -> " << tgtport.fullname <<
"\n";
333 for(
auto& srcmod : module.submodules) {
334 for(
auto& srcport : srcmod.ports) {
335 if(!srcport.input && srcport.port_if)
336 for(
auto& tgtmod : module.submodules) {
337 for(
auto& tgtport : tgtmod.ports) {
338 if(srcmod.fullname == tgtmod.fullname && tgtport.fullname == srcport.fullname)
340 if(tgtport.port_if == srcport.port_if && tgtport.input)
341 e << indent * level <<
"edge " << srcport.fullname <<
" -> " << tgtport.fullname <<
"\n";
347 e << indent * level <<
"}\n"
351 void generate_port_json(writer_type& writer, hierarchy_dumper::file_type type,
const scc::Port& p) {
352 writer.StartObject();
355 writer.String(p.id.c_str());
356 if(type != hierarchy_dumper::D3JSON) {
357 writer.Key(
"labels");
360 writer.StartObject();
363 writer.String(p.name.c_str());
370 writer.Key(
"height");
372 writer.Key(
"layoutOptions");
373 writer.StartObject();
375 writer.Key(
"port.side");
376 writer.String(p.input ?
"WEST" :
"EAST");
379 if(type == hierarchy_dumper::DBGJSON) {
381 writer.String(p.type.c_str());
383 writer.Bool(p.input);
384 writer.Key(
"interface");
385 writer.Uint64(
reinterpret_cast<uintptr_t
>(p.port_if));
388 writer.Key(
"direction");
389 writer.String(p.input ?
"INPUT" :
"OUTPUT");
390 writer.Key(
"hwMeta");
391 writer.StartObject();
394 writer.String(p.name.c_str());
397 writer.Key(
"connectedAsParent");
401 writer.Key(
"properties");
402 writer.StartObject();
405 writer.String(p.input ?
"WEST" :
"EAST");
409 writer.Key(
"children");
417 void generate_edge_json(writer_type& writer,
const scc::Port& srcport,
const scc::Port& tgtport) {
418 writer.StartObject();
420 auto edge_name = fmt::format(
"{}", ++object_counter);
422 writer.String(edge_name.c_str());
423 writer.Key(
"sources");
425 { writer.String(srcport.id.c_str()); }
427 writer.Key(
"targets");
429 { writer.String(tgtport.id.c_str()); }
435 void generate_edge_d3_json(writer_type& writer,
const scc::Module& srcmod,
const scc::Port& srcport,
const scc::Module& tgtmod,
436 const scc::Port& tgtport) {
437 writer.StartObject();
439 auto edge_name = fmt::format(
"{}", ++object_counter);
441 writer.String(edge_name.c_str());
442 writer.Key(
"source");
443 writer.String(srcmod.id.c_str());
444 writer.Key(
"sourcePort");
445 writer.String(srcport.id.c_str());
446 writer.Key(
"target");
447 writer.String(tgtmod.id.c_str());
448 writer.Key(
"targetPort");
449 writer.String(tgtport.id.c_str());
450 writer.Key(
"hwMeta");
451 writer.StartObject();
453 if(srcport.sig_name.size()) {
455 writer.String(srcport.sig_name.c_str());
456 }
else if(tgtport.sig_name.size()) {
458 writer.String(tgtport.sig_name.c_str());
461 writer.String(fmt::format(
"{}_to_{}", srcport.name, tgtport.name).c_str());
471 void generate_mod_json(writer_type& writer, hierarchy_dumper::file_type type, Module
const& module,
unsigned level = 0) {
472 unsigned num_in{0}, num_out{0};
473 for(
auto& port : module.ports)
478 writer.StartObject();
481 writer.String(module.id.c_str());
486 for(
auto& p : module.ports)
487 generate_port_json(writer, type, p);
491 if(type == hierarchy_dumper::D3JSON && module.parent)
492 writer.Key(
"_children");
494 writer.Key(
"children");
497 for(
auto& c : module.submodules)
498 generate_mod_json(writer, type, c, level * 1);
502 if(type == hierarchy_dumper::D3JSON && module.parent)
503 writer.Key(
"_edges");
509 for(
auto& srcport : module.ports) {
510 if(srcport.port_if) {
511 for(
auto& tgtmod : module.submodules) {
512 for(
auto& tgtport : tgtmod.ports) {
513 if(tgtport.port_if == srcport.port_if)
514 if(type == hierarchy_dumper::D3JSON)
515 generate_edge_d3_json(writer, module, srcport, tgtmod, tgtport);
517 generate_edge_json(writer, srcport, tgtport);
523 for(
auto& srcmod : module.submodules) {
524 for(
auto& srcport : srcmod.ports) {
525 if(!srcport.input && srcport.port_if) {
526 for(
auto& tgtmod : module.submodules) {
527 for(
auto& tgtport : tgtmod.ports) {
528 if(srcmod.fullname == tgtmod.fullname && tgtport.fullname == srcport.fullname)
530 if(tgtport.port_if == srcport.port_if && tgtport.input)
531 if(type == hierarchy_dumper::D3JSON)
532 generate_edge_d3_json(writer, srcmod, srcport, tgtmod, tgtport);
534 generate_edge_json(writer, srcport, tgtport);
542 if(type != hierarchy_dumper::D3JSON) {
543 writer.Key(
"labels");
546 writer.StartObject();
549 writer.String(module.name.c_str());
556 writer.Key(
"height");
557 writer.Uint(std::max(80U, std::max(num_in, num_out) * 20));
558 if(type == hierarchy_dumper::DBGJSON) {
560 writer.String(module.name.c_str());
562 writer.String(module.type.c_str());
563 writer.Key(
"topmodule");
564 writer.Bool(!module.parent);
567 writer.Key(
"hwMeta");
568 writer.StartObject();
571 writer.String(module.name.c_str());
573 writer.String(module.type.c_str());
576 writer.Uint(object_counter);
577 writer.Key(
"isExternalPort");
583 writer.Key(
"properties");
584 writer.StartObject();
586 writer.Key(
"org.eclipse.elk.layered.mergeEdges");
588 writer.Key(
"org.eclipse.elk.portConstraints");
589 writer.String(
"FIXED_SIDE");
597 std::unique_ptr<Module> scan_object_tree() {
598 std::vector<sc_core::sc_object*> obja = sc_core::sc_get_top_level_objects();
599 if(obja.size() == 1 && std::string(obja[0]->kind()) ==
"sc_module" && std::string(obja[0]->basename()).substr(0, 3) !=
"$$$") {
600 SCCDEBUG() << obja[0]->name() <<
"(" << obja[0]->kind() <<
")";
601 auto topModule = scc::make_unique<Module>(obja[0]->name(), obja[0]->basename(),
type(*obja[0]));
602 for(
auto* child : obja[0]->get_child_objects())
603 scan_object(child, *topModule, 1);
606 SCCDEBUG() <<
"sc_main ( function sc_main() )";
607 auto topModule = scc::make_unique<Module>(
"sc_main",
"sc_main",
"sc_main()");
608 for(
auto* child : obja)
609 scan_object(child, *topModule, 1);
613 void dump_structure(std::ostream& e, hierarchy_dumper::file_type format) {
614 auto topModule = scan_object_tree();
615 infer_implicit_ports(*topModule);
616 if(format == hierarchy_dumper::ELKT) {
617 e <<
"algorithm: org.eclipse.elk.layered\n";
618 e <<
"edgeRouting: ORTHOGONAL\n";
619 generate_elk(e, *topModule);
620 SCCINFO() <<
"SystemC Structure Dumped to ELK file";
622 OStreamWrapper stream(e);
623 writer_type writer(stream);
624 writer.StartObject();
626 auto elems =
util::split(sc_core::sc_argv()[0],
'/');
629 writer.Key(
"labels");
632 writer.StartObject();
635 writer.String(elems[elems.size() - 1].c_str());
640 writer.Key(
"layoutOptions");
641 writer.StartObject();
643 writer.Key(
"algorithm");
644 writer.String(
"layered");
647 writer.Key(
"children");
649 { generate_mod_json(writer, format, *topModule); }
654 if(format == hierarchy_dumper::D3JSON) {
655 writer.Key(
"hwMeta");
656 writer.StartObject();
663 writer.String(elems[elems.size() - 1].c_str());
666 writer.Key(
"properties");
667 writer.StartObject();
669 writer.Key(
"org.eclipse.elk.layered.mergeEdges");
671 writer.Key(
"org.eclipse.elk.portConstraints");
672 writer.String(
"FIXED_ORDER");
678 SCCINFO() <<
"SystemC Structure Dumped to JSON file";
683 hierarchy_dumper::hierarchy_dumper(
const std::string& filename, file_type format)
684 : sc_core::sc_module(sc_core::sc_module_name(
"$$$hierarchy_dumper$$$"))
685 , dump_hier_file_name{filename}
686 , dump_format{format} {}
688 hierarchy_dumper::~hierarchy_dumper() {}
690 void hierarchy_dumper::start_of_simulation() {
691 if(dump_hier_file_name.size()) {
692 std::ofstream of{dump_hier_file_name};
694 dump_structure(of, dump_format);
std::vector< std::string > split(const std::string &s, char separator)