Cerata
A library to generate structural hardware designs
dot.cc
1 // Copyright 2018-2019 Delft University of Technology
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "cerata/dot/dot.h"
16 
17 #include <sstream>
18 
19 #include "cerata/logging.h"
20 #include "cerata/edge.h"
21 #include "cerata/type.h"
22 #include "cerata/output.h"
23 #include "cerata/utils.h"
24 #include "cerata/node.h"
25 #include "cerata/expression.h"
26 
27 namespace cerata::dot {
28 
30  CreateDir(root_dir_ + "/" + subdir());
32  for (const auto &o : outputs_) {
33  if (o.comp != nullptr) {
34  CERATA_LOG(DEBUG, "DOT: Generating output for Graph: " + o.comp->name());
35  dot.GenFile(*o.comp, root_dir_ + "/" + subdir() + "/" + o.comp->name() + ".dot");
36  }
37  }
38 }
39 
40 static std::string ToHex(const Node &n) {
41  std::stringstream ret;
42  ret << std::hex << reinterpret_cast<uint64_t>(&n);
43  return ret.str();
44 }
45 
46 std::string Grapher::GenEdges(const Graph &graph, int level) {
47  std::stringstream ret;
48  auto all_edges = GetAllEdges(graph);
49  for (const auto &e : all_edges) {
50  if (!Contains(drawn_edges, e)) {
51  // Remember we've drawn this edge
52  drawn_edges.push_back(e);
53 
54  // Check if edge is complete
55  auto dst = e->dst();
56  auto src = e->src();
57  if ((dst == nullptr) || (src == nullptr)) {
58  continue;
59  }
60  // Don't draw literals
61  if (dst->IsLiteral() || src->IsLiteral()) {
62  continue;
63  }
64 
65  auto draw_style = false;
66  // Draw edge
67  ret << tab(level);
68  if (src->IsExpression() && style.config.nodes.expand.expression) {
69  auto srcname = ToHex(*src);
70  ret << " -> ";
71  ret << NodeName(*dst);
72  ret << "\"" + srcname + "\"";
73  draw_style = true;
74  } else if ((src->IsParameter() && style.config.nodes.parameters) || !src->IsParameter()) {
75  auto srcname = NodeName(*src);
76  ret << srcname;
77  ret << " -> ";
78  ret << NodeName(*dst);
79  draw_style = true;
80  }
81 
82  if (draw_style) {
83  // Set style
84  StyleBuilder sb;
85  ret << " [";
86  switch (src->type()->id()) {
87  default: {
88  sb << style.edge.base;
89  break;
90  }
91  }
92 
93  // Put array index label
94  if (src->array() && !dst->array()) {
95  sb << "label=\"" + std::to_string((*src->array())->IndexOf(*src)) + "\"";
96  }
97  if (!src->array() && dst->array()) {
98  sb << "label=\"" + std::to_string((*dst->array())->IndexOf(*dst)) + "\"";
99  }
100  if (src->array() && dst->array()) {
101  sb << "label=\"" + std::to_string((*src->array())->IndexOf(*src)) + " to "
102  + std::to_string((*dst->array())->IndexOf(*dst)) + "\"";
103  }
104 
105  if ((src->IsPort()) && style.config.nodes.ports) {
106  if (dst->IsSignal()) {
107  // Port to signal
108  sb << style.edge.port_to_sig;
109  } else if (dst->IsPort()) {
110  sb << style.edge.port_to_port;
111  }
112  } else if (src->IsSignal() && style.config.nodes.signals) {
113  if (dst->IsPort()) {
114  // Signal to port
115  sb << style.edge.sig_to_port;
116  }
117  } else if (src->IsParameter() && style.config.nodes.parameters) {
118  sb << style.edge.param;
119  } else if (src->IsLiteral() && style.config.nodes.literals) {
120  sb << style.edge.lit;
121  } else if (src->IsExpression() && style.config.nodes.expressions) {
122  sb << style.edge.expr;
124  sb << "lhead=\"cluster_" + NodeName(*src) + "\"";
125  }
126  } else {
127  ret << "]\n";
128  continue;
129  }
130  ret << sb.ToString();
131  // Generic edge
132  ret << "]\n";
133  }
134  }
135  }
136 
137  return ret.str();
138 }
139 
140 std::string Style::GenHTMLTableCell(const Type &t,
141  const std::string &name,
142  int level) {
143  std::stringstream str;
144  // Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn
145  if (t.Is(Type::RECORD)) {
146  auto rec = dynamic_cast<const Record &>(t);
147  str << R"(<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0")";
148  if (level == 0) {
149  str << R"( PORT="cell")";
150  }
151  str << ">";
152  str << "<TR>";
153  str << "<TD";
154  str << R"( BGCOLOR=")" + node.color.record + R"(">)";
155  str << name;
156  str << "</TD>";
157  str << "<TD ";
158  if (level == 0) {
159  str << R"( PORT="cell")";
160  }
161  str << R"( BGCOLOR=")" + node.color.record_child + R"(">)";
162  str << R"(<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0">)";
163  for (const auto &f : rec.fields()) {
164  str << "<TR><TD>";
165  str << GenHTMLTableCell(*f->type(), f->name(), level + 1);
166  str << "</TD></TR>";
167  }
168  str << "</TABLE>";
169  str << "</TD>";
170  str << "</TR></TABLE>";
171  } else {
172  str << name;
173  if (t.Is(Type::VECTOR)) {
174  auto vec = dynamic_cast<const Vector &>(t);
175  auto width = vec.width();
176  if (width) {
177  str << "[" + width.value()->ToString() + "]";
178  } else {
179  str << "[..]";
180  }
181  }
182  }
183  return str.str();
184 }
185 
186 std::string Style::GenDotRecordCell(const Type &t,
187  const std::string &name,
188  int level) {
189  std::stringstream str;
190  // Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn
191  if (t.Is(Type::RECORD)) {
192  auto rec = dynamic_cast<const Record &>(t);
193  if (level == 0) {
194  str << "<cell>";
195  }
196  str << name;
197  str << "|";
198  str << "{";
199  auto record_fields = rec.fields();
200  for (const auto &f : record_fields) {
201  str << GenDotRecordCell(*f->type(), f->name(), level + 1);
202  if (f != record_fields.back()) {
203  str << "|";
204  }
205  }
206  str << "}";
207  } else {
208  str << name;
209  }
210  return str.str();
211 }
212 
213 std::string Grapher::GenNode(const Node &n, int level) {
214  std::stringstream str;
215  if (n.IsExpression() && style.config.nodes.expand.expression) {
216  str << GenExpr(n);
217  } else {
218  // Indent
219  str << tab(level);
220  str << NodeName(n);
221  // Draw style
222  str << " [";
223  str << style.GetStyle(n);
224  str << "];\n";
225  }
226  return str.str();
227 }
228 
229 std::string Grapher::GenNodes(const Graph &graph, Node::NodeID id, int level, bool no_group) {
230  std::stringstream ret;
231  auto nodes = graph.GetNodesOfType(id);
232  auto arrays = graph.GetArraysOfType(id);
233  if (!nodes.empty() || !arrays.empty()) {
234  if (!no_group) {
235  ret << tab(level) << "subgraph cluster_" << sanitize(graph.name()) + "_" + ToString(id) << " {\n";
236  // ret << tab(level + 1) << "label=\"" << ToString(id) << "s\";\n";
237  ret << tab(level + 1) << "rankdir=LR;\n";
238  ret << tab(level + 1) << "label=\"\";\n";
239  ret << tab(level + 1) << "style=" + style.nodegroup.base + ";\n";
240  ret << tab(level + 1) << "color=\"" + style.nodegroup.color + "\";\n";
241  }
242  for (const auto &n : nodes) {
243  ret << GenNode(*n, level + no_group + 1);
244  }
245  for (const auto &a : arrays) {
246  ret << GenNode(*a->base(), level + no_group + 1);
247  }
248  if (!no_group) {
249  ret << tab(level) << "}\n";
250  }
251  }
252  return ret.str();
253 }
254 
255 std::string Grapher::GenGraph(const Graph &graph, int level) {
256  std::stringstream ret;
257 
258  // (sub)graph header
259  if (level == 0) {
260  ret << "digraph {\n";
261 
262  // Preferably we would want to use splines=ortho, but dot is bugged when using html tables w.r.t. arrow directions
263  // resulting from this setting
264  ret << tab(level + 1) << "splines=ortho;\n";
265  ret << tab(level + 1) << "rankdir=LR;\n";
266  } else {
267  ret << tab(level) << "subgraph cluster_" << sanitize(graph.name()) << " {\n";
268  ret << tab(level + 1) << "rankdir=TB;\n";
269  ret << tab(level + 1) << "style=" + style.subgraph.base + ";\n";
270  ret << tab(level + 1) << "color=\"" + style.subgraph.color + "\";\n";
271  ret << tab(level + 1) << "label=\"" << sanitize(graph.name()) << "\";\n";
272  }
273 
274  // Nodes
276  ret << GenNodes(graph, Node::NodeID::EXPRESSION, level + 1);
277 
279  ret << GenNodes(graph, Node::NodeID::LITERAL, level + 1);
280 
282  ret << GenNodes(graph, Node::NodeID::PARAMETER, level + 1);
283 
284  if (style.config.nodes.ports)
285  ret << GenNodes(graph, Node::NodeID::PORT, level + 1);
286 
288  ret << GenNodes(graph, Node::NodeID::SIGNAL, level + 1, true);
289 
290  if (graph.IsComponent()) {
291  auto &comp = dynamic_cast<const Component &>(graph);
292  if (!comp.children().empty()) {
293  ret << "\n";
294  }
295 
296  // Graph children
297  for (const auto &child : comp.children()) {
298  ret << GenGraph(*child, level + 1);
299  }
300  if (level == 0) {
301  ret << GenEdges(graph, level + 1);
302  }
303  }
304 
305  ret << tab(level) << "}\n";
306 
307  return ret.str();
308 }
309 
310 std::string Grapher::GenFile(const Graph &graph, const std::string &path) {
311  std::string dot = GenGraph(graph);
312  std::ofstream out(path);
313  out << dot;
314  out.close();
315  return dot;
316 }
317 
318 std::string Grapher::GenExpr(const Node &node, const std::string &prefix, int level) {
319  std::stringstream str;
320 
321  std::string node_id;
322  if (!prefix.empty()) {
323  node_id = prefix + "_";
324  }
325  node_id += ToHex(node);
326 
327  if (level == 0) {
328  str << "subgraph cluster_" + NodeName(node) + " {\n";
329  }
330 
331  str << "\"" + node_id + "\" [label=\"" + sanitize(node.name()) + "\" ";
332  if (level == 0) {
333  str << ", color=red";
334  }
335  str << "];\n";
336  if (node.IsExpression()) {
337  auto expr = dynamic_cast<const Expression &>(node);
338  auto left_node_id = node_id + "_" + ToHex(*expr.lhs());
339  auto right_node_id = node_id + "_" + ToHex(*expr.rhs());
340  str << "\"" + node_id + "\" -> \"" + left_node_id + "\"\n";
341  str << "\"" + node_id + "\" -> \"" + right_node_id + "\"\n";
342  str << GenExpr(*expr.lhs(), node_id, level + 1);
343  str << GenExpr(*expr.rhs(), node_id, level + 1);
344  }
345  if (level == 0) {
346  str << "}\n";
347  }
348  return str.str();
349 }
350 
351 std::string NodeName(const Node &node, const std::string &suffix) {
352  std::stringstream ret;
353  if (node.parent()) {
354  auto name = (*node.parent())->name();
355  ret << name + ":" + ToString(node.node_id()) + ":";
356  }
357  if (node.IsExpression()) {
358  ret << "Anon_" + ToString(node.node_id()) + "_" + ToHex(node);
359  } else if (!node.name().empty()) {
360  ret << node.name();
361  }
362 
363  return sanitize(ret.str()) + suffix;
364 }
365 
366 } // namespace cerata::dot
cerata::dot::Config::NodeConfig::signals
bool signals
Show signals.
Definition: style.h:88
cerata::Type::RECORD
@ RECORD
? | ? | Yes
Definition: type.h:77
cerata::dot::Config::nodes
struct cerata::dot::Config::NodeConfig nodes
Node configuration.
cerata::dot::tab
std::string tab(uint n)
Return indent string.
Definition: style.h:28
cerata::dot::Style::subgraph
struct cerata::dot::Style::SubGraph subgraph
Style for sub graphs.
cerata::Component
A Component graph.
Definition: graph.h:158
cerata::dot::DOTOutputGenerator::subdir
std::string subdir() override
Returns the subdirectory used by this OutputGenerator.
Definition: dot.h:65
cerata::OutputGenerator::root_dir_
std::string root_dir_
The root directory to generate the output in.
Definition: output.h:54
cerata::Vector
Vector type.
Definition: type.h:222
cerata::GetAllEdges
std::vector< Edge * > GetAllEdges(const Graph &graph)
Obtain all edges in a graph.
Definition: edge.cc:180
cerata::dot::Grapher
Dot graph output generator.
Definition: dot.h:31
cerata::dot::Style::NodeStyle::Colors::record
str record
Record node color.
Definition: style.h:161
cerata::dot::Style::EdgeStyle::param
str param
Style for parameter edges.
Definition: style.h:148
cerata::Node::NodeID::LITERAL
@ LITERAL
No-input AND multi-output node with storage type and storage value.
cerata::dot::Style::EdgeStyle::base
str base
Base style.
Definition: style.h:144
cerata::dot::Grapher::GenFile
std::string GenFile(const Graph &graph, const std::string &path)
Generate a DOT file.
Definition: dot.cc:310
cerata::dot::Config::NodeConfig::expressions
bool expressions
Show expressions.
Definition: style.h:90
cerata::dot::DOTOutputGenerator::Generate
void Generate() override
Generate the DOT graphs.
Definition: dot.cc:29
cerata::Expression
A node representing a binary tree of other nodes.
Definition: expression.h:34
cerata::dot::Style::NodeGroup::base
str base
Base style for groups.
Definition: style.h:133
cerata::Graph
A graph representing a hardware structure.
Definition: graph.h:37
cerata::Type
A Type.
Definition: type.h:63
cerata::dot::Style::EdgeStyle::port_to_port
str port_to_port
Style for port-to-port.
Definition: style.h:147
cerata::Vector::width
std::optional< Node * > width() const override
Return a pointer to the node representing the width of this vector, if specified.
Definition: type.cc:211
cerata::Record
A Record type containing zero or more fields.
Definition: type.h:301
cerata::dot::Style::EdgeStyle::expr
str expr
Style for expressions.
Definition: style.h:151
cerata::dot::Grapher::GenExpr
static std::string GenExpr(const Node &exp, const std::string &prefix="", int level=0)
Generate expressions.
Definition: dot.cc:318
cerata::dot::Grapher::GenNode
std::string GenNode(const Node &n, int level=0)
Generate a node.
cerata::dot::Style::SubGraph::color
str color
Subgraph color.
Definition: style.h:128
cerata::Graph::IsComponent
bool IsComponent() const
Return true if this graph is a component, false otherwise.
Definition: graph.h:51
cerata::dot::Style::str
std::string str
Short-hand for std::string.
Definition: style.h:123
cerata::Node
A node.
Definition: node.h:42
cerata::dot::Style::NodeGroup::color
str color
Color for groups.
Definition: style.h:134
cerata::dot::Style::GetStyle
std::string GetStyle(const Node &n)
Get the style for a node.
Definition: style.cc:175
cerata::dot::StyleBuilder
Convenience structure to build up dot styles.
Definition: style.h:74
cerata::dot::Config::NodeConfig::parameters
bool parameters
Show parameters.
Definition: style.h:86
cerata::Type::Is
bool Is(ID type_id) const
Return true if the Type ID is type_id, false otherwise.
Definition: type.cc:28
cerata::Node::NodeID::PARAMETER
@ PARAMETER
Single-input AND multi-output node with default value.
cerata::dot::Style::EdgeStyle::port_to_sig
str port_to_sig
Style for port-to-signal.
Definition: style.h:145
cerata::dot::Grapher::style
Style style
The style.
Definition: dot.h:33
cerata::dot::Style::nodegroup
struct cerata::dot::Style::NodeGroup nodegroup
Style for group of nodes.
cerata::dot::Grapher::GenNodes
std::string GenNodes(const Graph &graph, Node::NodeID id, int level=0, bool no_group=false)
Generate nodes.
cerata::Node::node_id
NodeID node_id() const
Return the node type ID.
Definition: node.h:62
cerata::dot::Grapher::GenEdges
std::string GenEdges(const Graph &graph, int level=0)
Generate edges.
Definition: dot.cc:46
cerata::Named::name
std::string name() const
Return the name of the object.
Definition: utils.h:45
cerata::Node::NodeID
NodeID
Node type IDs with different properties.
Definition: node.h:45
cerata::ToString
std::string ToString(Expression::Op operation)
Human-readable expression operator.
Definition: expression.cc:149
cerata::Node::NodeID::EXPRESSION
@ EXPRESSION
No-input AND multi-output node that forms a binary tree with operations and nodes.
cerata::dot
Contains everything related to the DOT back-end.
Definition: dot.cc:27
cerata::dot::Style::node
struct cerata::dot::Style::NodeStyle node
Style for nodes.
cerata::dot::Config::NodeConfig::expand
struct cerata::dot::Config::NodeConfig::ExpandConfig expand
Configures what types of nodes to expand.
cerata::Node::NodeID::SIGNAL
@ SIGNAL
Single-input AND multi-output node.
cerata::dot::Grapher::GenGraph
std::string GenGraph(const Graph &graph, int level=0)
Generate a graph.
Definition: dot.cc:255
cerata::dot::Style::edge
struct cerata::dot::Style::EdgeStyle edge
Style for edges.
cerata::dot::Config::NodeConfig::ExpandConfig::expression
bool expression
Expand expressions.
Definition: style.h:96
cerata::CreateDir
void CreateDir(const std::string &dir_name)
Create a directory.
Definition: utils.cc:52
cerata::dot::Style::SubGraph::base
str base
Subgraph base style.
Definition: style.h:127
cerata::dot::Style::EdgeStyle::sig_to_port
str sig_to_port
Style for signal-to-port.
Definition: style.h:146
cerata::dot::StyleBuilder::ToString
std::string ToString()
Generate the style string.
Definition: style.cc:75
cerata::Contains
bool Contains(const std::vector< std::shared_ptr< T >> &list, const std::shared_ptr< T > &item)
Return true if vector contains item, false otherwise.
Definition: utils.h:58
cerata::OutputGenerator::outputs_
std::vector< OutputSpec > outputs_
A list of things to put out.
Definition: output.h:56
cerata::dot::Style::config
Config config
Configuration of what types of constructs to show or hide for this style.
Definition: style.h:186
cerata::dot::Style::GenDotRecordCell
static std::string GenDotRecordCell(const Type &t, const std::string &name, int level=0)
Generate a DOT record cell from a type.
cerata::dot::Config::NodeConfig::literals
bool literals
Show literals.
Definition: style.h:87
cerata::dot::Style::EdgeStyle::lit
str lit
Style for literal edges.
Definition: style.h:150
cerata::dot::sanitize
std::string sanitize(std::string in)
Sanitize a string for usage in DOT.
Definition: style.h:38
cerata::dot::Config::NodeConfig::ports
bool ports
Show ports.
Definition: style.h:89
cerata::dot::NodeName
std::string NodeName(const Node &node, const std::string &suffix)
Return the DOT name of a node.
Definition: dot.cc:351
cerata::Node::NodeID::PORT
@ PORT
Single-input AND multi-output node with direction.
cerata::dot::Style::GenHTMLTableCell
std::string GenHTMLTableCell(const Type &t, const std::string &name, int level=0)
Generate a HTML table cell from a type.
Definition: dot.cc:140
cerata::Type::VECTOR
@ VECTOR
Yes | Yes | No.
Definition: type.h:71
cerata::dot::Style::NodeStyle::Colors::record_child
str record_child
Record child color.
Definition: style.h:163
cerata::Object::parent
virtual std::optional< Graph * > parent() const
Return the parent graph of this object, if any. Returns empty option otherwise.
Definition: object.cc:33
cerata::dot::Style::NodeStyle::color
struct cerata::dot::Style::NodeStyle::Colors color
Colors for specific nodes.
cerata::dot::Grapher::drawn_edges
std::vector< Edge * > drawn_edges
Edges that were already drawn.
Definition: dot.h:35