scc 2025.09
SystemC components library
hierarchy_dumper.cpp
1/*******************************************************************************
2 * Copyright 2022 MINRES Technologies GmbH
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *******************************************************************************/
16
17#ifndef SC_INCLUDE_DYNAMIC_PROCESSES
18#define SC_INCLUDE_DYNAMIC_PROCESSES
19#endif
20#include "hierarchy_dumper.h"
21#include "configurer.h"
22#include "perf_estimator.h"
23#include "report.h"
24#include "tracer.h"
25#include <deque>
26#include <fmt/format.h>
27#include <fstream>
28#include <iostream>
29#include <memory>
30#include <rapidjson/istreamwrapper.h>
31#include <rapidjson/ostreamwrapper.h>
32#include <rapidjson/prettywriter.h>
33#include <rapidjson/rapidjson.h>
34#include <regex>
35#include <scc/utilities.h>
36#include <sstream>
37#include <tlm>
38#include <unordered_map>
39#include <unordered_set>
40
41#include <string>
42#include <typeinfo>
43#ifdef __GNUG__
44#include <cstdlib>
45#include <cxxabi.h>
46#endif
47
48namespace scc {
49using namespace rapidjson;
50using writer_type = PrettyWriter<OStreamWrapper>;
51
52namespace {
53#ifdef __GNUG__
54std::string demangle(const char* name) {
55 int status = -4; // some arbitrary value to eliminate the compiler warning
56 // enable c++11 by passing the flag -std=c++11 to g++
57 std::unique_ptr<char, void (*)(void*)> res{abi::__cxa_demangle(name, NULL, NULL, &status), std::free};
58 return (status == 0) ? res.get() : name;
59}
60#else
61// does nothing if not g++
62std::string demangle(const char* name) { return name; }
63#endif
64std::string demangle(const char* name);
65
66template <class T> std::string type(const T& t) { return demangle(typeid(t).name()); }
67
68unsigned object_counter{0};
69
70struct Module;
71
72struct Port {
73 std::string const fullname;
74 std::string const name;
75 void const* port_if{nullptr};
76 bool input{false};
77 std::string const type;
78 std::string const sig_name;
79 std::string const id{fmt::format("{}", ++object_counter)};
80 Module* const owner;
81
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 = "")
84 : fullname(fullname)
85 , name(name)
86 , port_if(ptr)
87 , input(input)
88 , type(type)
89 , sig_name(sig_name)
90 , owner(&owner) {}
91};
92
93struct Module {
94 std::string const fullname;
95 std::string const name;
96 std::string const type;
97 Module* const parent;
98 std::string const id{fmt::format("{}", ++object_counter)};
99 std::deque<std::unique_ptr<Module>> submodules;
100 std::deque<Port> ports;
101
102 Module(std::string const& fullname, std::string const& name, std::string const& type, Module& parent)
103 : fullname(fullname)
104 , name(name)
105 , type(type)
106 , parent(&parent) {}
107 Module(std::string const& fullname, std::string const& name, std::string const& type)
108 : fullname(fullname)
109 , name(name)
110 , type(type)
111 , parent(nullptr) {}
112};
113
114const std::unordered_set<std::string> ignored_entities = {"tlm_initiator_socket",
115 "sc_export",
116 "sc_thread_process",
117 "sc_signal",
118 "sc_object",
119 "sc_fifo",
120 "sc_method_process",
121 "sc_mutex",
122 "sc_vector",
123 "sc_semaphore_ordered",
124 "sc_variable",
125 "sc_prim_channel",
126 "tlm_signal",
127 "tlm_fifo",
128 "sc_register",
129 "sc_buffer"};
130
131const 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"};
134
135std::string indent{" "};
136std::string operator*(std::string const& str, const unsigned int level) {
137 std::ostringstream ss;
138 for(unsigned int i = 0; i < level; i++)
139 ss << str;
140 return ss.str();
141}
142
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()
150#else
151#define NO_TLM_EXTRACT
152#endif
153#endif
154
155std::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) == "$$$")
158 return {};
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) == "$$$")
167 continue;
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;
171 });
172 if(it != std::end(keep_outs))
173 continue;
174 }
175 auto ks = scan_object(child, *currentModule.submodules.back(), level + 1);
176 if(ks.size())
177 for(auto& s : ks)
178 keep_outs.insert(s);
179 }
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
186#ifndef NCSC
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;
190 if(input) {
191 currentModule.ports.push_back(Port(obj->name(), name, GET_EXPORT_IF(tptr), input, kind, currentModule));
192 return {name + "_port", name + "_port_0"};
193 } else {
194 currentModule.ports.push_back(Port(obj->name(), name, GET_PORT_IF(tptr), input, kind, currentModule));
195 return {name + "_export", name + "_export_0"};
196 }
197#endif
198#endif
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));
212#endif
213 } else if(ignored_entities.find(kind) == ignored_entities.end()) {
214 SCCWARN() << "object not known (" << kind << ")";
215 }
216 return {};
217}
218
219using stack_t = std::deque<Module const*>;
220using registry_t = std::unordered_map<void const*, std::vector<Port*>>;
221
222void 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);
227}
228
229using breadcrumb_t = std::tuple<Module*, bool>;
230bool 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());
233 if(current_mod == i)
234 return true;
235 if(visited.count(current_mod))
236 return false;
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))
241 return true;
242 bread_crumb.pop_back();
243 }
244 if(upwards && current_mod->parent) {
245 bread_crumb.emplace_back(current_mod->parent, true);
246 if(get_path_to(i, bread_crumb, visited))
247 return true;
248 bread_crumb.pop_back();
249 }
250 return false;
251}
252
253void infer_implicit_ports(Module& m) {
254 registry_t registry;
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)) {
265 Port* end_port = *i;
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));
281 }
282 last_upwards = upwards;
283 bread_crumb.pop_back();
284 }
285 }
286 i++;
287 }
288 o++;
289 }
290 }
291 }
292}
293void 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)
297 if(port.input)
298 num_in++;
299 else
300 num_out++;
301 if(!module.ports.size() && !module.submodules.size())
302 return;
303 e << indent * level << "node " << module.name << " {"
304 << "\n";
305 level++;
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";
309
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";
314 }
315
316 for(auto& m : module.submodules)
317 generate_elk(e, *m, level);
318 // Draw edges module <-> submodule:
319 for(auto& srcport : module.ports) {
320 if(srcport.port_if)
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";
325 }
326 }
327 }
328 // Draw edges submodule -> submodule:
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)
335 continue;
336 if(tgtport.port_if == srcport.port_if && tgtport.input)
337 e << indent * level << "edge " << srcport.fullname << " -> " << tgtport.fullname << "\n";
338 }
339 }
340 }
341 }
342 level--;
343 e << indent * level << "}\n"
344 << "\n";
345}
346
347void generate_port_json(writer_type& writer, hierarchy_dumper::file_type type, const scc::Port& p) {
348 writer.StartObject();
349 {
350 writer.Key("id");
351 writer.String(p.id.c_str());
352 if(type != hierarchy_dumper::D3JSON) {
353 writer.Key("labels");
354 writer.StartArray();
355 {
356 writer.StartObject();
357 {
358 writer.Key("text");
359 writer.String(p.name.c_str());
360 }
361 writer.EndObject();
362 }
363 writer.EndArray();
364 writer.Key("width");
365 writer.Uint(6);
366 writer.Key("height");
367 writer.Uint(6);
368 writer.Key("layoutOptions");
369 writer.StartObject();
370 {
371 writer.Key("port.side");
372 writer.String(p.input ? "WEST" : "EAST");
373 }
374 writer.EndObject();
375 if(type == hierarchy_dumper::DBGJSON) {
376 writer.Key("type");
377 writer.String(p.type.c_str());
378 writer.Key("input");
379 writer.Bool(p.input);
380 writer.Key("interface");
381 writer.Uint64(reinterpret_cast<uintptr_t>(p.port_if));
382 }
383 } else {
384 writer.Key("direction");
385 writer.String(p.input ? "INPUT" : "OUTPUT");
386 writer.Key("hwMeta");
387 writer.StartObject();
388 {
389 writer.Key("name");
390 writer.String(p.name.c_str());
391 // writer.Key("cssClass"); writer.String("node-style0");
392 // writer.Key("cssStyle"); writer.String("fill:red");
393 writer.Key("connectedAsParent");
394 writer.Bool(false);
395 }
396 writer.EndObject();
397 writer.Key("properties");
398 writer.StartObject();
399 {
400 writer.Key("side");
401 writer.String(p.input ? "WEST" : "EAST");
402 // writer.Key("index"); writer.Int(0);
403 }
404 writer.EndObject();
405 writer.Key("children");
406 writer.StartArray();
407 writer.EndArray();
408 }
409 }
410 writer.EndObject();
411}
412
413void generate_edge_json(writer_type& writer, const scc::Port& srcport, const scc::Port& tgtport) {
414 writer.StartObject();
415 {
416 auto edge_name = fmt::format("{}", ++object_counter);
417 writer.Key("id");
418 writer.String(edge_name.c_str());
419 writer.Key("sources");
420 writer.StartArray();
421 { writer.String(srcport.id.c_str()); }
422 writer.EndArray();
423 writer.Key("targets");
424 writer.StartArray();
425 { writer.String(tgtport.id.c_str()); }
426 writer.EndArray();
427 }
428 writer.EndObject();
429}
430
431void 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();
434 {
435 auto edge_name = fmt::format("{}", ++object_counter);
436 writer.Key("id");
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();
448 {
449 if(srcport.sig_name.size()) {
450 writer.Key("name");
451 writer.String(srcport.sig_name.c_str());
452 } else if(tgtport.sig_name.size()) {
453 writer.Key("name");
454 writer.String(tgtport.sig_name.c_str());
455 } else {
456 writer.Key("name");
457 writer.String(fmt::format("{}_to_{}", srcport.name, tgtport.name).c_str());
458 }
459 // writer.Key("cssClass"); writer.String("link-style0");
460 // writer.Key("cssStyle"); writer.String("stroke:red");
461 }
462 writer.EndObject();
463 }
464 writer.EndObject();
465}
466
467void 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)
470 if(port.input)
471 num_in++;
472 else
473 num_out++;
474 writer.StartObject();
475 {
476 writer.Key("id");
477 writer.String(module.id.c_str());
478 // process ports
479 writer.Key("ports");
480 writer.StartArray();
481 {
482 for(auto& p : module.ports)
483 generate_port_json(writer, type, p);
484 }
485 writer.EndArray();
486 // process modules
487 if(type == hierarchy_dumper::D3JSON && module.parent)
488 writer.Key("_children");
489 else
490 writer.Key("children");
491 writer.StartArray();
492 {
493 for(auto& c : module.submodules)
494 generate_mod_json(writer, type, *c, level * 1);
495 }
496 writer.EndArray();
497 // process connections
498 if(type == hierarchy_dumper::D3JSON && module.parent)
499 writer.Key("_edges");
500 else
501 writer.Key("edges");
502 writer.StartArray();
503 {
504 // Draw edges module <-> submodule:
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);
512 } else {
513 generate_edge_json(writer, srcport, tgtport);
514 }
515 }
516 }
517 }
518 }
519 }
520 // Draw edges submodule -> submodule:
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) {
527 continue;
528 }
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);
532 } else {
533 generate_edge_json(writer, srcport, tgtport);
534 }
535 }
536 }
537 }
538 }
539 }
540 }
541 }
542 writer.EndArray();
543 if(type != hierarchy_dumper::D3JSON) {
544 writer.Key("labels");
545 writer.StartArray();
546 {
547 writer.StartObject();
548 {
549 writer.Key("text");
550 writer.String(module.name.c_str());
551 }
552 writer.EndObject();
553 }
554 writer.EndArray();
555 writer.Key("width");
556 writer.Uint(50);
557 writer.Key("height");
558 writer.Uint(std::max(80U, std::max(num_in, num_out) * 20));
559 if(type == hierarchy_dumper::DBGJSON) {
560 writer.Key("name");
561 writer.String(module.name.c_str());
562 writer.Key("type");
563 writer.String(module.type.c_str());
564 writer.Key("topmodule");
565 writer.Bool(!module.parent);
566 }
567 } else {
568 writer.Key("hwMeta");
569 writer.StartObject();
570 {
571 writer.Key("name");
572 writer.String(module.name.c_str());
573 writer.Key("cls");
574 writer.String(module.type.c_str());
575 // writer.Key("bodyText"); writer.String(module.type.c_str());
576 writer.Key("maxId");
577 writer.Uint(object_counter);
578 writer.Key("isExternalPort");
579 writer.Bool(false);
580 // writer.Key("cssClass"); writer.String("node-style0");
581 // writer.Key("cssStyle"); writer.String("fill:red");
582 }
583 writer.EndObject();
584 writer.Key("properties");
585 writer.StartObject();
586 {
587 writer.Key("org.eclipse.elk.layered.mergeEdges");
588 writer.Uint(1);
589 writer.Key("org.eclipse.elk.portConstraints");
590 writer.String("FIXED_SIDE");
591 }
592 writer.EndObject();
593 }
594 }
595 writer.EndObject();
596}
597
598std::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);
605 return topModule;
606 } else {
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);
611 return topModule;
612 }
613}
614void 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";
622 } else {
623 OStreamWrapper stream(e);
624 writer_type writer(stream);
625 writer.StartObject();
626 {
627 auto elems = util::split(sc_core::sc_argv()[0], '/');
628 writer.Key("id");
629 writer.String("0");
630 writer.Key("labels");
631 writer.StartArray();
632 {
633 writer.StartObject();
634 {
635 writer.Key("text");
636 writer.String(elems[elems.size() - 1].c_str());
637 }
638 writer.EndObject();
639 }
640 writer.EndArray();
641 writer.Key("layoutOptions");
642 writer.StartObject();
643 {
644 writer.Key("algorithm");
645 writer.String("layered");
646 }
647 writer.EndObject();
648 writer.Key("children");
649 writer.StartArray();
650 { generate_mod_json(writer, format, *topModule); }
651 writer.EndArray();
652 writer.Key("edges");
653 writer.StartArray();
654 writer.EndArray();
655 if(format == hierarchy_dumper::D3JSON) {
656 writer.Key("hwMeta");
657 writer.StartObject();
658 {
659 writer.Key("cls");
660 writer.Null();
661 writer.Key("maxId");
662 writer.Uint(65536);
663 writer.Key("name");
664 writer.String(elems[elems.size() - 1].c_str());
665 }
666 writer.EndObject();
667 writer.Key("properties");
668 writer.StartObject();
669 {
670 writer.Key("org.eclipse.elk.layered.mergeEdges");
671 writer.Uint(1);
672 writer.Key("org.eclipse.elk.portConstraints");
673 writer.String("FIXED_ORDER");
674 }
675 writer.EndObject();
676 }
677 }
678 writer.EndObject();
679 SCCINFO() << "SystemC Structure Dumped to JSON file";
680 }
681}
682} // namespace
683
684hierarchy_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} {}
688
690
691void hierarchy_dumper::start_of_simulation() {
692 if(dump_hier_file_name.size()) {
693 std::ofstream of{dump_hier_file_name};
694 if(of.is_open())
695 dump_structure(of, dump_format);
696 }
697}
698} // namespace scc
virtual ~hierarchy_dumper()
Destroys the hierarchy_dumper object.
hierarchy_dumper(const std::string &filename, file_type format)
Constructs a hierarchy_dumper object with the specified file name and format.
file_type
The supported file formats for dumping the hierarchy.
SCC TLM utilities.
std::vector< std::string > split(const std::string &s, char separator)
Definition ities.h:264