scc  2024.06
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 
48 namespace scc {
49 using namespace rapidjson;
50 using writer_type = PrettyWriter<OStreamWrapper>;
51 
52 namespace {
53 #ifdef __GNUG__
54 std::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++
62 std::string demangle(const char* name) { return name; }
63 #endif
64 std::string demangle(const char* name);
65 
66 template <class T> std::string type(const T& t) { return demangle(typeid(t).name()); }
67 
68 unsigned object_counter{0};
69 
70 struct Module;
71 
72 struct 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 
93 struct 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 
114 const 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 
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"};
134 
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++)
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 
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) == "$$$")
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 
219 using stack_t = std::deque<Module const*>;
220 using registry_t = std::unordered_map<void const*, std::vector<Port*>>;
221 
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);
227 }
228 
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());
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 
253 void 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 }
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)
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 
347 void 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 
413 void 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 
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();
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 
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)
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 
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);
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 }
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";
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 
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} {}
688 
689 hierarchy_dumper::~hierarchy_dumper() {}
690 
691 void 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
SCC TLM utilities.
std::vector< std::string > split(const std::string &s, char separator)
Definition: ities.h:223