diff --git a/fpga/include/villas/directed_graph.hpp b/fpga/include/villas/directed_graph.hpp index 5f8488b13..5273979d3 100644 --- a/fpga/include/villas/directed_graph.hpp +++ b/fpga/include/villas/directed_graph.hpp @@ -29,6 +29,10 @@ class Vertex { public: using Identifier = std::size_t; + const Identifier& + getIdentifier() const + { return id; } + friend std::ostream& operator<< (std::ostream& stream, const Vertex& vertex) { return stream << vertex.id; } @@ -314,7 +318,7 @@ public: } } -private: +protected: VertexIdentifier lastVertexId; EdgeIdentifier lastEdgeId; diff --git a/fpga/include/villas/fpga/ip_node.hpp b/fpga/include/villas/fpga/ip_node.hpp index 890156d1d..f305d60f1 100644 --- a/fpga/include/villas/fpga/ip_node.hpp +++ b/fpga/include/villas/fpga/ip_node.hpp @@ -43,6 +43,52 @@ namespace villas { namespace fpga { namespace ip { +class StreamVertex : public graph::Vertex { +public: + StreamVertex(const std::string& node, const std::string& port, bool isMaster) : + nodeName(node), portName(port), isMaster(isMaster) {} + + std::string getName() const + { return nodeName + "/" + portName + "(" + (isMaster ? "M" : "S") + ")"; } + + friend std::ostream& + operator<< (std::ostream& stream, const StreamVertex& vertex) + { return stream << vertex.getIdentifier() << ": " << vertex.getName(); } + +public: + std::string nodeName; + std::string portName; + bool isMaster; +}; + + +class StreamGraph : public graph::DirectedGraph { +public: + StreamGraph() : graph::DirectedGraph("StreamGraph") {} + + std::shared_ptr + getOrCreateStreamVertex(const std::string& node, + const std::string& port, + bool isMaster) + { + for(auto& [vertexId, vertex] : vertices) { + (void) vertexId; + if(vertex->nodeName == node and vertex->portName == port and vertex->isMaster == isMaster) + return vertex; + } + + // vertex not found, create new one + auto vertex = std::make_shared(node, port, isMaster); + addVertex(vertex); + + return vertex; + } +}; + + +extern StreamGraph streamGraph; + + // TODO: reflect on interface that an IpNode exposes and how to design it to // blend in with VILLASnode software nodes class IpNode : public IpCore { @@ -58,6 +104,14 @@ public: bool connect(std::string portName, const StreamPort& to); bool disconnect(std::string portName); + const StreamVertex& + getMasterPort(const std::string& name) const + { return *portsMaster.at(name); } + + const StreamVertex& + getSlavePort(const std::string& name) const + { return *portsSlave.at(name); } + bool loopbackPossible() const; bool connectLoopback(); @@ -65,8 +119,8 @@ private: std::pair getLoopbackPorts() const; protected: - std::map portsMaster; - std::map portsSlave; + std::map> portsMaster; + std::map> portsSlave; }; class IpNodeFactory : public IpCoreFactory { diff --git a/fpga/lib/ip_node.cpp b/fpga/lib/ip_node.cpp index d202dae14..589d10034 100644 --- a/fpga/lib/ip_node.cpp +++ b/fpga/lib/ip_node.cpp @@ -13,6 +13,8 @@ namespace fpga { namespace ip { +StreamGraph streamGraph; + bool IpNodeFactory::configureJson(IpCore& ip, json_t* json_ip) { @@ -49,25 +51,28 @@ IpNodeFactory::configureJson(IpCore& ip, json_t* json_ip) return false; } - int port_num; - try { - port_num = std::stoi(tokens[1]); - } catch(const std::invalid_argument&) { - logger->error("Target port number is not an integer: '{}'", target_raw); - return false; - } - - IpNode::StreamPort port; - port.nodeName = tokens[0]; - port.portNumber = port_num; - const std::string role(role_raw); - if(role == "master" or role == "initiator") { - ipNode.portsMaster[name_raw] = port; - } else /* slave */ { - ipNode.portsSlave[name_raw] = port; - } + const bool isMaster = (role == "master" or role == "initiator"); + + auto thisVertex = streamGraph.getOrCreateStreamVertex( + ip.getInstanceName(), + name_raw, + isMaster); + + auto connectedVertex = streamGraph.getOrCreateStreamVertex( + tokens[0], + tokens[1], + not isMaster); + + + if(isMaster) { + streamGraph.addDefaultEdge(thisVertex->getIdentifier(), + connectedVertex->getIdentifier()); + ipNode.portsMaster[name_raw] = thisVertex; + } else /* slave */ { + ipNode.portsSlave[name_raw] = thisVertex; + } } return true; @@ -79,7 +84,7 @@ IpNode::getLoopbackPorts() const for(auto& [masterName, masterTo] : portsMaster) { for(auto& [slaveName, slaveTo] : portsSlave) { // TODO: should we also check which IP both ports are connected to? - if(masterTo.nodeName == slaveTo.nodeName) { + if(masterTo->nodeName == slaveTo->nodeName) { return { masterName, slaveName }; } } @@ -105,11 +110,11 @@ IpNode::connectLoopback() logger->debug("master port: {}", ports.first); logger->debug("slave port: {}", ports.second); - logger->debug("switch at: {}", portMaster.nodeName); + logger->debug("switch at: {}", portMaster->nodeName); // TODO: verify this is really a switch! auto axiStreamSwitch = reinterpret_cast( - card->lookupIp(portMaster.nodeName)); + card->lookupIp(portMaster->nodeName)); if(axiStreamSwitch == nullptr) { logger->error("Cannot find switch"); @@ -117,7 +122,7 @@ IpNode::connectLoopback() } // switch's slave port is our master port and vice versa - return axiStreamSwitch->connect(portMaster.portNumber, portSlave.portNumber); + return axiStreamSwitch->connect(*portMaster, *portSlave); } } // namespace ip