Cerata
A library to generate structural hardware designs
graph.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/graph.h"
16 
17 #include <string>
18 #include <memory>
19 #include <map>
20 
21 #include "cerata/utils.h"
22 #include "cerata/node.h"
23 #include "cerata/logging.h"
24 #include "cerata/object.h"
25 #include "cerata/pool.h"
26 #include "cerata/edge.h"
27 #include "cerata/parameter.h"
28 #include "cerata/expression.h"
29 
30 namespace cerata {
31 
32 Graph &Graph::Add(const std::shared_ptr<Object> &object) {
33  // Check for duplicates in name / ownership
34  for (const auto &o : objects_) {
35  if (o->name() == object->name()) {
36  if (o.get() == object.get()) {
37  // Graph already owns object. We can skip adding.
38  return *this;
39  } else {
40  CERATA_LOG(ERROR, "Graph " + name() + " already contains an object with name " + object->name());
41  }
42  }
43  }
44 
45  // Check if it already has a parent. At this point we may want to move to unique ptrs to objects.
46  if (object->parent() && object->parent().value() != this) {
47  CERATA_LOG(FATAL, "Object " + object->name() + " already has parent " + object->parent().value()->name());
48  }
49 
50  // Get any objects referenced by this object. They must already be on this graph.
51  std::vector<Object *> references;
52  object->AppendReferences(&references);
53  for (const auto &ref : references) {
54  // If the sub-object has a parent and it is this graph, everything is OK.
55  if (ref->parent()) {
56  if (ref->parent().value() == this) {
57  continue;
58  }
59  }
60  if (ref->IsNode()) {
61  // There are two special cases where a references doesn't yet have to be owned by this graph.
62  auto gen_node = dynamic_cast<Node *>(ref);
63  // Literals are owned by the literal pool, so everything is OK as well in that case.
64  if (gen_node->IsLiteral()) {
65  continue;
66  }
67  if (gen_node->IsExpression()) {
68  continue;
69  }
70  }
71  // Otherwise throw an error.
72  CERATA_LOG(ERROR, "Object [" + ref->name()
73  + "] bound to object [" + object->name()
74  + "] is not present on Graph " + name());
75  }
76  // No conflicts, add the object
77  objects_.push_back(object);
78  // Set this graph as parent.
79  object->SetParent(this);
80 
81  return *this;
82 }
83 
84 Graph &Graph::Add(const std::vector<std::shared_ptr<Object>> &objects) {
85  for (const auto &obj : objects) {
86  Add(obj);
87  }
88  return *this;
89 }
90 
92  for (auto o = objects_.begin(); o < objects_.end(); o++) {
93  if (o->get() == obj) {
94  objects_.erase(o);
95  }
96  }
97  return *this;
98 }
99 
100 std::optional<Node *> Graph::FindNode(const std::string &node_name) const {
101  for (const auto &n : GetAll<Node>()) {
102  if (n->name() == node_name) {
103  return n;
104  }
105  }
106  return std::nullopt;
107 }
108 
109 Node *Graph::GetNode(const std::string &node_name) const {
110  for (const auto &n : GetAll<Node>()) {
111  if (n->name() == node_name) {
112  return n;
113  }
114  }
115  CERATA_LOG(FATAL, "Node with name " + node_name + " does not exist on Graph " + this->name());
116 }
117 
118 size_t Graph::CountNodes(Node::NodeID id) const {
119  size_t count = 0;
120  for (const auto &n : GetAll<Node>()) {
121  if (n->Is(id)) {
122  count++;
123  }
124  }
125  return count;
126 }
127 
129  size_t count = 0;
130  for (const auto &n : GetAll<NodeArray>()) {
131  if (n->node_id() == id) {
132  count++;
133  }
134  }
135  return count;
136 }
137 
138 std::vector<Node *> Graph::GetNodesOfType(Node::NodeID id) const {
139  std::vector<Node *> result;
140  for (const auto &n : GetAll<Node>()) {
141  if (n->Is(id)) {
142  result.push_back(n);
143  }
144  }
145  return result;
146 }
147 
148 std::vector<NodeArray *> Graph::GetArraysOfType(Node::NodeID id) const {
149  std::vector<NodeArray *> result;
150  auto arrays = GetAll<NodeArray>();
151  for (const auto &a : arrays) {
152  if (a->node_id() == id) {
153  result.push_back(a);
154  }
155  }
156  return result;
157 }
158 
159 Port *Graph::prt(const std::string &name) const {
160  return Get<Port>(name);
161 }
162 
163 Signal *Graph::sig(const std::string &name) const {
164  return Get<Signal>(name);
165 }
166 
167 Parameter *Graph::par(const std::string &name) const {
168  return Get<Parameter>(name);
169 }
170 
171 Parameter *Graph::par(const Parameter &param) const {
172  return Get<Parameter>(param.name());
173 }
174 
175 Parameter *Graph::par(const std::shared_ptr<Parameter> &param) const {
176  return Get<Parameter>(param->name());
177 }
178 
179 PortArray *Graph::prt_arr(const std::string &name) const {
180  return Get<PortArray>(name);
181 }
182 
183 SignalArray *Graph::sig_arr(const std::string &name) const {
184  return Get<SignalArray>(name);
185 }
186 
187 std::vector<Node *> Graph::GetImplicitNodes() const {
188  std::vector<Node *> result;
189  for (const auto &n : GetAll<Node>()) {
190  for (const auto &i : n->sources()) {
191  if (i->src()) {
192  if (!i->src()->parent()) {
193  result.push_back(i->src());
194  }
195  }
196  }
197  }
198  FilterDuplicates(&result);
199  return result;
200 }
201 
202 std::vector<Node *> Graph::GetNodesOfTypes(std::initializer_list<Node::NodeID> ids) const {
203  std::vector<Node *> result;
204  for (const auto &n : GetAll<Node>()) {
205  for (const auto &id : ids) {
206  if (n->node_id() == id) {
207  result.push_back(n);
208  break;
209  }
210  }
211  }
212  return result;
213 }
214 
215 Graph &Graph::SetMeta(const std::string &key, std::string value) {
216  meta_[key] = std::move(value);
217  return *this;
218 }
219 
220 bool Graph::Has(const std::string &name) {
221  for (const auto &o : objects_) {
222  if (o->name() == name) {
223  return true;
224  }
225  }
226  return false;
227 }
228 
229 std::string Graph::ToStringAllOjects() const {
230  std::stringstream ss;
231  for (const auto &o : objects_) {
232  ss << o->name();
233  if (o != objects_.back()) {
234  ss << ", ";
235  }
236  }
237  return ss.str();
238 }
239 
240 Component &Component::AddChild(std::unique_ptr<Instance> child) {
241  // Add this graph to the child's parent graphs
242  child->SetParent(this);
243  // Add the child graph
244  children_.push_back(std::move(child));
245  return *this;
246 }
247 
248 std::shared_ptr<Component> component(std::string name,
249  const std::vector<std::shared_ptr<Object>> &objects,
250  ComponentPool *component_pool) {
251  // Create the new component.
252  auto *ptr = new Component(std::move(name));
253  auto ret = std::shared_ptr<Component>(ptr);
254  // Add the component to the pool.
255  component_pool->Add(ret);
256  // Add the objects shown in the vector.
257  for (const auto &object : objects) {
258  // Add the object to the graph.
259  ret->Add(object);
260  }
261  return ret;
262 }
263 
264 std::shared_ptr<Component> component(std::string name, ComponentPool *component_pool) {
265  return component(std::move(name), {}, component_pool);
266 }
267 
268 std::vector<const Component *> Component::GetAllInstanceComponents() const {
269  std::vector<const Component *> ret;
270  for (const auto &child : children_) {
271  const Component *comp = nullptr;
272  if (child->IsComponent()) {
273  // Graph itself is the component to potentially insert
274  auto child_as_comp = dynamic_cast<Component *>(child.get());
275  comp = child_as_comp;
276  } else if (child->IsInstance()) {
277  auto child_as_inst = dynamic_cast<Instance *>(child.get());
278  // Graph is an instance, its component definition should be inserted.
279  comp = child_as_inst->component();
280  }
281  if (comp != nullptr) {
282  if (!Contains(ret, comp)) {
283  // If so, push it onto the vector
284  ret.push_back(comp);
285  }
286  }
287  }
288  return ret;
289 }
290 
291 Instance *Component::Instantiate(const std::shared_ptr<Component> &comp, const std::string &name) {
292  return Instantiate(comp.get(), name);
293 }
294 
295 Instance *Component::Instantiate(Component *comp, const std::string &name) {
296  // Mark the component as once instantiated.
297  comp->was_instantiated = true;
298 
299  auto new_name = name;
300  if (new_name.empty()) {
301  new_name = comp->name() + "_inst";
302  }
303  int i = 1;
304  while (HasChild(new_name)) {
305  new_name = comp->name() + "_inst" + std::to_string(i);
306  i++;
307  }
308  auto inst = Instance::Make(comp, new_name, this);
309 
310  // Now, all parameters are reconnected to their defaults. Add all these to the inst to component node map.
311  // Whenever we derive stuff from instantiated nodes, like signalizing a port, we will know what value to use.
312  for (const auto &param : inst->GetAll<Parameter>()) {
313  inst_to_comp[param] = param->default_value();
314  }
315 
316  auto raw_ptr = inst.get();
317  AddChild(std::move(inst));
318 
319  return raw_ptr;
320 }
321 
322 bool Component::HasChild(const std::string &name) const {
323  for (const auto &g : this->children()) {
324  if (g->name() == name) {
325  return true;
326  }
327  }
328  return false;
329 }
330 
331 bool Component::HasChild(const Instance &inst) const {
332  for (const auto &g : this->children()) {
333  if (&inst == g) {
334  return true;
335  }
336  }
337  return false;
338 }
339 
340 static void ThrowErrorIfInstantiated(const Graph &g, bool was_instantiated, const Object &o) {
341  if (was_instantiated) {
342  bool generate_warning = false;
343  if (o.IsNode()) {
344  auto &node = dynamic_cast<const Node &>(o);
345  if (node.IsPort() || node.IsParameter()) {
346  generate_warning = true;
347  }
348  } else if (o.IsArray()) {
349  auto &array = dynamic_cast<const NodeArray &>(o);
350  if (array.base()->IsPort() || array.base()->IsParameter()) {
351  generate_warning = true;
352  }
353  }
354  if (generate_warning) {
355  CERATA_LOG(ERROR, "Mutating port or parameter nodes " + o.name() + " of component graph " + g.name()
356  + " after instantiation is not allowed.");
357  }
358  }
359 }
360 
361 Graph &Component::Add(const std::shared_ptr<Object> &object) {
362  ThrowErrorIfInstantiated(*this, was_instantiated, *object);
363  return Graph::Add(object);
364 }
365 
367  ThrowErrorIfInstantiated(*this, was_instantiated, *object);
368  return Graph::Remove(object);
369 }
370 
371 Graph &Component::Add(const std::vector<std::shared_ptr<Object>> &objects) { return Graph::Add(objects); }
372 
373 std::unique_ptr<Instance> Instance::Make(Component *component, const std::string &name, Component *parent) {
374  auto inst = new Instance(component, name, parent);
375  return std::unique_ptr<Instance>(inst);
376 }
377 
378 Instance::Instance(Component *comp, std::string name, Component *parent)
379  : Graph(std::move(name), INSTANCE), component_(comp), parent_(parent) {
380  // Copy over all parameters.
381  for (const auto &param : component_->GetAll<Parameter>()) {
382  param->CopyOnto(this, param->name(), &comp_to_inst);
383  }
384 
385  // Copy over all ports.
386  for (const auto &port : component_->GetAll<Port>()) {
387  port->CopyOnto(this, port->name(), &comp_to_inst);
388  }
389 
390  // Make copies of port arrays
391  for (const auto &port_array : component_->GetAll<PortArray>()) {
392  port_array->CopyOnto(this, port_array->name(), &comp_to_inst);
393  }
394 }
395 
396 Graph &Instance::Add(const std::shared_ptr<Object> &object) {
397  if (object->IsNode()) {
398  auto node = std::dynamic_pointer_cast<Node>(object);
399  if (node->IsSignal()) {
400  CERATA_LOG(FATAL, "Instance Graph cannot own Signal nodes. " + node->ToString());
401  }
402  }
403  Graph::Add(object);
404  object->SetParent(this);
405  return *this;
406 }
407 
409  parent_ = parent;
410  return *this;
411 }
412 
413 } // namespace cerata
cerata::Component::was_instantiated
bool was_instantiated
Whether this component was instantiated.
Definition: graph.h:212
cerata::Component::GetAllInstanceComponents
virtual std::vector< const Component * > GetAllInstanceComponents() const
Returns all unique Components that are referred to by child Instances of this graph.
Definition: graph.cc:268
cerata::Instance::parent_
Graph * parent_
The parent of this instance.
Definition: graph.h:254
cerata::Component
A Component graph.
Definition: graph.h:158
cerata::Graph::Add
virtual Graph & Add(const std::shared_ptr< Object > &object)
Add an object to the component.
Definition: graph.cc:32
cerata::Graph::sig_arr
SignalArray * sig_arr(const std::string &name) const
Shorthand to Get<SignalArray>(...)
Definition: graph.cc:183
cerata::Graph::GetAll
std::vector< T * > GetAll() const
Get all objects of a specific type.
Definition: graph.h:63
cerata::SignalArray
An array of signal nodes.
Definition: array.h:100
cerata::Component::Add
Graph & Add(const std::shared_ptr< Object > &object) override
Add an object to the component.
Definition: graph.cc:361
cerata::Object::IsNode
bool IsNode() const
Return true if this object is a node.
Definition: object.h:52
cerata::Instance::parent
Graph * parent() const
Return the parent graph.
Definition: graph.h:238
cerata::Component::Instantiate
Instance * Instantiate(Component *comp, const std::string &name="")
Add an Instance of another Component to this component.
Definition: graph.cc:295
cerata::Instance::component
Component * component() const
Return the component this is an instance of.
Definition: graph.h:236
cerata::Graph
A graph representing a hardware structure.
Definition: graph.h:37
cerata::Graph::meta_
std::unordered_map< std::string, std::string > meta_
KV storage for metadata of tools or specific backend implementations.
Definition: graph.h:150
cerata::Instance::Add
Graph & Add(const std::shared_ptr< Object > &obj) override
Add a node to the component, throwing an exception if the node is a signal.
Definition: graph.cc:396
cerata::FilterDuplicates
void FilterDuplicates(std::vector< T > *vec)
Filter duplicate entries from a vector.
Definition: utils.h:174
cerata::ComponentPool
A pool of Components.
Definition: pool.h:86
cerata::Signal
A Signal Node.
Definition: signal.h:30
cerata
Contains every Cerata class, function, etc...
Definition: api.h:41
cerata::Graph::CountArrays
size_t CountArrays(Node::NodeID id) const
Count nodes of a specific array type.
Definition: graph.cc:128
cerata::Graph::objects_
std::vector< std::shared_ptr< Object > > objects_
Graph objects.
Definition: graph.h:148
cerata::Instance::Make
static std::unique_ptr< Instance > Make(Component *component, const std::string &name, Component *parent)
Create an instance.
Definition: graph.cc:373
cerata::Graph::GetNodesOfTypes
std::vector< Node * > GetNodesOfTypes(std::initializer_list< Node::NodeID > ids) const
Obtain all nodes which ids are in a list of Node::IDs.
Definition: graph.cc:202
cerata::Graph::GetImplicitNodes
std::vector< Node * > GetImplicitNodes() const
Return all graph nodes that do not explicitly belong to the graph.
Definition: graph.cc:187
cerata::Instance::comp_to_inst
NodeMap comp_to_inst
Mapping from component port and parameter nodes to instantiated nodes.
Definition: graph.h:256
cerata::Node
A node.
Definition: node.h:42
cerata::Object::IsArray
bool IsArray() const
Return true if this object is an array.
Definition: object.h:54
cerata::Component::children
std::vector< Instance * > children() const
Returns all Instance graphs from this Component.
Definition: graph.h:187
cerata::Instance::component_
Component * component_
The component that this instance instantiates.
Definition: graph.h:252
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::Graph::prt_arr
PortArray * prt_arr(const std::string &name) const
Shorthand to Get<PortArray>(...)
Definition: graph.cc:179
cerata::Component::Remove
Graph & Remove(Object *object) override
Remove an object from the component.
Definition: graph.cc:366
cerata::component
std::shared_ptr< Component > component(std::string name, const std::vector< std::shared_ptr< Object >> &objects, ComponentPool *component_pool)
Construct a Component with initial nodes.
Definition: graph.cc:248
cerata::Graph::prt
Port * prt(const std::string &name) const
Shorthand to Get<Port>(...)
Definition: graph.cc:159
cerata::Graph::par
Parameter * par(const std::string &name) const
Shorthand to Get<Parameter>(...)
Definition: graph.cc:167
cerata::Instance::SetParent
Graph & SetParent(Graph *parent)
Set the parent.
Definition: graph.cc:408
cerata::Graph::SetMeta
Graph & SetMeta(const std::string &key, std::string value)
Set metadata.
Definition: graph.cc:215
cerata::Object
A Cerata Object on a graph.
Definition: object.h:34
cerata::Component::children_
std::vector< std::unique_ptr< Instance > > children_
Instances.
Definition: graph.h:209
cerata::Graph::sig
Signal * sig(const std::string &name) const
Shorthand to Get<Signal>(...)
Definition: graph.cc:163
cerata::Graph::GetArraysOfType
std::vector< NodeArray * > GetArraysOfType(Node::NodeID id) const
Get all arrays of a specific type.
Definition: graph.cc:148
cerata::Graph::FindNode
std::optional< Node * > FindNode(const std::string &node_name) const
Find a node with a specific name.
Definition: graph.cc:100
cerata::Parameter
A Parameter node.
Definition: parameter.h:29
cerata::Graph::ToStringAllOjects
std::string ToStringAllOjects() const
Return a comma separated list of object names.
Definition: graph.cc:229
cerata::Instance
An instance.
Definition: graph.h:231
cerata::Component::HasChild
bool HasChild(const std::string &name) const
Return true if child graph exists on instance.
Definition: graph.cc:322
cerata::Graph::GetNodesOfType
std::vector< Node * > GetNodesOfType(Node::NodeID id) const
Get all nodes of a specific type.
Definition: graph.cc:138
cerata::Component::AddChild
Component & AddChild(std::unique_ptr< Instance > child)
Add and take ownership of an Instance graph.
Definition: graph.cc:240
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::Graph::Has
bool Has(const std::string &name)
Return true if object with name already exists on graph.
Definition: graph.cc:220
cerata::Graph::objects
std::vector< Object * > objects() const
Get all objects.
Definition: graph.h:131
cerata::Pool::Add
void Add(const std::shared_ptr< T > &object)
Add an object to the pool, taking shared ownership. Object may not already exist in the pool.
Definition: pool.h:42
cerata::PortArray
An array of port nodes.
Definition: array.h:123
cerata::Graph::GetNode
Node * GetNode(const std::string &node_name) const
Get a Node of a specific type with a specific name.
Definition: graph.cc:109
cerata::Graph::CountNodes
size_t CountNodes(Node::NodeID id) const
Count nodes of a specific node type.
Definition: graph.cc:118
cerata::Graph::Remove
virtual Graph & Remove(Object *obj)
Remove an object from the component.
Definition: graph.cc:91
cerata::Instance::Instance
Instance(Component *comp, std::string name, Component *parent)
Construct an Instance of a Component, copying over all its ports and parameters.
Definition: graph.cc:378
cerata::port_array
std::shared_ptr< PortArray > port_array(const std::string &name, const std::shared_ptr< Type > &type, const std::shared_ptr< Node > &size, Port::Dir dir, const std::shared_ptr< ClockDomain > &domain)
Get a smart pointer to a new ArrayPort.
Definition: array.cc:175
cerata::Port
A port is a terminator node on a graph.
Definition: port.h:57
cerata::Component::inst_to_comp
NodeMap inst_to_comp
Mapping for instance nodes that have been connected.
Definition: graph.h:215
cerata::port
std::shared_ptr< Port > port(const std::string &name, const std::shared_ptr< Type > &type, Term::Dir dir, const std::shared_ptr< ClockDomain > &domain)
Make a new port with some name, type and direction.
Definition: port.cc:22