From 12024d53e51dbd9f6762eac0e58975ee8f8d6f21 Mon Sep 17 00:00:00 2001 From: daniel-k Date: Thu, 21 Dec 2017 21:22:05 +0100 Subject: [PATCH] lib/ip-node: add IpNode class, IpCore which has streaming ports --- fpga/etc/fpga.json | 66 ++++++++++++++----- fpga/include/villas/fpga/ip.hpp | 2 +- fpga/include/villas/fpga/ip_node.hpp | 81 +++++++++++++++++++++++ fpga/include/villas/fpga/ips/intc.hpp | 2 + fpga/lib/CMakeLists.txt | 1 + fpga/lib/ip_node.cpp | 94 +++++++++++++++++++++++++++ 6 files changed, 229 insertions(+), 17 deletions(-) create mode 100644 fpga/include/villas/fpga/ip_node.hpp create mode 100644 fpga/lib/ip_node.cpp diff --git a/fpga/etc/fpga.json b/fpga/etc/fpga.json index 5fcc31556..b1cbb50ea 100644 --- a/fpga/etc/fpga.json +++ b/fpga/etc/fpga.json @@ -26,7 +26,6 @@ "slot": "03:00.0", "do_reset": true, "ips": { - "axi_reset_0": { "vlnv": "xilinx.com:ip:axi_gpio:2.0", "baseaddr": 28672 @@ -39,7 +38,6 @@ "dma_0": { "vlnv": "xilinx.com:ip:axi_dma:7.1", "baseaddr": 12288, - "port": "switch_0:1", "irq": "axi_pcie_intc_0:3" }, "axi_pcie_intc_0": { @@ -49,26 +47,38 @@ "dma_1": { "vlnv": "xilinx.com:ip:axi_dma:7.1", "baseaddr": 8192, - "port": "switch_0:6", + "ports": { + "master": [ { "num": 0, "other": "switch_0:6" } ], + "slave": [ { "num": 0, "other": "switch_0:6" } ] + }, "irq": "axi_pcie_intc_0:3" }, "fifo_mm_s_0": { "vlnv": "xilinx.com:ip:axi_fifo_mm_s:4.1", "baseaddr": 24576, "baseaddr_axi4": 49152, - "port": "switch_0:2", + "ports": { + "master": [ { "num": 0, "other": "switch_0:2" } ], + "slave": [ { "num": 0, "other": "switch_0:2" } ] + }, "irq": "axi_pcie_intc_0:2" }, "rtds_axis_0": { "vlnv": "acs.eonerc.rwth-aachen.de:user:rtds_axis:1.0", "baseaddr": 32768, - "port": "switch_0:0", + "ports": { + "master": [ { "num": 0, "other": "switch_0:0" } ], + "slave": [ { "num": 0, "other": "switch_0:0" } ] + }, "irq": "axi_pcie_intc_0:5" }, "hls_dft_0": { "vlnv": "acs.eonerc.rwth-aachen.de:hls:hls_dft:1.0", "baseaddr": 36864, - "port": "switch_0:5", + "ports": { + "master": [ { "num": 0, "other": "switch_0:5" } ], + "slave": [ { "num": 0, "other": "switch_0:5" } ] + }, "irq": "axi_pcie_intc_0:1", "period": 400, "harmonics": [ @@ -82,23 +92,47 @@ }, "axis_data_fifo_0": { "vlnv": "xilinx.com:ip:axis_data_fifo:1.1", - "port": "switch_0:3" + "ports": { + "master": [ { "num": 0, "other": "switch_0:3" } ], + "slave": [ { "num": 0, "other": "switch_0:3" } ] + } }, "switch_0": { "vlnv": "xilinx.com:ip:axis_interconnect:2.1", "baseaddr": 20480, - "num_ports": 10, - "paths": [ - { - "in": "rtds_axis_0", - "out": "dma_1", - "reverse": true - } - ] + "ports": { + "master": [ + { "num": 0 }, + { "num": 1 }, + { "num": 2 }, + { "num": 3 }, + { "num": 4 }, + { "num": 5 }, + { "num": 6 }, + { "num": 7 }, + { "num": 8 }, + { "num": 9 } + ], + "slave": [ + { "num": 0 }, + { "num": 1 }, + { "num": 2 }, + { "num": 3 }, + { "num": 4 }, + { "num": 5 }, + { "num": 6 }, + { "num": 7 }, + { "num": 8 }, + { "num": 9 } + ] + } }, "axis_data_fifo_1": { "vlnv": "xilinx.com:ip:axis_data_fifo:1.1", - "port": "switch_0:6" + "ports": { + "master": [ { "num": 0, "other": "switch_0:6" } ], + "slave": [ { "num": 0, "other": "switch_0:6" } ] + } } } } diff --git a/fpga/include/villas/fpga/ip.hpp b/fpga/include/villas/fpga/ip.hpp index 8912c3190..d265ecc50 100644 --- a/fpga/include/villas/fpga/ip.hpp +++ b/fpga/include/villas/fpga/ip.hpp @@ -122,7 +122,7 @@ using IpCoreList = std::list>; class IpCoreFactory : public Plugin { public: IpCoreFactory(std::string concreteName) : - Plugin(std::string("FPGA IpCore Factory: ") + concreteName) + Plugin(std::string("IpCore - ") + concreteName) { pluginType = Plugin::Type::FpgaIp; } /// Returns a running and checked FPGA IP diff --git a/fpga/include/villas/fpga/ip_node.hpp b/fpga/include/villas/fpga/ip_node.hpp new file mode 100644 index 000000000..0548988d9 --- /dev/null +++ b/fpga/include/villas/fpga/ip_node.hpp @@ -0,0 +1,81 @@ +/** Interlectual Property component. + * + * This class represents a module within the FPGA. + * + * @file + * @author Steffen Vogel + * @author Daniel Krebs + * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASfpga + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *********************************************************************************/ + +/** @addtogroup fpga VILLASfpga + * @{ + */ + +#ifndef VILLAS_IP_NODE_HPP +#define VILLAS_IP_NODE_HPP + +#include +#include +#include + +#include "ip.hpp" +#include "log.hpp" + +namespace villas { +namespace fpga { +namespace ip { + +// TODO: reflect on interface that an IpNode exposes and how to design it to +// blend in with VILLASnode software nodes +class IpNode : public IpCore { +public: + + friend class IpNodeFactory; + + struct OtherIpNode { + int otherPortNum; + std::string otherName; + }; + + bool connectTo(int port, const OtherIpNode& other); + bool disconnect(int port); + +protected: + std::map portsMaster; + std::map portsSlave; +}; + +class IpNodeFactory : public IpCoreFactory { +public: + IpNodeFactory(std::string name) : IpCoreFactory("Ip Node - " + name) {} + + virtual bool configureJson(IpCore& ip, json_t *json_ip); + +private: + bool populatePorts(std::map& portMap, json_t* json); +}; + +/** @} */ + +} // namespace ip +} // namespace fpga +} // namespace villas + +#endif // VILLAS_IP_NODE_HPP diff --git a/fpga/include/villas/fpga/ips/intc.hpp b/fpga/include/villas/fpga/ips/intc.hpp index 1a9f525be..fe7e20f32 100644 --- a/fpga/include/villas/fpga/ips/intc.hpp +++ b/fpga/include/villas/fpga/ips/intc.hpp @@ -31,6 +31,8 @@ #include "fpga/ip.hpp" #include +#include "fpga/ip_node.hpp" + namespace villas { namespace fpga { namespace ip { diff --git a/fpga/lib/CMakeLists.txt b/fpga/lib/CMakeLists.txt index 40e5df6bd..e4bb46b76 100644 --- a/fpga/lib/CMakeLists.txt +++ b/fpga/lib/CMakeLists.txt @@ -1,6 +1,7 @@ set(SOURCES ip.cpp ip.c + ip_node.cpp vlnv.cpp vlnv.c card.c diff --git a/fpga/lib/ip_node.cpp b/fpga/lib/ip_node.cpp new file mode 100644 index 000000000..cca7fc579 --- /dev/null +++ b/fpga/lib/ip_node.cpp @@ -0,0 +1,94 @@ +#include +#include +#include + +#include "utils.hpp" +#include "fpga/ip_node.hpp" + +namespace villas { +namespace fpga { +namespace ip { + +bool +IpNodeFactory::configureJson(IpCore& ip, json_t* json_ip) +{ + auto ipNode = reinterpret_cast(ip); + + json_t* json_ports = json_object_get(json_ip, "ports"); + if(json_ports == nullptr) { + cpp_error << "IpNode " << ip << " has no ports property"; + return false; + } + + json_t* json_master = json_object_get(json_ports, "master"); + json_t* json_slave = json_object_get(json_ports, "slave"); + + const bool hasMasterPorts = json_is_array(json_master); + const bool hasSlavePorts = json_is_array(json_slave); + + if( (not hasMasterPorts) and (not hasSlavePorts)) { + cpp_error << "IpNode " << ip << " has not ports"; + return false; + } + + // intentionally use short-circuit evaluation to only call populatePorts + // if the property exists + bool masterPortsSuccess = + (hasMasterPorts and populatePorts(ipNode.portsMaster, json_master)) + or (not hasMasterPorts); + + bool slavePortsSuccess = + (hasSlavePorts and populatePorts(ipNode.portsSlave, json_slave)) + or (not hasSlavePorts); + + return (masterPortsSuccess and slavePortsSuccess); +} + +bool +IpNodeFactory::populatePorts(std::map& portMap, json_t* json) +{ + size_t index; + json_t* json_port; + json_array_foreach(json, index, json_port) { + int myPortNum; + const char* other = nullptr; + + int ret = json_unpack(json_port, "{ s : i, s? : s}", + "num", &myPortNum, + "other", &other); + if(ret != 0) { + cpp_error << "Port definition required field 'num'"; + return false; + } + + if(other == nullptr) { + cpp_warn << "Nothing connected to port " << myPortNum; + portMap[myPortNum] = {}; + continue; + } + + const auto tokens = utils::tokenize(other, ":"); + if(tokens.size() != 2) { + cpp_error << "Too many tokens in property 'other'"; + return false; + } + + int otherPortNum; + + try { + otherPortNum = std::stoi(tokens[1]); + } catch(const std::invalid_argument&) { + cpp_error << "Other port number is not an integral number"; + return false; + } + + cpp_debug << "Adding port mapping: " << myPortNum << ":" << other; + portMap[myPortNum] = { otherPortNum, tokens[0] }; + } + + return true; +} + +} // namespace ip +} // namespace fpga +} // namespace villas