Cerata
A library to generate structural hardware designs
instantiation.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 <vector>
16 #include <string>
17 
18 #include "cerata/logging.h"
19 #include "cerata/edge.h"
20 #include "cerata/node.h"
21 #include "cerata/expression.h"
22 #include "cerata/array.h"
23 #include "cerata/type.h"
24 #include "cerata/graph.h"
25 #include "cerata/pool.h"
26 #include "cerata/parameter.h"
27 
28 #include "cerata/vhdl/instantiation.h"
29 #include "cerata/vhdl/identifier.h"
30 #include "cerata/vhdl/vhdl.h"
31 
32 namespace cerata::vhdl {
33 
34 static std::string lit2vhdl(const Literal &lit) {
35  switch (lit.type()->id()) {
36  default:return lit.ToString();
37  case Type::STRING:
38  // If it is a string enclose it within quotes
39  return "\"" + lit.ToString() + "\"";
40  case Type::BOOLEAN:
41  // Convert to VHDL boolean
42  if (lit.BoolValue()) {
43  return "true";
44  } else {
45  return "false";
46  }
47  }
48 }
49 
50 static bool IsInputTerminator(const Object &obj) {
51  try {
52  auto term = dynamic_cast<const Term &>(obj);
53  return term.IsInput();
54  }
55  catch (const std::bad_cast &e) {
56  return false;
57  }
58 }
59 
61  Block ret;
62  Line l;
63  l << ToUpper(par.name()) << " => ";
64  // Get the value to apply
65  auto val = par.value();
66  // If it is a literal, make it VHDL compatible
67  if (val->IsLiteral()) {
68  auto *lit = dynamic_cast<const Literal *>(val);
69  l << lit2vhdl(*lit);
70  } else {
71  l << ToUpper(val->ToString());
72  }
73  ret << l;
74  return ret;
75 }
76 
77 static Block GenerateMappingPair(const MappingPair &p,
78  size_t ia,
79  const std::shared_ptr<Node> &offset_a,
80  size_t ib,
81  const std::shared_ptr<Node> &offset_b,
82  const std::string &lh_prefix,
83  const std::string &rh_prefix,
84  bool a_is_array,
85  bool b_is_array,
86  bool full_array) {
87  Block ret;
88 
89  std::shared_ptr<Node> next_offset_a;
90  std::shared_ptr<Node> next_offset_b;
91 
92  auto a_width = p.flat_type_a(ia).type_->width();
93  auto b_width = p.flat_type_b(ib).type_->width();
94 
95  next_offset_a = (offset_a + (b_width ? b_width.value() : rintl(0)));
96  next_offset_b = (offset_b + (a_width ? a_width.value() : rintl(0)));
97 
98  if (p.flat_type_a(0).type_->Is(Type::RECORD)) {
99  // Don't output anything for the abstract record type.
100  } else {
101  Line l;
102  l << p.flat_type_a(ia).name(NamePart(lh_prefix, true));
103  // if right side is concatenated onto the left side
104  // or the left side is an array (right is also concatenated onto the left side)
105  if ((p.num_b() > 1) || (a_is_array && !full_array)) {
106  if (p.flat_type_a(ia).type_->Is(Type::BIT)) {
107  l += "(" + offset_a->ToString() + ")";
108  } else {
109  l += "(" + (next_offset_a - 1)->ToString();
110  l += " downto " + offset_a->ToString() + ")";
111  }
112  }
113  l << " => ";
114  l << p.flat_type_b(ib).name(NamePart(rh_prefix, true));
115  if ((p.num_a() > 1) || (b_is_array && !full_array)) {
116  if (p.flat_type_b(ib).type_->Is(Type::BIT)) {
117  l += "(" + offset_b->ToString() + ")";
118  } else {
119  l += "(" + (next_offset_b - 1)->ToString();
120  l += " downto " + offset_b->ToString() + ")";
121  }
122  }
123  ret << l;
124  }
125 
126  return ret;
127 }
128 
129 static Block GeneratePortMappingPair(std::vector<MappingPair> pairs, const Node &a, const Node &b, bool full_array) {
130  Block ret;
131  // Sort the pair in order of appearance on the flat map
132  std::sort(pairs.begin(), pairs.end(), [](const MappingPair &x, const MappingPair &y) -> bool {
133  return x.index_a(0) < y.index_a(0);
134  });
135  bool a_array = false;
136  bool b_array = false;
137  size_t a_idx = 0;
138  size_t b_idx = 0;
139  // Figure out if these nodes are on NodeArrays and what their index is
140  if (a.array()) {
141  a_array = true;
142  a_idx = a.array().value()->IndexOf(a);
143  }
144  if (b.array()) {
145  b_array = true;
146  b_idx = b.array().value()->IndexOf(b);
147  }
148  if (a.type()->meta.count(meta::FORCE_VECTOR) > 0) {
149  a_array = true;
150  }
151  if (b.type()->meta.count(meta::FORCE_VECTOR) > 0) {
152  b_array = true;
153  }
154  // Loop over all pairs
155  for (const auto &pair : pairs) {
156  // Offset on the right side
157  std::shared_ptr<Node> b_offset = pair.width_a(intl(1)) * intl(b_idx);
158  // Loop over everything on the left side
159  for (int64_t ia = 0; ia < pair.num_a(); ia++) {
160  // Get the width of the left side.
161  auto a_width = pair.flat_type_a(ia).type_->width();
162  // Offset on the left side.
163  std::shared_ptr<Node> a_offset = pair.width_b(intl(1)) * intl(a_idx);
164  for (int64_t ib = 0; ib < pair.num_b(); ib++) {
165  // Get the width of the right side.
166  auto b_width = pair.flat_type_b(ib).type_->width();
167  // Generate the mapping pair with given offsets
168  auto mpblock =
169  GenerateMappingPair(pair, ia, a_offset, ib, b_offset, a.name(), b.name(), a_array, b_array, full_array);
170  ret << mpblock;
171  // Increase the offset on the left side.
172  a_offset = a_offset + (b_width ? b_width.value() : rintl(1));
173  }
174  // Increase the offset on the right side.
175  b_offset = b_offset + (a_width ? a_width.value() : rintl(1));
176  }
177  }
178  return ret;
179 }
180 
181 Block Inst::GeneratePortMaps(const Port &port, bool full_array) {
182  Block result;
183  std::vector<Edge *> connections;
184  // Check if this is an input or output port
185  if (IsInputTerminator(port)) {
186  connections = port.sources();
187  } else {
188  connections = port.sinks();
189  }
190  // Get the port type.
191  auto port_type = port.type();
192  // Iterate over all connected edges
193  for (const auto &edge : connections) {
194  // Get the node on the other side of the connection
195  auto other = *edge->GetOtherNode(port);
196  // Get the other type.
197  auto other_type = other->type();
198  // Check if a type mapping exists
199  auto optional_type_mapper = port_type->GetMapper(other_type);
200  if (optional_type_mapper) {
201  auto type_mapper = optional_type_mapper.value();
202  // Obtain the unique mapping pairs for this mapping
203  auto pairs = type_mapper->GetUniqueMappingPairs();
204  // Generate the mapping for this port-node pair.
205  result << GeneratePortMappingPair(pairs, port, *other, full_array);
206  } else {
207  CERATA_LOG(FATAL, "No type mapping available for: Port[" + port.name() + ": " + port.type()->name()
208  + "] to Other[" + other->name() + " : " + other->type()->name() + "]");
209  }
210  }
211  return result;
212 }
213 
215  Block ret;
216  // Figure out if this whole array is connected to a single other array.
217  std::vector<NodeArray *> others;
218  for (const auto &node : port_array.nodes()) {
219  for (const auto &e : node->edges()) {
220  auto other = e->GetOtherNode(*node);
221  if (other) {
222  if (other.value()->array()) {
223  if (other.value()->array().value()->IsArray()) {
224  auto na = dynamic_cast<NodeArray *>(other.value()->array().value());
225  others.push_back(na);
226  }
227  }
228  }
229  }
230  }
231  // Remove duplicates.
232  others.erase(std::unique(others.begin(), others.end()), others.end());
233  bool full_array = others.size() == 1;
234 
235  // Go over each node in the array
236  for (const auto &node : port_array.nodes()) {
237  const auto &port = dynamic_cast<const Port &>(*node);
238  ret << GeneratePortMaps(port, full_array);
239  if (full_array) {
240  break;
241  }
242  }
243  return ret.Sort('(');
244 }
245 
247  MultiBlock ret(1);
248 
249  if (!graph.IsInstance()) {
250  return ret;
251  }
252  auto &inst = dynamic_cast<const Instance &>(graph);
253 
254  Block ih(ret.indent); // Instantiation header
255  Block gmh(ret.indent + 1); // generic map header
256  Block gmb(ret.indent + 2); // generic map body
257  Block gmf(ret.indent + 1); // generic map footer
258  Block pmh(ret.indent + 1); // port map header
259  Block pmb(ret.indent + 2); // port map body
260  Block pmf(ret.indent + 1); // port map footer
261 
262  ih << inst.name() + " : " + inst.component()->name();
263 
264  // Generic map
265  if (inst.CountNodes(Node::NodeID::PARAMETER) > 0) {
266  Line gh, gf;
267  gh << "generic map (";
268  gmh << gh;
269  for (Parameter *g : inst.GetAll<Parameter>()) {
270  gmb << GenerateGenericMap(*g);
271  }
272  gmb <<= ",";
273  gf << ")";
274  gmf << gf;
275  }
276 
277  auto num_ports = inst.CountNodes(Node::NodeID::PORT) + inst.CountArrays(Node::NodeID::PORT);
278  if (num_ports > 0) {
279  // Port map
280  Line ph, pf;
281  ph << "port map (";
282  pmh << ph;
283  for (const auto &p : inst.GetAll<Port>()) {
284  Block pm;
285  pm << GeneratePortMaps(*p);
286  pmb << pm;
287  }
288  for (const auto &a : inst.GetAll<PortArray>()) {
289  Block pm;
290  pm << GeneratePortArrayMaps(*a);
291  pmb << pm;
292  }
293  pmb <<= ",";
294  pf << ");";
295  pmf << pf;
296  }
297 
298  ret << ih;
299  ret << gmh << gmb << gmf;
300  ret << pmh << pmb << pmf;
301 
302  return ret;
303 }
304 
305 } // namespace cerata::vhdl
cerata::Type::RECORD
@ RECORD
? | ? | Yes
Definition: type.h:77
cerata::Type::BOOLEAN
@ BOOLEAN
No | No | No.
Definition: type.h:75
cerata::vhdl::MultiBlock
A structure to hold multiple blocks.
Definition: block.h:77
cerata::Literal
A Literal Node.
Definition: literal.h:36
cerata::Parameter::value
Node * value() const
Return the value node.
Definition: parameter.cc:68
cerata::Graph::IsInstance
bool IsInstance() const
Return true if this graph is an instance, false otherwise.
Definition: graph.h:53
cerata::MappingPair::flat_type_a
FlatType flat_type_a(int64_t i) const
Return the i-th FlatType on the "a"-side in the mapping matrix.
Definition: flattype.h:299
cerata::Graph
A graph representing a hardware structure.
Definition: graph.h:37
cerata::FlatType::type_
Type * type_
A pointer to the original type.
Definition: flattype.h:56
cerata::NodeArray
An array of nodes.
Definition: array.h:31
cerata::Type::BIT
@ BIT
Yes | No | No.
Definition: type.h:70
cerata::vhdl::Line
A line of code.
Definition: block.h:25
cerata::vhdl::Inst::GeneratePortArrayMaps
static Block GeneratePortArrayMaps(const PortArray &array)
Generate an associativity list for an instantiated PortArray.
Definition: instantiation.cc:214
cerata::vhdl::Inst::GeneratePortMaps
static Block GeneratePortMaps(const Port &port, bool full_array=false)
Generate an associativity list for an instantiated Port.
Definition: instantiation.cc:181
cerata::NamePart
Convenience struct to generate names in parts.
Definition: flattype.h:40
cerata::vhdl::MultiBlock::indent
int indent
Indent level.
Definition: block.h:85
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::vhdl::Inst::Generate
static MultiBlock Generate(const Graph &graph)
Generate a VHDL instantiation of a graph.
Definition: instantiation.cc:246
cerata::Named::name
std::string name() const
Return the name of the object.
Definition: utils.h:45
cerata::Type::STRING
@ STRING
No | No | No.
Definition: type.h:74
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::MappingPair::num_b
int64_t num_b() const
Return the number of FlatTypes on the "b"-side.
Definition: flattype.h:289
cerata::ToUpper
std::string ToUpper(std::string str)
Convert string to upper-case.
Definition: utils.cc:25
cerata::MappingPair::flat_type_b
FlatType flat_type_b(int64_t i) const
Return the i-th FlatType on the "b"-side in the mapping matrix.
Definition: flattype.h:301
cerata::Type::width
virtual std::optional< Node * > width() const
Return the width of the type, if it is synthesizable.
Definition: type.h:106
cerata::FlatType::name
std::string name(const NamePart &root=NamePart(), const std::string &sep="_") const
Return the name of this flattened type, constructed from the name parts.
Definition: flattype.cc:32
cerata::vhdl::ToString
std::string ToString(const std::vector< Block > &blocks)
Return a vector of blocks as a single string.
Definition: block.cc:186
cerata::Parameter
A Parameter node.
Definition: parameter.h:29
cerata::rintl
Literal * rintl(int64_t i)
Obtain a raw pointer to an integer literal from the default node pool.
Definition: pool.h:140
cerata::vhdl::Block
A block of code.
Definition: block.h:50
cerata::Instance
An instance.
Definition: graph.h:231
cerata::vhdl::Block::Sort
Block & Sort(std::optional< char > c=std::nullopt)
Sort the lines in the block. Supply a character to stop sorting per line after encountering the chara...
Definition: block.cc:85
cerata::MappingPair
A structure representing a mapping pair for a type mapping.
Definition: flattype.h:276
cerata::PortArray
An array of port nodes.
Definition: array.h:123
cerata::vhdl::Inst::GenerateGenericMap
static Block GenerateGenericMap(const Parameter &par)
Generate an associativity list for an instantiated Parameter.
Definition: instantiation.cc:60
cerata::Node::NodeID::PORT
@ PORT
Single-input AND multi-output node with direction.
cerata::NodeArray::size
Node * size() const
Return the size node.
Definition: array.h:43
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::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::vhdl
Contains everything related to the VHDL back-end.
Definition: architecture.cc:31
cerata::MappingPair::num_a
int64_t num_a() const
Return the number of FlatTypes on the "a"-side.
Definition: flattype.h:287