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"
26 #include <fmt/format.h>
30 #include <rapidjson/istreamwrapper.h>
31 #include <rapidjson/ostreamwrapper.h>
32 #include <rapidjson/prettywriter.h>
33 #include <rapidjson/rapidjson.h>
35 #include <scc/utilities.h>
38 #include <unordered_map>
39 #include <unordered_set>
49 using namespace rapidjson;
50 using writer_type = PrettyWriter<OStreamWrapper>;
54 std::string demangle(
const char* name) {
57 std::unique_ptr<char, void (*)(
void*)> res{abi::__cxa_demangle(name, NULL, NULL, &status), std::free};
58 return (status == 0) ? res.get() : name;
62 std::string demangle(
const char* name) {
return name; }
64 std::string demangle(
const char* name);
66 template <
class T> std::string
type(
const T& t) {
return demangle(
typeid(t).name()); }
68 unsigned object_counter{0};
73 std::string
const fullname;
74 std::string
const name;
75 void const* port_if{
nullptr};
77 std::string
const type;
78 std::string
const sig_name;
79 std::string
const id{fmt::format(
"{}", ++object_counter)};
82 Port(std::string
const& fullname, std::string
const& name,
void const* ptr,
bool input, std::string
const& type, Module& owner,
83 std::string
const& sig_name =
"")
94 std::string
const fullname;
95 std::string
const name;
96 std::string
const type;
98 std::string
const id{fmt::format(
"{}", ++object_counter)};
99 std::deque<std::unique_ptr<Module>> submodules;
100 std::deque<Port> ports;
102 Module(std::string
const& fullname, std::string
const& name, std::string
const& type, Module& parent)
107 Module(std::string
const& fullname, std::string
const& name, std::string
const& type)
114 const std::unordered_set<std::string> ignored_entities = {
"tlm_initiator_socket",
123 "sc_semaphore_ordered",
131 const std::unordered_set<std::string> module_entities = {
132 "sc_module",
"uvm::uvm_root",
"uvm::uvm_test",
"uvm::uvm_env",
"uvm::uvm_component",
133 "uvm::uvm_agent",
"uvm::uvm_monitor",
"uvm::uvm_scoreboard",
"uvm::uvm_driver",
"uvm::uvm_sequencer"};
135 std::string indent{
" "};
136 std::string operator*(std::string
const& str,
const unsigned int level) {
137 std::ostringstream ss;
138 for(
unsigned int i = 0; i < level; i++)
143 #if TLM_VERSION_MAJOR == 2 and TLM_VERSION_MINOR == 0
144 #if TLM_VERSION_PATCH == 6
145 #define GET_EXPORT_IF(tptr) tptr->get_base_export().get_interface()
146 #define GET_PORT_IF(tptr) tptr->get_base_port().get_interface()
147 #elif TLM_VERSION_PATCH == 5
148 #define GET_EXPORT_IF(tptr) tptr->get_export_base().get_interface()
149 #define GET_PORT_IF(tptr) tptr->get_port_base().get_interface()
151 #define NO_TLM_EXTRACT
155 std::vector<std::string> scan_object(sc_core::sc_object
const* obj, Module& currentModule,
unsigned const level) {
156 std::string name{obj->basename()};
157 if(name.substr(0, 3) ==
"$$$")
159 SCCDEBUG() << indent * level << obj->name() <<
"(" << obj->kind() <<
"), id=" << (object_counter + 1);
160 std::string kind{obj->kind()};
161 if(
auto const* mod =
dynamic_cast<sc_core::sc_module const*
>(obj)) {
162 currentModule.submodules.emplace_back(
new Module(obj->name(), name,
type(*obj), currentModule));
163 std::unordered_set<std::string> keep_outs;
164 for(
auto* child : mod->get_child_objects()) {
165 const std::string child_name{child->basename()};
166 if(child_name.substr(0, 3) ==
"$$$")
168 if(!keep_outs.empty()) {
169 auto it = std::find_if(std::begin(keep_outs), std::end(keep_outs), [&child_name](std::string
const& e) {
170 return child_name.size() > e.size() && child_name.substr(0, e.size()) == e;
172 if(it != std::end(keep_outs))
175 auto ks = scan_object(child, *currentModule.submodules.back(), level + 1);
180 }
else if(kind ==
"sc_clock") {
181 sc_core::sc_prim_channel
const* prim_chan =
dynamic_cast<sc_core::sc_prim_channel const*
>(obj);
182 currentModule.submodules.emplace_back(
new Module(obj->name(), name,
type(*obj), currentModule));
183 currentModule.submodules.back()->ports.push_back(Port(std::string(obj->name()) +
"." + name, name, prim_chan,
false, obj->kind(),
184 *currentModule.submodules.back(), obj->basename()));
185 #ifndef NO_TLM_EXTRACT
187 }
else if(
auto const* tptr =
dynamic_cast<tlm::tlm_base_socket_if const*
>(obj)) {
188 auto cat = tptr->get_socket_category();
189 bool input = (cat & tlm::TLM_TARGET_SOCKET) == tlm::TLM_TARGET_SOCKET;
191 currentModule.ports.push_back(Port(obj->name(), name, GET_EXPORT_IF(tptr), input, kind, currentModule));
192 return {name +
"_port", name +
"_port_0"};
194 currentModule.ports.push_back(Port(obj->name(), name, GET_PORT_IF(tptr), input, kind, currentModule));
195 return {name +
"_export", name +
"_export_0"};
199 }
else if(
auto const* optr =
dynamic_cast<sc_core::sc_port_base const*
>(obj)) {
200 sc_core::sc_interface
const* if_ptr = optr->get_interface();
201 sc_core::sc_prim_channel
const* if_obj =
dynamic_cast<sc_core::sc_prim_channel const*
>(if_ptr);
202 bool is_input = kind ==
"sc_in" || kind ==
"sc_fifo_in";
203 currentModule.ports.push_back(Port(obj->name(), name, if_obj ?
static_cast<void const*
>(if_obj) :
static_cast<void const*
>(if_ptr),
204 is_input, obj->kind(), currentModule, if_obj ? if_obj->basename() :
""));
205 }
else if(
auto const* optr =
dynamic_cast<sc_core::sc_export_base const*
>(obj)) {
206 sc_core::sc_interface
const* pointer = optr->get_interface();
207 currentModule.ports.push_back(Port(obj->name(), name, pointer,
true, obj->kind(), currentModule));
208 #if defined(RECORD_UVM_ANALYSIS)
209 }
else if(kind ==
"sc_object" &&
dynamic_cast<sc_core::sc_interface const*
>(obj)) {
210 auto const* ifptr =
dynamic_cast<sc_core::sc_interface const*
>(obj);
211 currentModule.ports.push_back(Port(obj->name(), name, ifptr,
false, obj->kind(), currentModule));
213 }
else if(ignored_entities.find(kind) == ignored_entities.end()) {
214 SCCWARN() <<
"object not known (" << kind <<
")";
219 using stack_t = std::deque<Module const*>;
220 using registry_t = std::unordered_map<void const*, std::vector<Port*>>;
222 void collect_ports_rec(Module& m, registry_t& registry) {
223 for(
auto& port : m.ports)
224 registry[port.port_if].emplace_back(&port);
225 for(
auto& child : m.submodules)
226 collect_ports_rec(*child, registry);
229 using breadcrumb_t = std::tuple<Module*, bool>;
230 bool get_path_to(Module
const* i, std::vector<breadcrumb_t>& bread_crumb, std::unordered_set<Module*>& visited) {
231 auto current_mod = std::get<0>(bread_crumb.back());
232 bool upwards = std::get<1>(bread_crumb.back());
235 if(visited.count(current_mod))
237 visited.insert(current_mod);
238 for(
auto& c : current_mod->submodules) {
239 bread_crumb.emplace_back(c.get(),
false);
240 if(get_path_to(i, bread_crumb, visited))
242 bread_crumb.pop_back();
244 if(upwards && current_mod->parent) {
245 bread_crumb.emplace_back(current_mod->parent,
true);
246 if(get_path_to(i, bread_crumb, visited))
248 bread_crumb.pop_back();
253 void infer_implicit_ports(Module& m) {
255 collect_ports_rec(m, registry);
256 for(
auto& entry : registry) {
257 auto& ports = entry.second;
258 if(ports.size() > 1) {
259 auto o = std::find_if(std::begin(ports), std::end(ports), [](Port
const* p) ->
bool {
return !p->input; });
260 auto i = std::find_if(std::begin(ports), std::end(ports), [](Port
const* p) ->
bool {
return p->input; });
261 while(o != std::end(ports)) {
262 Port* start_port = *o;
263 Module* start_mod = start_port->owner;
264 while(i != std::end(ports)) {
266 std::vector<breadcrumb_t> bread_crumb{std::make_tuple(start_mod,
true)};
267 std::unordered_set<Module*> visited;
268 if(get_path_to(end_port->owner, bread_crumb, visited) && bread_crumb.size() > 1) {
269 void const* port_if = end_port->port_if;
270 auto last_upwards =
false;
271 while(bread_crumb.size() > 1) {
272 auto mod = std::get<0>(bread_crumb.back());
273 auto upwards = std::get<1>(bread_crumb.back());
274 auto port_iter = std::find_if(std::begin(mod->ports), std::end(mod->ports),
275 [port_if](Port
const& p) ->
bool { return p.port_if == port_if; });
276 if(port_iter == std::end(mod->ports) && upwards == last_upwards) {
277 std::ostringstream oss;
278 auto ref_port = upwards ? start_port : end_port;
279 oss << mod->fullname <<
"." << ref_port->name;
280 mod->ports.push_back(Port(oss.str(), ref_port->name, port_if, !upwards, ref_port->type, *mod));
282 last_upwards = upwards;
283 bread_crumb.pop_back();
293 void generate_elk(std::ostream& e, Module
const& module,
unsigned level = 0) {
294 SCCDEBUG() << module.name;
295 unsigned num_in{0}, num_out{0};
296 for(
auto& port : module.ports)
301 if(!module.ports.size() && !module.submodules.size())
303 e << indent * level <<
"node " << module.name <<
" {"
306 e << indent * level <<
"layout [ size: 50, " << std::max(80U, std::max(num_in, num_out) * 20) <<
" ]\n";
307 e << indent * level <<
"portConstraints: FIXED_SIDE\n";
308 e << indent * level <<
"label \"" << module.name <<
"\"\n";
310 for(
auto& port : module.ports) {
311 SCCDEBUG() <<
" " << port.name <<
"\n";
312 auto side = port.input ?
"WEST" :
"EAST";
313 e << indent * level <<
"port " << port.name <<
" { ^port.side: " << side <<
" label '" << port.name <<
"' }\n";
316 for(
auto& m : module.submodules)
317 generate_elk(e, *m, level);
319 for(
auto& srcport : module.ports) {
321 for(
auto& tgtmod : module.submodules) {
322 for(
auto& tgtport : tgtmod->ports) {
323 if(tgtport.port_if == srcport.port_if)
324 e << indent * level <<
"edge " << srcport.fullname <<
" -> " << tgtport.fullname <<
"\n";
329 for(
auto& srcmod : module.submodules) {
330 for(
auto& srcport : srcmod->ports) {
331 if(!srcport.input && srcport.port_if)
332 for(
auto& tgtmod : module.submodules) {
333 for(
auto& tgtport : tgtmod->ports) {
334 if(srcmod->fullname == tgtmod->fullname && tgtport.fullname == srcport.fullname)
336 if(tgtport.port_if == srcport.port_if && tgtport.input)
337 e << indent * level <<
"edge " << srcport.fullname <<
" -> " << tgtport.fullname <<
"\n";
343 e << indent * level <<
"}\n"
347 void generate_port_json(writer_type& writer, hierarchy_dumper::file_type type,
const scc::Port& p) {
348 writer.StartObject();
351 writer.String(p.id.c_str());
352 if(type != hierarchy_dumper::D3JSON) {
353 writer.Key(
"labels");
356 writer.StartObject();
359 writer.String(p.name.c_str());
366 writer.Key(
"height");
368 writer.Key(
"layoutOptions");
369 writer.StartObject();
371 writer.Key(
"port.side");
372 writer.String(p.input ?
"WEST" :
"EAST");
375 if(type == hierarchy_dumper::DBGJSON) {
377 writer.String(p.type.c_str());
379 writer.Bool(p.input);
380 writer.Key(
"interface");
381 writer.Uint64(
reinterpret_cast<uintptr_t
>(p.port_if));
384 writer.Key(
"direction");
385 writer.String(p.input ?
"INPUT" :
"OUTPUT");
386 writer.Key(
"hwMeta");
387 writer.StartObject();
390 writer.String(p.name.c_str());
393 writer.Key(
"connectedAsParent");
397 writer.Key(
"properties");
398 writer.StartObject();
401 writer.String(p.input ?
"WEST" :
"EAST");
405 writer.Key(
"children");
413 void generate_edge_json(writer_type& writer,
const scc::Port& srcport,
const scc::Port& tgtport) {
414 writer.StartObject();
416 auto edge_name = fmt::format(
"{}", ++object_counter);
418 writer.String(edge_name.c_str());
419 writer.Key(
"sources");
421 { writer.String(srcport.id.c_str()); }
423 writer.Key(
"targets");
425 { writer.String(tgtport.id.c_str()); }
431 void generate_edge_d3_json(writer_type& writer,
const scc::Module& srcmod,
const scc::Port& srcport,
const scc::Module& tgtmod,
432 const scc::Port& tgtport) {
433 writer.StartObject();
435 auto edge_name = fmt::format(
"{}", ++object_counter);
437 writer.String(edge_name.c_str());
438 writer.Key(
"source");
439 writer.String(srcmod.id.c_str());
440 writer.Key(
"sourcePort");
441 writer.String(srcport.id.c_str());
442 writer.Key(
"target");
443 writer.String(tgtmod.id.c_str());
444 writer.Key(
"targetPort");
445 writer.String(tgtport.id.c_str());
446 writer.Key(
"hwMeta");
447 writer.StartObject();
449 if(srcport.sig_name.size()) {
451 writer.String(srcport.sig_name.c_str());
452 }
else if(tgtport.sig_name.size()) {
454 writer.String(tgtport.sig_name.c_str());
457 writer.String(fmt::format(
"{}_to_{}", srcport.name, tgtport.name).c_str());
467 void generate_mod_json(writer_type& writer, hierarchy_dumper::file_type type, Module
const& module,
unsigned level = 0) {
468 unsigned num_in{0}, num_out{0};
469 for(
auto& port : module.ports)
474 writer.StartObject();
477 writer.String(module.id.c_str());
482 for(
auto& p : module.ports)
483 generate_port_json(writer, type, p);
487 if(type == hierarchy_dumper::D3JSON && module.parent)
488 writer.Key(
"_children");
490 writer.Key(
"children");
493 for(
auto& c : module.submodules)
494 generate_mod_json(writer, type, *c, level * 1);
498 if(type == hierarchy_dumper::D3JSON && module.parent)
499 writer.Key(
"_edges");
505 for(
auto& srcport : module.ports) {
506 if(srcport.port_if) {
507 for(
auto& tgtmod : module.submodules) {
508 for(
auto& tgtport : tgtmod->ports) {
509 if(tgtport.port_if == srcport.port_if) {
510 if(type == hierarchy_dumper::D3JSON) {
511 generate_edge_d3_json(writer, module, srcport, *tgtmod, tgtport);
513 generate_edge_json(writer, srcport, tgtport);
521 for(
auto& srcmod : module.submodules) {
522 for(
auto& srcport : srcmod->ports) {
523 if(!srcport.input && srcport.port_if) {
524 for(
auto& tgtmod : module.submodules) {
525 for(
auto& tgtport : tgtmod->ports) {
526 if(srcmod->fullname == tgtmod->fullname && tgtport.fullname == srcport.fullname) {
529 if(tgtport.port_if == srcport.port_if && tgtport.input) {
530 if(type == hierarchy_dumper::D3JSON) {
531 generate_edge_d3_json(writer, *srcmod, srcport, *tgtmod, tgtport);
533 generate_edge_json(writer, srcport, tgtport);
543 if(type != hierarchy_dumper::D3JSON) {
544 writer.Key(
"labels");
547 writer.StartObject();
550 writer.String(module.name.c_str());
557 writer.Key(
"height");
558 writer.Uint(std::max(80U, std::max(num_in, num_out) * 20));
559 if(type == hierarchy_dumper::DBGJSON) {
561 writer.String(module.name.c_str());
563 writer.String(module.type.c_str());
564 writer.Key(
"topmodule");
565 writer.Bool(!module.parent);
568 writer.Key(
"hwMeta");
569 writer.StartObject();
572 writer.String(module.name.c_str());
574 writer.String(module.type.c_str());
577 writer.Uint(object_counter);
578 writer.Key(
"isExternalPort");
584 writer.Key(
"properties");
585 writer.StartObject();
587 writer.Key(
"org.eclipse.elk.layered.mergeEdges");
589 writer.Key(
"org.eclipse.elk.portConstraints");
590 writer.String(
"FIXED_SIDE");
598 std::unique_ptr<Module> scan_object_tree() {
599 std::vector<sc_core::sc_object*> obja = sc_core::sc_get_top_level_objects();
600 if(obja.size() == 1 && std::string(obja[0]->kind()) ==
"sc_module" && std::string(obja[0]->basename()).substr(0, 3) !=
"$$$") {
601 SCCDEBUG() << obja[0]->name() <<
"(" << obja[0]->kind() <<
")";
602 auto topModule = scc::make_unique<Module>(obja[0]->name(), obja[0]->basename(),
type(*obja[0]));
603 for(
auto* child : obja[0]->get_child_objects())
604 scan_object(child, *topModule, 1);
607 SCCDEBUG() <<
"sc_main ( function sc_main() )";
608 auto topModule = scc::make_unique<Module>(
"sc_main",
"sc_main",
"sc_main()");
609 for(
auto* child : obja)
610 scan_object(child, *topModule, 1);
614 void dump_structure(std::ostream& e, hierarchy_dumper::file_type format) {
615 auto topModule = scan_object_tree();
616 infer_implicit_ports(*topModule);
617 if(format == hierarchy_dumper::ELKT) {
618 e <<
"algorithm: org.eclipse.elk.layered\n";
619 e <<
"edgeRouting: ORTHOGONAL\n";
620 generate_elk(e, *topModule);
621 SCCINFO() <<
"SystemC Structure Dumped to ELK file";
623 OStreamWrapper stream(e);
624 writer_type writer(stream);
625 writer.StartObject();
627 auto elems =
util::split(sc_core::sc_argv()[0],
'/');
630 writer.Key(
"labels");
633 writer.StartObject();
636 writer.String(elems[elems.size() - 1].c_str());
641 writer.Key(
"layoutOptions");
642 writer.StartObject();
644 writer.Key(
"algorithm");
645 writer.String(
"layered");
648 writer.Key(
"children");
650 { generate_mod_json(writer, format, *topModule); }
655 if(format == hierarchy_dumper::D3JSON) {
656 writer.Key(
"hwMeta");
657 writer.StartObject();
664 writer.String(elems[elems.size() - 1].c_str());
667 writer.Key(
"properties");
668 writer.StartObject();
670 writer.Key(
"org.eclipse.elk.layered.mergeEdges");
672 writer.Key(
"org.eclipse.elk.portConstraints");
673 writer.String(
"FIXED_ORDER");
679 SCCINFO() <<
"SystemC Structure Dumped to JSON file";
684 hierarchy_dumper::hierarchy_dumper(
const std::string& filename, file_type format)
685 : sc_core::sc_module(sc_core::sc_module_name(
"$$$hierarchy_dumper$$$"))
686 , dump_hier_file_name{filename}
687 , dump_format{format} {}
689 hierarchy_dumper::~hierarchy_dumper() {}
691 void hierarchy_dumper::start_of_simulation() {
692 if(dump_hier_file_name.size()) {
693 std::ofstream of{dump_hier_file_name};
695 dump_structure(of, dump_format);
std::vector< std::string > split(const std::string &s, char separator)