Cerata
A library to generate structural hardware designs
array.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/array.h"
16 
17 #include <optional>
18 #include <utility>
19 #include <vector>
20 #include <memory>
21 #include <string>
22 
23 #include "cerata/edge.h"
24 #include "cerata/node.h"
25 #include "cerata/expression.h"
26 #include "cerata/parameter.h"
27 #include "cerata/pool.h"
28 #include "cerata/graph.h"
29 
30 namespace cerata {
31 
32 static std::shared_ptr<Node> IncrementNode(Node *node) {
33  if (node->IsLiteral() || node->IsExpression()) {
34  return node->shared_from_this() + 1;
35  } else if (node->IsParameter()) {
36  // If the node is a parameter, we should be able to trace its source back to a literal node.
37  // We then replace the last parameter node in the trace by a copy and source the copy from an incremented literal.
38  auto param = dynamic_cast<Parameter *>(node);
39  // Initialize the trace with the parameter node.
40  std::vector<Node *> value_trace;
41  param->TraceValue(&value_trace);
42  // Sanity check the trace.
43  if (!value_trace.back()->IsLiteral()) {
44  CERATA_LOG(FATAL, "Parameter node " + param->ToString() + " not (indirectly) sourced by literal.");
45  }
46  // The second-last node is of importance, because this is the final parameter node.
47  auto second_last = value_trace[value_trace.size() - 2];
48  auto incremented = value_trace.back()->shared_from_this() + 1;
49  // Source the second last node with whatever literal was at the end of the trace, plus one.
50  Connect(second_last, incremented);
51  return node->shared_from_this();
52  } else {
53  CERATA_LOG(FATAL, "Can only increment literal, expression or parameter size node " + node->ToString());
54  }
55 }
56 
57 void NodeArray::SetSize(const std::shared_ptr<Node> &size) {
58  if (!(size->IsLiteral() || size->IsParameter() || size->IsExpression())) {
59  CERATA_LOG(FATAL, "NodeArray size node must be literal, parameter or expression.");
60  }
61  if (size->IsParameter()) {
62  auto param = size->AsParameter();
63  if (param->node_array_parent) {
64  auto na = size->AsParameter()->node_array_parent.value();
65  if (na != this) {
66  CERATA_LOG(FATAL, "NodeArray size can only be used by a single NodeArray.");
67  }
68  }
69  param->node_array_parent = this;
70  }
71  size_ = size;
72 }
73 
75  SetSize(IncrementNode(size()));
76 }
77 
78 std::shared_ptr<Node> NodeArray::Append(bool increment_size) {
79  // Create a new copy of the base node.
80  auto new_node = std::dynamic_pointer_cast<Node>(base_->Copy());
81  if (parent()) {
82  new_node->SetParent(*parent());
83  }
84  new_node->SetArray(this);
85  nodes_.push_back(new_node);
86 
87  // Increment this NodeArray's size node.
88  if (increment_size) {
89  IncrementSize();
90  }
91 
92  // Return the new node.
93  return new_node;
94 }
95 
96 Node *NodeArray::node(size_t i) const {
97  if (i < nodes_.size()) {
98  return nodes_[i].get();
99  } else {
100  CERATA_LOG(FATAL, "Index " + std::to_string(i) + " out of bounds for node " + ToString());
101  }
102 }
103 
106  base_->SetParent(parent);
107  for (const auto &e : nodes_) {
108  e->SetParent(parent);
109  }
110 }
111 
112 size_t NodeArray::IndexOf(const Node &n) const {
113  for (size_t i = 0; i < nodes_.size(); i++) {
114  if (nodes_[i].get() == &n) {
115  return i;
116  }
117  }
118  CERATA_LOG(FATAL, "Node " + n.ToString() + " is not element of " + this->ToString());
119 }
120 
121 void NodeArray::SetType(const std::shared_ptr<Type> &type) {
122  base_->SetType(type);
123  for (auto &n : nodes_) {
124  n->SetType(type);
125  }
126 }
127 
128 NodeArray::NodeArray(std::string name, Node::NodeID id, std::shared_ptr<Node> base, const std::shared_ptr<Node> &size)
129  : Object(std::move(name), Object::ARRAY), node_id_(id), base_(std::move(base)) {
130  base_->SetArray(this);
131  SetSize(size);
132 }
133 
134 std::shared_ptr<Object> NodeArray::Copy() const {
135  auto ret = std::make_shared<NodeArray>(name(), node_id_, base_, intl(0));
136  return ret;
137 }
138 
139 NodeArray *NodeArray::CopyOnto(Graph *dst, const std::string &name, NodeMap *rebinding) {
140  // Make a normal copy (that does not rebind the type generics).
141  auto result = std::dynamic_pointer_cast<NodeArray>(this->Copy());
142  result->SetName(name);
143 
144  // Figure out the right size node.
145  std::shared_ptr<Node> new_size = result->size_;
146  if (size_->IsParameter()) {
147  if (rebinding->count(size_.get()) == 0) {
148  CERATA_LOG(FATAL, "Size node parameters of NodeArray " + size_->name()
149  + " must be in rebind map before NodeArray can be copied.");
150  } else {
151  result->SetSize(rebinding->at(size_.get())->shared_from_this());
152  }
153  }
154 
155  // Obtains the references of the base type.
156  auto base_generics = base_->type()->GetGenerics();
157  if (!base_generics.empty()) {
158  ImplicitlyRebindNodes(dst, base_generics, rebinding);
159  // Make a copy of the type, rebinding the generic nodes.
160  auto rebound_base_type = result->type()->Copy(*rebinding);
161  // Set the type of the new node to this new type.
162  result->base_->SetType(rebound_base_type);
163  }
164 
165  // It should now be possible to add the copy of the array onto the graph.
166  dst->Add(result);
167 
168  return result.get();
169 }
170 
171 PortArray::PortArray(const std::shared_ptr<Port> &base,
172  const std::shared_ptr<Node> &size) :
173  NodeArray(base->name(), Node::NodeID::PORT, base, size), Term(base->dir()) {}
174 
175 std::shared_ptr<PortArray> port_array(const std::string &name,
176  const std::shared_ptr<Type> &type,
177  const std::shared_ptr<Node> &size,
178  Port::Dir dir,
179  const std::shared_ptr<ClockDomain> &domain) {
180  auto base_node = port(name, type, dir, domain);
181  auto *port_array = new PortArray(base_node, size);
182  return std::shared_ptr<PortArray>(port_array);
183 }
184 
185 std::shared_ptr<PortArray> port_array(const std::shared_ptr<Port> &base_node,
186  const std::shared_ptr<Node> &size) {
187  auto *port_array = new PortArray(base_node, size);
188  return std::shared_ptr<PortArray>(port_array);
189 }
190 
191 std::shared_ptr<Object> PortArray::Copy() const {
192  // Create the new PortArray using the new nodes.
193  auto result = port_array(name(), base_->type()->shared_from_this(), intl(0), dir_, *GetDomain(*base_));
194  // Return the resulting object.
195  return result;
196 }
197 
198 std::shared_ptr<SignalArray> signal_array(const std::string &name,
199  const std::shared_ptr<Type> &type,
200  const std::shared_ptr<Node>& size,
201  const std::shared_ptr<ClockDomain> &domain) {
202  auto base_node = signal(name, type, domain);
203  auto *sig_array = new SignalArray(base_node, size);
204  return std::shared_ptr<SignalArray>(sig_array);
205 }
206 
207 } // namespace cerata
cerata::NodeArray::node_id_
Node::NodeID node_id_
The type ID of the nodes in this NodeArray.
Definition: array.h:88
cerata::NodeArray::type
Type * type() const
Return the type of the nodes in the NodeArray.
Definition: array.h:50
cerata::Graph::Add
virtual Graph & Add(const std::shared_ptr< Object > &object)
Add an object to the component.
Definition: graph.cc:32
cerata::NodeArray::SetParent
void SetParent(Graph *new_parent) override
Set the parent of this NodeArray base node and array nodes.
Definition: array.cc:104
cerata::SignalArray
An array of signal nodes.
Definition: array.h:100
cerata::NodeArray::CopyOnto
NodeArray * CopyOnto(Graph *dst, const std::string &name, NodeMap *rebinding)
Copy the NodeArray onto a graph, but not the array nodes. Creates a new size node set to zero.
Definition: array.cc:139
cerata::GetDomain
std::optional< std::shared_ptr< ClockDomain > > GetDomain(const Node &node)
Return the clock domain of a node, if it is a synchronous node.
Definition: domain.cc:33
cerata::NodeArray::base_
std::shared_ptr< Node > base_
A node representing the template for each of the element nodes.
Definition: array.h:92
cerata::NodeArray::Append
std::shared_ptr< Node > Append(bool increment_size=true)
Append a node to this array, optionally incrementing the size node. Returns a pointer to that node.
Definition: array.cc:78
cerata::Term
A terminator structure to enable terminator sanity checks.
Definition: port.h:27
cerata::Graph
A graph representing a hardware structure.
Definition: graph.h:37
cerata::NodeArray
An array of nodes.
Definition: array.h:31
cerata::Object::SetParent
virtual void SetParent(Graph *parent)
Set the parent graph of this object.
Definition: object.cc:25
cerata
Contains every Cerata class, function, etc...
Definition: api.h:41
cerata::NodeArray::nodes_
std::vector< std::shared_ptr< Node > > nodes_
The nodes contained by this array.
Definition: array.h:96
cerata::Node
A node.
Definition: node.h:42
cerata::Term::Dir
Dir
Terminator direction.
Definition: port.h:30
cerata::NodeArray::Copy
std::shared_ptr< Object > Copy() const override
Deep-copy the NodeArray, but not the array nodes. Resets the size node to an integer literal of 0.
Definition: array.cc:134
cerata::NodeMap
std::unordered_map< const Node *, Node * > NodeMap
A mapping from one object to another object, used in e.g. type generic rebinding.
Definition: node.h:135
cerata::NodeArray::ToString
std::string ToString() const
Return a human-readable representation of this NodeArray.
Definition: array.h:72
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::PortArray::PortArray
PortArray(const std::shared_ptr< Port > &base, const std::shared_ptr< Node > &size)
Construct a new port array.
Definition: array.cc:171
cerata::intl
std::shared_ptr< Literal > intl(int64_t i)
Obtain a shared pointer to an integer literal from the default node pool.
Definition: pool.h:144
cerata::Object
A Cerata Object on a graph.
Definition: object.h:34
cerata::NodeArray::IndexOf
size_t IndexOf(const Node &n) const
Return the index of a specific node.
Definition: array.cc:112
cerata::PortArray::Copy
std::shared_ptr< Object > Copy() const override
Make a copy of this port array.
Definition: array.cc:191
cerata::NodeArray::SetSize
void SetSize(const std::shared_ptr< Node > &size)
Set the size node.
Definition: array.cc:57
cerata::NodeArray::IncrementSize
void IncrementSize()
Increment the size of the ArrayNode.
Definition: array.cc:74
cerata::Node::ToString
virtual std::string ToString() const
Return a human-readable string of this node.
Definition: node.cc:35
cerata::signal_array
std::shared_ptr< SignalArray > signal_array(const std::string &name, const std::shared_ptr< Type > &type, const std::shared_ptr< Node > &size, const std::shared_ptr< ClockDomain > &domain)
Construct a new node array and return a shared pointer to it.
Definition: array.cc:198
cerata::NodeArray::SetType
void SetType(const std::shared_ptr< Type > &type)
Set the type of the base node and array nodes.
Definition: array.cc:121
cerata::signal
std::shared_ptr< Signal > signal(const std::string &name, const std::shared_ptr< Type > &type, const std::shared_ptr< ClockDomain > &domain)
Create a new Signal and return a smart pointer to it.
Definition: signal.cc:36
cerata::PortArray
An array of port nodes.
Definition: array.h:123
cerata::Term::dir_
Dir dir_
The direction of this terminator.
Definition: port.h:51
cerata::NodeArray::size_
std::shared_ptr< Node > size_
A node representing the number of concatenated edges.
Definition: array.h:94
cerata::NodeArray::size
Node * size() const
Return the size node.
Definition: array.h:43
cerata::NodeArray::NodeArray
NodeArray(std::string name, Node::NodeID id, std::shared_ptr< Node > base, const std::shared_ptr< Node > &size)
ArrayNode constructor.
Definition: array.cc:128
cerata::NodeArray::node
Node * node(size_t i) const
Return element node i.
Definition: array.cc:96
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::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::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
cerata::ImplicitlyRebindNodes
void ImplicitlyRebindNodes(Graph *dst, const std::vector< Node * > &nodes, NodeMap *rebinding)
Make sure that the NodeMap contains all nodes to be rebound onto the destination graph.
Definition: node.cc:39
cerata::Connect
std::shared_ptr< Edge > Connect(Node *dst, Node *src)
Connect two nodes, returns the corresponding edge.
Definition: edge.cc:68