 |
Cerata
A library to generate structural hardware designs
|
15 #include "cerata/edge.h"
23 #include "cerata/graph.h"
24 #include "cerata/node.h"
25 #include "cerata/logging.h"
30 :
Named(std::move(name)), dst_(dst), src_(src) {
31 if ((
dst ==
nullptr) || (
src ==
nullptr)) {
32 CERATA_LOG(FATAL,
"Cannot construct edge with nullptr nodes.");
36 std::shared_ptr<Edge>
Edge::Make(
const std::string &name,
40 return std::shared_ptr<Edge>(e);
43 static void CheckDomains(
Node *src,
Node *dst) {
44 if ((src->IsPort() || src->IsSignal()) && (dst->IsPort() || dst->IsSignal())) {
45 auto src_dom =
dynamic_cast<Synchronous *
>(src)->domain();
46 auto dst_dom =
dynamic_cast<Synchronous *
>(dst)->domain();
47 if (src_dom != dst_dom) {
48 std::stringstream warning;
49 warning <<
"Attempting to connect Synchronous nodes, but clock domains differ.\n";
51 warning <<
"Src: [" + src->
ToString() +
"] in domain: [" + dst_dom->name() +
"]";
53 warning <<
" on parent: [" + src->
parent().value()->name() +
"]";
56 warning <<
"\nDst: [" + dst->
ToString() +
"] in domain: [" + src_dom->name() +
"]";
58 warning <<
" on parent: [" + dst->
parent().value()->name() +
"]";
61 warning <<
"\nAutomated CDC crossings are not yet implemented or instantiated.";
62 warning <<
"This behavior may cause incorrect designs.";
63 CERATA_LOG(WARNING, warning.str());
71 CERATA_LOG(FATAL,
"Source node is null");
73 }
else if (dst ==
nullptr) {
74 CERATA_LOG(FATAL,
"Destination node is null");
80 CheckDomains(src, dst);
83 if (src->IsPort() || src->IsSignal()) {
86 CERATA_LOG(ERROR,
"No known type mapping available for connection between node ["
95 auto sp = src->
parent().value();
96 auto dp = dst->
parent().value();
97 if (dp->IsComponent()) {
98 if (sp->IsComponent() && (sp != dp)) {
100 CERATA_LOG(ERROR,
"Edge between component " + dp->name() +
" node " + dst->
name() +
101 " and component " + sp->name() +
" node " + src->
name() +
" not allowed.");
103 auto si =
dynamic_cast<Instance *
>(sp);
106 if (dc->HasChild(*si) && src->IsParameter()) {
107 CERATA_LOG(ERROR,
"Instance parameters can not source component nodes.");
112 auto dp = dst->
parent().value();
113 if (dp->IsInstance()) {
114 auto ip =
dynamic_cast<Instance *
>(dp);
118 auto map =
dynamic_cast<Component *
>(ip->parent())->inst_to_comp_map();
125 auto port =
dynamic_cast<Port *
>(dst);
128 auto parent = *dst->
parent();
129 if (parent->IsInstance() &&
port->IsOutput()) {
132 "Cannot drive instance " + dst->
parent().value()->ToString() +
" port " + dst->
ToString()
133 +
" of mode output with " + src->
ToString());
134 }
else if (parent->IsComponent() &&
port->IsInput()) {
137 "Cannot drive component " + dst->
parent().value()->ToString() +
" port " + dst->
ToString()
138 +
" of mode input with " + src->
ToString());
145 auto port =
dynamic_cast<Port *
>(src);
148 auto parent = *src->
parent();
149 if (parent->IsInstance() &&
port->IsInput()) {
152 "Cannot source from instance port " + src->
ToString() +
" of mode input on " + parent->ToString());
153 }
else if (parent->IsComponent() &&
port->IsOutput()) {
156 "Cannot source from component port " + src->
ToString() +
" of mode output on " + parent->ToString());
161 std::string edge_name = src->
name() +
"_to_" + dst->
name();
168 std::shared_ptr<Edge>
operator<<=(Node *dst,
const std::shared_ptr<Node> &src) {
169 return Connect(dst, src.get());
172 std::shared_ptr<Edge>
operator<<=(
const std::shared_ptr<Node> &dst,
const std::shared_ptr<Node> &src) {
173 return Connect(dst.get(), src.get());
176 std::shared_ptr<Edge>
operator<<=(
const std::shared_ptr<Node> &dst, Node *src) {
177 return Connect(dst.get(), src);
181 std::vector<Edge *> all_edges;
184 for (
const auto &node : graph.
GetAll<
Node>()) {
185 auto out_edges = node->sinks();
186 for (
const auto &e : out_edges) {
187 all_edges.push_back(e);
189 auto in_edges = node->sources();
190 for (
const auto &e : in_edges) {
191 all_edges.push_back(e);
196 for (
const auto &node : array->nodes()) {
197 auto out_edges = node->sinks();
198 for (
const auto &e : out_edges) {
199 all_edges.push_back(e);
201 auto in_edges = node->sources();
202 for (
const auto &e : in_edges) {
203 all_edges.push_back(e);
209 auto &comp =
dynamic_cast<const Component &
>(graph);
210 for (
const auto &g : comp.children()) {
212 all_edges.insert(all_edges.end(), child_edges.begin(), child_edges.end());
219 std::shared_ptr<Edge>
Connect(
Node *dst,
const std::shared_ptr<Node> &src) {
220 return Connect(dst, src.get());
223 std::shared_ptr<Edge>
Connect(
const std::shared_ptr<Node> &dst,
Node *src) {
224 return Connect(dst.get(), src);
227 std::shared_ptr<Edge>
Connect(
const std::shared_ptr<Node> &dst,
const std::shared_ptr<Node> &src) {
228 return Connect(dst.get(), src.get());
234 }
else if (
dst_ == &node) {
241 static std::shared_ptr<ClockDomain> DomainOf(
NodeArray *node_array) {
242 std::shared_ptr<ClockDomain> result;
243 auto base = node_array->
base();
244 if (base->IsSignal()) {
245 result = std::dynamic_pointer_cast<Signal>(base)->domain();
246 }
else if (base->IsPort()) {
247 result = std::dynamic_pointer_cast<Port>(base)->domain();
249 throw std::runtime_error(
"Base node is not a signal or port.");
255 std::shared_ptr<Type> type = node->
type()->shared_from_this();
257 if (type->IsGeneric()) {
265 type = type->Copy(*rebinding);
270 if (node->IsSignal()) domain = node->AsSignal()->domain();
271 if (node->IsPort()) domain = node->AsPort()->domain();
278 if (node->
parent().value()->IsInstance()) {
279 name = node->
parent().value()->name() +
"_" + name;
282 auto new_name = name;
285 while (comp->
Has(new_name)) {
287 new_name = name +
"_" + std::to_string(i);
293 auto new_signal =
signal(name, type, domain);
296 new_signal->meta = node->
meta;
298 comp->
Add(new_signal);
301 for (
auto &e : node->
sinks()) {
313 for (
auto &e : node->
sources()) {
324 return new_signal.get();
330 auto size = rebinding->at(array->
size())->shared_from_this();
333 auto base = array->
base();
335 std::shared_ptr<Type> type = base->type()->shared_from_this();
337 if (type->IsGeneric()) {
339 auto generics = base->type()->GetGenerics();
345 type = array->
type()->
Copy(*rebinding);
349 auto domain = DomainOf(array);
350 auto name = array->
name();
353 if (array->
parent().value()->IsInstance()) {
354 name = array->
parent().value()->name() +
"_" + name;
357 auto new_name = name;
360 while (comp->
Has(new_name)) {
362 new_name = name +
"_" + std::to_string(i);
366 auto new_array =
signal_array(new_name, type, size, domain);
367 comp->
Add(new_array);
370 for (
size_t n = 0; n < array->
num_nodes(); n++) {
373 auto new_sig = new_array->Append(
false);
375 bool has_sinks =
false;
376 bool has_sources =
false;
379 for (
auto &e : array->
node(n)->
sinks()) {
409 return new_array.get();
virtual std::vector< Edge * > sinks() const
Get the output edges of this Node.
Type * type() const
Return the type of the nodes in the NodeArray.
std::unordered_map< std::string, std::string > meta
KV storage for metadata of tools or specific backend implementations.
virtual std::vector< Edge * > sources() const
Get the input edges of this Node.
std::vector< Edge * > GetAllEdges(const Graph &graph)
Obtain all edges in a graph.
std::vector< T * > GetAll() const
Get all objects of a specific type.
std::shared_ptr< Edge > operator<<=(Node *dst, const std::shared_ptr< Node > &src)
Create an edge, connecting the src node to the dst node.
An array of signal nodes.
Node * dst() const
Return the destination node.
Graph & Add(const std::shared_ptr< Object > &object) override
Add an object to the component.
Node * src() const
Return the source node.
std::vector< Edge * > sources() const override
Return the incoming edges (in this case just the single input edge).
size_t num_nodes() const
Return the number of element nodes.
A graph representing a hardware structure.
Contains every Cerata class, function, etc...
bool IsComponent() const
Return true if this graph is a component, false otherwise.
Signal * AttachSignalToNode(Component *comp, NormalNode *node, NodeMap *rebinding, std::string name)
Attach a Signal to a Node, redirecting all edges through the new Signal.
std::optional< std::shared_ptr< TypeMapper > > GetMapper(Type *other, bool generate_implicit=true)
Get a mapper to another type, if it exists. Generates one, if possible, when generate_implicit = true...
std::unordered_map< const Node *, Node * > NodeMap
A mapping from one object to another object, used in e.g. type generic rebinding.
std::shared_ptr< Literal > strl(std::string str)
Obtain a shared pointer to a string literal from the default node pool.
Class to mark nodes with information for synchronous designs, e.g. clock domain.
static std::shared_ptr< Edge > Make(const std::string &name, Node *dst, Node *src)
Shorthand to get a smart pointer to an edge.
std::string name() const
Return the name of the object.
virtual bool RemoveEdge(Edge *edge)=0
Remove an edge of this node.
A single-input, multiple-outputs node.
virtual bool AddEdge(const std::shared_ptr< Edge > &edge)=0
Add an edge to this node.
std::shared_ptr< Node > base() const
Return the base node of this NodeArray.
Convenience structure for anything that is named. Names are case-sensitive.
Node * dst_
Destination node.
SignalArray * AttachSignalArrayToNodeArray(Component *comp, NodeArray *array, NodeMap *rebinding)
Attach a SignalArray to a Node, redirecting all edges through the new SignalArray.
std::vector< Edge * > sinks() const override
The outgoing Edges that this Node sinks.
std::shared_ptr< ClockDomain > default_domain()
Return a static default clock domain.
bool Has(const std::string &name)
Return true if object with name already exists on graph.
virtual std::string ToString() const
Return a human-readable string of this node.
Edge(std::string name, Node *dst, Node *src)
Construct a new edge.
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.
virtual std::vector< Node * > GetGenerics() const
Obtain any nodes that this type uses as generics.
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.
std::optional< Node * > GetOtherNode(const Node &node) const
Get the node opposite to the other edge node.
virtual std::shared_ptr< Type > Copy(const NodeMap &rebinding) const =0
Make a copy of the type, and rebind any type generic nodes that are keys in the rebinding to their va...
Node * size() const
Return the size node.
Node * node(size_t i) const
Return element node i.
virtual std::optional< Graph * > parent() const
Return the parent graph of this object, if any. Returns empty option otherwise.
A port is a terminator node on a graph.
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.
Type * type() const
Return the node Type.
bool RemoveEdge(Edge *edge) override
Remove an edge from this node.
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.
std::shared_ptr< Edge > Connect(Node *dst, Node *src)
Connect two nodes, returns the corresponding edge.