From 7d927155db10c3372243576944494417e85e6758 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Wed, 31 Jan 2018 12:21:36 +0100 Subject: [PATCH 01/21] tests: minimal test of memory manager --- fpga/tests/graph.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/fpga/tests/graph.cpp b/fpga/tests/graph.cpp index 2dd8e6c9b..409d26537 100644 --- a/fpga/tests/graph.cpp +++ b/fpga/tests/graph.cpp @@ -3,6 +3,13 @@ #include #include #include +#include + +static void init_graph() +{ + spdlog::set_pattern("[%T] [%l] [%n] %v"); + spdlog::set_level(spdlog::level::debug); +} TestSuite(graph, .description = "Graph library" @@ -102,3 +109,15 @@ Test(graph, path, .description = "Find path") logger->info(" -> edge {}", edge); } } + +Test(graph, memory_manager, .description = "Global Memory Manager") +{ + auto& mm = villas::MemoryManager::get(); + + auto dmaRegs = mm.createAddressSpace("DMA Registers"); + auto pcieBridge = mm.createAddressSpace("PCIe Bridge"); + + mm.createMapping(0x1000, 0, 0x1000, dmaRegs, pcieBridge); + + mm.dump(); +} From 44ad8271212febe58a3af2cd2d9bda497e686a38 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Wed, 31 Jan 2018 12:22:04 +0100 Subject: [PATCH 02/21] hwdef-parse: treat PCIe bridge the same as all other IPs This is needed in order to construct a global memory graph. --- fpga/scripts/hwdef-parse.py | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/fpga/scripts/hwdef-parse.py b/fpga/scripts/hwdef-parse.py index 123ca3f9c..353e91043 100755 --- a/fpga/scripts/hwdef-parse.py +++ b/fpga/scripts/hwdef-parse.py @@ -34,7 +34,8 @@ whitelist = [ [ 'acs.eonerc.rwth-aachen.de', 'hls' ], [ 'acs.eonerc.rwth-aachen.de', 'sysgen' ], [ 'xilinx.com', 'ip', 'axi_gpio' ], - [ 'xilinx.com', 'ip', 'axi_bram_ctrl' ] + [ 'xilinx.com', 'ip', 'axi_bram_ctrl' ], + [ 'xilinx.com', 'ip', 'axi_pcie' ] ] # List of VLNI ids of AXI4-Stream infrastructure IP cores which do not alter data @@ -140,29 +141,19 @@ for module in modules: mem = ips[instance].setdefault('memory-view', {}) for mrange in mmap: - mem_interface = remove_prefix(mrange.get('MASTERBUSINTERFACE'), 'M_AXI_') + mem_interface = mrange.get('MASTERBUSINTERFACE') mem_instance = mrange.get('INSTANCE') + mem_block = mrange.get('ADDRESSBLOCK') - entry = mem.setdefault(mem_interface, {}).setdefault(mem_instance, {}) + _interface = mem.setdefault(mem_interface, {}) + _instance = _interface.setdefault(mem_instance, {}) + _block = _instance.setdefault(mem_block, {}) - entry['baseaddr'] = int(mrange.get('BASEVALUE'), 16); - entry['highaddr'] = int(mrange.get('HIGHVALUE'), 16); + _block['baseaddr'] = int(mrange.get('BASEVALUE'), 16) + _block['highaddr'] = int(mrange.get('HIGHVALUE'), 16) + _block['size'] = _block['highaddr'] - _block['baseaddr'] + 1 - -# find PCI-e module to extract memory map -pcie = root.find('.//MODULE[@MODTYPE="axi_pcie"]') -mmap = pcie.find('.//MEMORYMAP') -for mrange in mmap: - instance = mrange.get('INSTANCE') - - if instance in ips: - base_name = remove_prefix(mrange.get('BASENAME'), 'C_').lower() - high_name = remove_prefix(mrange.get('HIGHNAME'), 'C_').lower() - - ips[instance][base_name] = int(mrange.get('BASEVALUE'), 16); - ips[instance][high_name] = int(mrange.get('HIGHVALUE'), 16); - # find AXI-Stream switch port mapping switch = root.find('.//MODULE[@MODTYPE="axis_switch"]') busifs = switch.find('.//BUSINTERFACES') From be3538f6977f55331a9f600d184790a903b22258 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Wed, 31 Jan 2018 12:31:37 +0100 Subject: [PATCH 03/21] hwdef-parse: fix switch/num_port to be an integer --- fpga/scripts/hwdef-parse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpga/scripts/hwdef-parse.py b/fpga/scripts/hwdef-parse.py index 353e91043..7f0c57863 100755 --- a/fpga/scripts/hwdef-parse.py +++ b/fpga/scripts/hwdef-parse.py @@ -188,7 +188,7 @@ for busif in busifs: ports[-1]['name'] = sanitize_name(busif_ep.get('NAME')) # set number of master/slave port pairs for switch -ips[switch.get('INSTANCE')]['num_ports'] = switch_ports / 2 +ips[switch.get('INSTANCE')]['num_ports'] = int(switch_ports / 2) # find Interrupt assignments From 912c3729d45fe5b5fc9aa5e82947692d74d5e131 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 6 Feb 2018 11:46:19 +0100 Subject: [PATCH 04/21] lib/ip: improve readability --- fpga/lib/ip.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fpga/lib/ip.cpp b/fpga/lib/ip.cpp index 4daac1309..fd9bc3dcb 100644 --- a/fpga/lib/ip.cpp +++ b/fpga/lib/ip.cpp @@ -94,7 +94,9 @@ buildDependencyGraph(DependencyGraph& dependencyGraph, json_t* json_ips, std::st return false; } - if(name == mapping[0]) { + const std::string& dependencyName = mapping[0]; + + if(name == dependencyName) { logger->error("IP {} cannot depend on itself", TXT_BOLD(name)); dependencyGraph.removeNode(name); @@ -103,13 +105,13 @@ buildDependencyGraph(DependencyGraph& dependencyGraph, json_t* json_ips, std::st // already add dependency, if adding it fails, removing the dependency // will also remove the current one - dependencyGraph.addDependency(name, mapping[0]); + dependencyGraph.addDependency(name, dependencyName); - if(not buildDependencyGraph(dependencyGraph, json_ips, mapping[0])) { + if(not buildDependencyGraph(dependencyGraph, json_ips, dependencyName)) { logger->error("Dependency {} of {} not satisfied", - mapping[0], TXT_BOLD(name)); + dependencyName, TXT_BOLD(name)); - dependencyGraph.removeNode(mapping[0]); + dependencyGraph.removeNode(dependencyName); return false; } } From 95adaad32fff45779518626a06acf3a32dc910f2 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 10:03:16 +0100 Subject: [PATCH 05/21] etc/json: update config file with current output of hwdef-parse --- fpga/etc/fpga.json | 237 +++++++++++++++++++++++++++++++++------------ 1 file changed, 177 insertions(+), 60 deletions(-) diff --git a/fpga/etc/fpga.json b/fpga/etc/fpga.json index 275cfbb73..2abcdc136 100644 --- a/fpga/etc/fpga.json +++ b/fpga/etc/fpga.json @@ -28,74 +28,114 @@ "ips": { "bram_0_axi_bram_ctrl_0": { "vlnv": "xilinx.com:ip:axi_bram_ctrl:4.0", - "s_axi_baseaddr": 0, - "s_axi_highaddr": 8191, "size": 8192 }, "hier_0_axi_dma_axi_dma_0": { "vlnv": "xilinx.com:ip:axi_dma:7.1", "memory-view": { - "SG": { + "M_AXI_SG": { "bram_0_axi_bram_ctrl_0": { - "baseaddr": 0, - "highaddr": 8191 + "Mem0": { + "baseaddr": 0, + "highaddr": 8191, + "size": 8192 + } }, "hier_0_axi_dma_axi_dma_1": { - "baseaddr": 8192, - "highaddr": 12287 + "Reg": { + "baseaddr": 8192, + "highaddr": 12287, + "size": 4096 + } }, "hier_0_axi_dma_axi_dma_0": { - "baseaddr": 12288, - "highaddr": 16383 + "Reg": { + "baseaddr": 12288, + "highaddr": 16383, + "size": 4096 + } }, "timer_0_axi_timer_0": { - "baseaddr": 16384, - "highaddr": 20479 + "Reg": { + "baseaddr": 16384, + "highaddr": 20479, + "size": 4096 + } }, "hier_0_axis_interconnect_0_axis_interconnect_0_xbar": { - "baseaddr": 20480, - "highaddr": 24575 + "Reg": { + "baseaddr": 20480, + "highaddr": 24575, + "size": 4096 + } }, "hier_0_axi_fifo_mm_s_0": { - "baseaddr": 49152, - "highaddr": 57343 + "Mem0": { + "baseaddr": 24576, + "highaddr": 28671, + "size": 4096 + }, + "Mem1": { + "baseaddr": 49152, + "highaddr": 57343, + "size": 8192 + } }, "pcie_0_axi_reset_0": { - "baseaddr": 28672, - "highaddr": 32767 + "Reg": { + "baseaddr": 28672, + "highaddr": 32767, + "size": 4096 + } }, "hier_0_rtds_axis_0": { - "baseaddr": 32768, - "highaddr": 36863 + "reg0": { + "baseaddr": 32768, + "highaddr": 36863, + "size": 4096 + } }, "hier_0_hls_dft_0": { - "baseaddr": 36864, - "highaddr": 40959 + "Reg": { + "baseaddr": 36864, + "highaddr": 40959, + "size": 4096 + } }, "pcie_0_axi_pcie_intc_0": { - "baseaddr": 45056, - "highaddr": 49151 + "Reg": { + "baseaddr": 45056, + "highaddr": 49151, + "size": 4096 + } }, "pcie_0_axi_pcie_0": { - "baseaddr": 268435456, - "highaddr": 536870911 + "CTL0": { + "baseaddr": 268435456, + "highaddr": 536870911, + "size": 268435456 + } } }, - "MM2S": { + "M_AXI_MM2S": { "pcie_0_axi_pcie_0": { - "baseaddr": 2147483648, - "highaddr": 4294967295 + "BAR0": { + "baseaddr": 2147483648, + "highaddr": 4294967295, + "size": 2147483648 + } } }, - "S2MM": { + "M_AXI_S2MM": { "pcie_0_axi_pcie_0": { - "baseaddr": 2147483648, - "highaddr": 4294967295 + "BAR0": { + "baseaddr": 2147483648, + "highaddr": 4294967295, + "size": 2147483648 + } } } }, - "baseaddr": 12288, - "highaddr": 16383, "ports": [ { "role": "initiator", @@ -112,21 +152,25 @@ "hier_0_axi_dma_axi_dma_1": { "vlnv": "xilinx.com:ip:axi_dma:7.1", "memory-view": { - "MM2S": { + "M_AXI_MM2S": { "pcie_0_axi_pcie_0": { - "baseaddr": 2147483648, - "highaddr": 4294967295 + "BAR0": { + "baseaddr": 2147483648, + "highaddr": 4294967295, + "size": 2147483648 + } } }, - "S2MM": { + "M_AXI_S2MM": { "pcie_0_axi_pcie_0": { - "baseaddr": 2147483648, - "highaddr": 4294967295 + "BAR0": { + "baseaddr": 2147483648, + "highaddr": 4294967295, + "size": 2147483648 + } } } }, - "baseaddr": 8192, - "highaddr": 12287, "ports": [ { "role": "initiator", @@ -142,10 +186,6 @@ }, "hier_0_axi_fifo_mm_s_0": { "vlnv": "xilinx.com:ip:axi_fifo_mm_s:4.1", - "baseaddr": 24576, - "highaddr": 28671, - "axi4_baseaddr": 49152, - "axi4_highaddr": 57343, "ports": [ { "role": "master", @@ -164,8 +204,6 @@ }, "hier_0_axis_interconnect_0_axis_interconnect_0_xbar": { "vlnv": "xilinx.com:ip:axis_switch:1.1", - "baseaddr": 20480, - "highaddr": 24575, "ports": [ { "role": "initiator", @@ -188,12 +226,10 @@ "name": "S04_AXIS" } ], - "num_ports": 14 + "num_ports": 7 }, "hier_0_hls_dft_0": { "vlnv": "acs.eonerc.rwth-aachen.de:hls:hls_dft:1.0", - "s_axi_ctrl_baseaddr": 36864, - "s_axi_ctrl_highaddr": 40959, "ports": [ { "role": "master", @@ -209,8 +245,6 @@ }, "hier_0_rtds_axis_0": { "vlnv": "acs.eonerc.rwth-aachen.de:user:rtds_axis:1.0", - "baseaddr": 32768, - "highaddr": 36863, "ports": [ { "role": "master", @@ -229,20 +263,103 @@ "irq_case": "pcie_0_axi_pcie_intc_0:7" } }, + "pcie_0_axi_pcie_0": { + "vlnv": "xilinx.com:ip:axi_pcie:2.8", + "memory-view": { + "M_AXI": { + "bram_0_axi_bram_ctrl_0": { + "Mem0": { + "baseaddr": 0, + "highaddr": 8191, + "size": 8192 + } + }, + "hier_0_axi_dma_axi_dma_1": { + "Reg": { + "baseaddr": 8192, + "highaddr": 12287, + "size": 4096 + } + }, + "hier_0_axi_dma_axi_dma_0": { + "Reg": { + "baseaddr": 12288, + "highaddr": 16383, + "size": 4096 + } + }, + "timer_0_axi_timer_0": { + "Reg": { + "baseaddr": 16384, + "highaddr": 20479, + "size": 4096 + } + }, + "hier_0_axis_interconnect_0_axis_interconnect_0_xbar": { + "Reg": { + "baseaddr": 20480, + "highaddr": 24575, + "size": 4096 + } + }, + "hier_0_axi_fifo_mm_s_0": { + "Mem0": { + "baseaddr": 24576, + "highaddr": 28671, + "size": 4096 + }, + "Mem1": { + "baseaddr": 49152, + "highaddr": 57343, + "size": 8192 + } + }, + "pcie_0_axi_reset_0": { + "Reg": { + "baseaddr": 28672, + "highaddr": 32767, + "size": 4096 + } + }, + "hier_0_rtds_axis_0": { + "reg0": { + "baseaddr": 32768, + "highaddr": 36863, + "size": 4096 + } + }, + "hier_0_hls_dft_0": { + "Reg": { + "baseaddr": 36864, + "highaddr": 40959, + "size": 4096 + } + }, + "pcie_0_axi_pcie_intc_0": { + "Reg": { + "baseaddr": 45056, + "highaddr": 49151, + "size": 4096 + } + }, + "pcie_0_axi_pcie_0": { + "CTL0": { + "baseaddr": 268435456, + "highaddr": 536870911, + "size": 268435456 + } + } + } + } + }, "pcie_0_axi_pcie_intc_0": { - "vlnv": "acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:1.0", - "baseaddr": 45056, - "highaddr": 49151 + "vlnv": "acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:1.0" }, "pcie_0_axi_reset_0": { - "vlnv": "xilinx.com:ip:axi_gpio:2.0", - "baseaddr": 28672, - "highaddr": 32767 + "vlnv": "xilinx.com:ip:axi_gpio:2.0" }, "timer_0_axi_timer_0": { "vlnv": "xilinx.com:ip:axi_timer:2.0", - "baseaddr": 16384, - "highaddr": 20479, "irqs": { "generateout0": "pcie_0_axi_pcie_intc_0:0" } From 02e873e8ffdc02f1034133810ef11f8c3be659c8 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 10:16:02 +0100 Subject: [PATCH 06/21] lib/ip: remove dependency graph Replace by static list of VLNVs that will be initialized first. --- fpga/include/villas/dependency_graph.hpp | 55 ----- fpga/include/villas/dependency_graph_impl.hpp | 111 ---------- fpga/lib/ip.cpp | 200 ++++++++---------- 3 files changed, 88 insertions(+), 278 deletions(-) delete mode 100644 fpga/include/villas/dependency_graph.hpp delete mode 100644 fpga/include/villas/dependency_graph_impl.hpp diff --git a/fpga/include/villas/dependency_graph.hpp b/fpga/include/villas/dependency_graph.hpp deleted file mode 100644 index f488e8ad7..000000000 --- a/fpga/include/villas/dependency_graph.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef VILLAS_DEPENDENCY_GRAPH_HPP -#define VILLAS_DEPENDENCY_GRAPH_HPP - -#include -#include -#include - -#include "log.hpp" - -namespace villas { -namespace utils { - - -template -class DependencyGraph { -public: - using NodeList = std::list; - - /// Create a node without dependencies if it not yet exists, return if a new - /// node has been created. - bool addNode(const T& node); - - /// Remove a node and all other nodes that depend on it - void removeNode(const T& node); - - /// Add a dependency to a node. Will create the node if it not yet exists - void addDependency(const T& node, const T& dependency); - - void dump(); - - /// Return a sequential evaluation order list. If a circular dependency has been - /// detected, all nodes involved will not be part of that list. - NodeList getEvaluationOrder() const; - -private: - /// Return whether a node already exists or not - bool nodeExists(const T& node) - { return graph.find(node) != graph.end(); } - - static bool - nodeInList(const NodeList& list, const T& node) - { return list.end() != std::find(list.begin(), list.end(), node); } - -private: - using Graph = std::map; - - Graph graph; -}; - -} // namespace utils -} // namespace villas - -#include "dependency_graph_impl.hpp" - -#endif // VILLAS_DEPENDENCY_GRAPH_HPP diff --git a/fpga/include/villas/dependency_graph_impl.hpp b/fpga/include/villas/dependency_graph_impl.hpp deleted file mode 100644 index b8090c2d0..000000000 --- a/fpga/include/villas/dependency_graph_impl.hpp +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef VILLAS_DEPENDENCY_GRAPH_HPP -#error "Do not include this file directly, please include depedency_graph.hpp" -#endif - -#include -#include "dependency_graph.hpp" - -#include "log.hpp" - -static auto logger = loggerGetOrCreate("DependencyGraph"); - -namespace villas { -namespace utils { - -template -bool -DependencyGraph::addNode(const T &node) -{ - bool existedBefore = nodeExists(node); - - // accessing is enough to create if not exists - graph[node]; - - return existedBefore; -} - -template -void -DependencyGraph::removeNode(const T &node) -{ - graph.erase(node); - - // check if other nodes depend on this one - for(auto& [key, dependencies] : graph) { - if(nodeInList(dependencies, node)) { - // remove other node that depends on the one to delete - removeNode(key); - } - } - -} - -template -void -DependencyGraph::addDependency(const T &node, const T &dependency) -{ - NodeList& dependencies = graph[node]; - if(not nodeInList(dependencies, dependency)) - dependencies.push_back(dependency); -} - -template -void -DependencyGraph::dump() { - for(auto& node : graph) { - std::stringstream ss; - for(auto& dep : node.second) { - ss << dep << " "; - } - logger->info("{}: {}", node.first, ss.str()); - } -} - -template -typename DependencyGraph::NodeList -DependencyGraph::getEvaluationOrder() const -{ - // copy graph to preserve information (we have to delete entries later) - Graph graph = this->graph; - - // output list - NodeList out; - - while(graph.size() > 0) { - int added = 0; - - // look for nodes with no dependencies - for(auto& [key, dependencies] : graph) { - - for(auto dep = dependencies.begin(); dep != dependencies.end(); ++dep) { - if(nodeInList(out, *dep)) { - // dependency has been pushed to list in last round - dep = dependencies.erase(dep); - } - } - - // nodes with no dependencies can be pushed to list - if(dependencies.empty()) { - out.push_back(key); - graph.erase(key); - added++; - } - } - - // if a round doesn't add any elements and is not the last, then - // there is a circular dependency - if(added == 0 and graph.size() > 0) { - logger->error("Circular dependency detected! IPs not available:"); - for(auto& [key, value] : graph) { - (void) value; - logger->error(" {}", key); - } - break; - } - } - - return out; -} - -} // namespace utils -} // namespace villas diff --git a/fpga/lib/ip.cpp b/fpga/lib/ip.cpp index fd9bc3dcb..a6d35bd7c 100644 --- a/fpga/lib/ip.cpp +++ b/fpga/lib/ip.cpp @@ -23,9 +23,9 @@ #include "log_config.h" #include "log.hpp" #include "plugin.h" -#include "dependency_graph.hpp" #include "utils.hpp" +#include "fpga/vlnv.hpp" #include "fpga/ip.hpp" #include "fpga/card.hpp" @@ -38,94 +38,28 @@ #include "log.hpp" -using DependencyGraph = villas::utils::DependencyGraph; +#include "fpga/ips/pcie.hpp" +#include "fpga/ips/intc.hpp" +#include "fpga/ips/switch.hpp" -static -std::list -dependencyTokens = {"irqs"}; - -static -bool -buildDependencyGraph(DependencyGraph& dependencyGraph, json_t* json_ips, std::string name) -{ - const bool nodeExists = dependencyGraph.addNode(name); - - // HACK: just get the right logger - auto logger = loggerGetOrCreate("IpCoreFactory"); - - // do not add IP multiple times - // this happens if more than 1 IP depends on a certain other IP - if(nodeExists) { - return true; - } - - json_t* json_ip = json_object_get(json_ips, name.c_str()); - if(json_ip == nullptr) { - logger->error("IP {} not found in config", name); - return false; - } - - for(auto& dependencyToken : dependencyTokens) { - json_t* json_dependency = json_object_get(json_ip, dependencyToken.c_str()); - if(json_dependency == nullptr) { - logger->debug("Property {} of {} is not present", - dependencyToken, TXT_BOLD(name)); - continue; - } - - const char* irq_name; - json_t* json_irq; - json_object_foreach(json_dependency, irq_name, json_irq) { - const char* value = json_string_value(json_irq); - if(value == nullptr) { - logger->warn("Property {} of {} is invalid", - dependencyToken, TXT_BOLD(name)); - continue; - } - - auto mapping = villas::utils::tokenize(value, ":"); - - - if(mapping.size() != 2) { - logger->error("Invalid {} mapping of {}", - dependencyToken, TXT_BOLD(name)); - - dependencyGraph.removeNode(name); - return false; - } - - const std::string& dependencyName = mapping[0]; - - if(name == dependencyName) { - logger->error("IP {} cannot depend on itself", TXT_BOLD(name)); - - dependencyGraph.removeNode(name); - return false; - } - - // already add dependency, if adding it fails, removing the dependency - // will also remove the current one - dependencyGraph.addDependency(name, dependencyName); - - if(not buildDependencyGraph(dependencyGraph, json_ips, dependencyName)) { - logger->error("Dependency {} of {} not satisfied", - dependencyName, TXT_BOLD(name)); - - dependencyGraph.removeNode(dependencyName); - return false; - } - } - } - - return true; -} namespace villas { namespace fpga { namespace ip { -void IpCore::dump() { +// Special IPs that have to be initialized first. Will be initialized in the +// same order as they appear in this list, i.e. first here will be initialized +// first. +static std::list +vlnvInitializationOrder = { + Vlnv(AxiPciExpressBridgeFactory::getCompatibleVlnvString()), + Vlnv(InterruptControllerFactory::getCompatibleVlnvString()), + Vlnv(AxiStreamSwitchFactory::getCompatibleVlnvString()), +}; + +void +IpCore::dump() { auto logger = getLogger(); logger->info("Base address = {:08x}", baseaddr); @@ -158,52 +92,68 @@ IpCore::getAddrMapped(uintptr_t address) const IpCoreList IpCoreFactory::make(PCIeCard* card, json_t *json_ips) { - DependencyGraph dependencyGraph; - IpCoreList initializedIps; + IpCoreList configuredIps; auto loggerStatic = getStaticLogger(); + std::list allIps; // all IPs available + std::list orderedIps; // IPs ordered in initialization order - loggerStatic->debug("Parsing IP dependency graph:"); - void* iter = json_object_iter(json_ips); - while(iter != nullptr) { - buildDependencyGraph(dependencyGraph, json_ips, json_object_iter_key(iter)); - iter = json_object_iter_next(json_ips, iter); - } - - - loggerStatic->debug("IP initialization order:"); - for(auto& ipName : dependencyGraph.getEvaluationOrder()) { - loggerStatic->debug(" {}", TXT_BOLD(ipName)); - } - - - for(auto& ipName : dependencyGraph.getEvaluationOrder()) { - loggerStatic->info("Initializing {}", TXT_BOLD(ipName)); - - json_t* json_ip = json_object_get(json_ips, ipName.c_str()); - - // extract VLNV from JSON + // parse all IP instance names and their VLNV into list `allIps` + const char* ipName; + json_t* json_ip; + json_object_foreach(json_ips, ipName, json_ip) { const char* vlnv; if(json_unpack(json_ip, "{ s: s }", "vlnv", &vlnv) != 0) { - loggerStatic->warn("IP {} has no entry 'vlnv'", ipName); + loggerStatic->warn("IP {} has no VLNV", ipName); continue; } - IpIdentifier id(Vlnv(vlnv), ipName); + allIps.push_back({vlnv, ipName}); + } + + + // Pick out IPs to be initialized first. + // + // Reverse order of the initialization order list, because we push to the + // front of the output list, so that the first element will also be the + // first to be initialized. + vlnvInitializationOrder.reverse(); + + for(auto& vlnvInitFirst : vlnvInitializationOrder) { + // iterate over IPs, if VLNV matches, push to front and remove from list + for(auto it = allIps.begin(); it != allIps.end(); ++it) { + if(vlnvInitFirst == it->getVlnv()) { + orderedIps.push_front(*it); + it = allIps.erase(it); + } + } + } + + // insert all other IPs at the end + orderedIps.splice(orderedIps.end(), allIps); + + + loggerStatic->debug("IP initialization order:"); + for(auto& id : orderedIps) { + loggerStatic->debug(" {}", TXT_BOLD(id.getName())); + } + + for(auto& id : orderedIps) { + loggerStatic->info("Initializing {}", id); // find the appropriate factory that can create the specified VLNV // Note: // This is the magic part! Factories automatically register as a // plugin as soon as they are instantiated. If there are multiple // candidates, the first suitable factory will be used. - IpCoreFactory* ipCoreFactory = lookup(id.vlnv); + IpCoreFactory* ipCoreFactory = lookup(id.getVlnv()); if(ipCoreFactory == nullptr) { - loggerStatic->warn("No plugin found to handle {}", vlnv); + loggerStatic->warn("No plugin found to handle {}", id.getVlnv()); continue; } else { loggerStatic->debug("Using {} for IP {}", - ipCoreFactory->getName(), vlnv); + ipCoreFactory->getName(), id.getVlnv()); } auto logger = ipCoreFactory->getLogger(); @@ -293,14 +243,40 @@ IpCoreFactory::make(PCIeCard* card, json_t *json_ips) continue; } - // TODO: currently fails, fix and remove comment + configuredIps.push_back(std::move(ip)); + } + + IpCoreList initializedIps; + for(auto& ip : configuredIps) { + + // Translate all memory blocks that the IP needs to be accessible from + // the process and cache in the instance, so this has not to be done at + // runtime. + for(auto& memoryBlock : ip->getMemoryBlocks()) { + // construct the global name of this address block + const auto addrSpaceName = + MemoryManager::getSlaveAddrSpaceName(ip->id.getName(), memoryBlock); + + // retrieve its address space identifier + const auto addrSpace = + MemoryManager::get().findAddressSpace(addrSpaceName); + + // get the translation to the address space + const auto& translation = + MemoryManager::get().getTranslationFromProcess(addrSpace); + + // cache it in the IP instance only with local name + ip->addressTranslations.emplace(memoryBlock, translation); + } + + if(not ip->init()) { - logger->error("Cannot start IP {}", ip->id.name); + loggerStatic->error("Cannot start IP {}", ip->id.getName()); continue; } if(not ip->check()) { - logger->error("Checking of IP {} failed", ip->id.name); + loggerStatic->error("Checking of IP {} failed", ip->id.getName()); continue; } From 33ba634d8779253f5cb330f17234172981b07835 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 10:17:33 +0100 Subject: [PATCH 07/21] lib/directed-graph: add findVertex() and minor refactoring --- fpga/include/villas/directed_graph.hpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fpga/include/villas/directed_graph.hpp b/fpga/include/villas/directed_graph.hpp index a90ef29bb..6335d8087 100644 --- a/fpga/include/villas/directed_graph.hpp +++ b/fpga/include/villas/directed_graph.hpp @@ -71,6 +71,7 @@ public: using VertexIdentifier = Vertex::Identifier; using EdgeIdentifier = Edge::Identifier; + using Path = std::list; DirectedGraph(const std::string& name = "DirectedGraph") : lastVertexId(0), lastEdgeId(0) @@ -88,6 +89,18 @@ public: return vertices.at(vertexId); } + template + VertexIdentifier findVertex(UnaryPredicate p) + { + for(auto& [vertexId, vertex] : vertices) { + if(p(vertex)) { + return vertexId; + } + } + + throw std::out_of_range("vertex not found"); + } + std::shared_ptr getEdge(EdgeIdentifier edgeId) const { if(edgeId < 0 or edgeId >= lastEdgeId) @@ -194,8 +207,9 @@ public: vertexGetEdges(VertexIdentifier vertexId) const { return getVertex(vertexId)->edges; } - bool getPath(VertexIdentifier fromVertexId, VertexIdentifier toVertexId, - std::list& path) + bool getPath(VertexIdentifier fromVertexId, + VertexIdentifier toVertexId, + Path& path) { if(fromVertexId == toVertexId) { // arrived at the destination From 035c6f8b2a7798a0ddcf15f597028b876a38f036 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 10:29:16 +0100 Subject: [PATCH 08/21] lib/kernel/vfio: add function to get size of device memory region --- fpga/include/villas/kernel/vfio.h | 3 +++ fpga/lib/kernel/vfio.c | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/fpga/include/villas/kernel/vfio.h b/fpga/include/villas/kernel/vfio.h index 1b4a9c336..35a2465f9 100644 --- a/fpga/include/villas/kernel/vfio.h +++ b/fpga/include/villas/kernel/vfio.h @@ -104,6 +104,9 @@ void vfio_dump(struct vfio_container *c); /** Map a device memory region to the application address space (e.g. PCI BARs) */ void * vfio_map_region(struct vfio_device *d, int idx); +/** Get the size of a device memory region */ +size_t vfio_region_size(struct vfio_device *d, int idx); + /** Map VM to an IOVA, which is accessible by devices in the container */ int vfio_map_dma(struct vfio_container *c, uint64_t virt, uint64_t phys, size_t len); diff --git a/fpga/lib/kernel/vfio.c b/fpga/lib/kernel/vfio.c index 0c0de58d4..7103cc519 100644 --- a/fpga/lib/kernel/vfio.c +++ b/fpga/lib/kernel/vfio.c @@ -574,6 +574,14 @@ void * vfio_map_region(struct vfio_device *d, int idx) return d->mappings[idx]; } +size_t vfio_region_size(struct vfio_device *d, int idx) +{ + assert(d != NULL); + assert((size_t)idx < d->info.num_regions); + + return d->regions[idx].size; +} + int vfio_unmap_region(struct vfio_device *d, int idx) { int ret; From 95e29f27060276d06d37ec9fb96333bf37a3414d Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 12:22:50 +0100 Subject: [PATCH 09/21] memory-manager: allow for traversing address spaces Major rework of the memory manager. Adds a memory translation class to resolve addresses across address spaces and extents the memory manager in order to do so. --- fpga/include/villas/memory_manager.hpp | 199 +++++++++++++++++++------ fpga/lib/memory_manager.cpp | 126 +++++++++++++++- fpga/tests/graph.cpp | 15 +- 3 files changed, 286 insertions(+), 54 deletions(-) diff --git a/fpga/include/villas/memory_manager.hpp b/fpga/include/villas/memory_manager.hpp index 935f262b3..941fd43c0 100644 --- a/fpga/include/villas/memory_manager.hpp +++ b/fpga/include/villas/memory_manager.hpp @@ -2,96 +2,207 @@ #include #include +#include #include "log.hpp" #include "directed_graph.hpp" namespace villas { - -class Mapping : public graph::Edge { - friend class MemoryManager; - +/** + * @brief Translation between a local (master) to a foreign (slave) address space + * + * Memory translations can be chained together using the `+=` operator which is + * used internally by the MemoryManager to compute a translation through + * multiple hops (memory mappings). + */ +class MemoryTranslation { public: - // create mapping here (if needed) - Mapping() {} - // destroy mapping here (if needed) - virtual ~Mapping(); + /** + * @brief MemoryTranslation + * @param src Base address of local address space + * @param dst Base address of foreign address space + * @param size Size of "memory window" + */ + MemoryTranslation(uintptr_t src, uintptr_t dst, size_t size) : + src(src), dst(dst), size(size) {} + + uintptr_t + getLocalAddr(uintptr_t addrInForeignAddrSpace) const; + + uintptr_t + getForeignAddr(uintptr_t addrInLocalAddrSpace) const; friend std::ostream& - operator<< (std::ostream& stream, const Mapping& mapping) + operator<< (std::ostream& stream, const MemoryTranslation& translation) { - return stream << static_cast(mapping) << " = " - << std::hex - << "(src=0x" << mapping.src - << ", dest=0x" << mapping.dest - << ", size=0x" << mapping.size + return stream << std::hex + << "(src=0x" << translation.src + << ", dst=0x" << translation.dst + << ", size=0x" << translation.size << ")"; } -private: - uintptr_t src; - uintptr_t dest; - size_t size; -}; - -class AddressSpace : public graph::Vertex { - friend class MemoryManager; - -public: - friend std::ostream& - operator<< (std::ostream& stream, const AddressSpace& addrSpace) - { - return stream << static_cast(addrSpace) << " = " - << addrSpace.name; - } + /// Merge two MemoryTranslations together + MemoryTranslation& operator+=(const MemoryTranslation& other); private: - std::string name; + uintptr_t src; ///< Base address of local address space + uintptr_t dst; ///< Base address of foreign address space + size_t size; ///< Size of "memory window" }; -// is or has a graph +/** + * @brief Global memory manager to resolve addresses across address spaces + * + * Every entity in the system has to register its (master) address space and + * create mappings to other (slave) address spaces that it can access. A + * directed graph is then constructed which allows to traverse addresses spaces + * through multiple mappings and resolve addresses through this "tunnel" of + * memory mappings. + */ class MemoryManager { private: - // This is a singleton, so private constructor + // This is a singleton, so private constructor ... MemoryManager() : - memoryGraph("MemoryGraph") {} + memoryGraph("MemoryGraph"), + logger(loggerGetOrCreate("MemoryManager")) {} - // no copying or assigning + // ... and no copying or assigning MemoryManager(const MemoryManager&) = delete; MemoryManager& operator=(const MemoryManager&) = delete ; + /** + * @brief Custom edge in memory graph representing a memory mapping + * + * A memory mapping maps from one address space into another and can only be + * traversed in the forward direction which reflects the nature of real + * memory mappings. + * + * Implementation Notes: + * The member #src is the address in the "from" address space, where the + * destination address space is mapped. The member #dest is the address in + * the destination address space, where the mapping points to. Often, #dest + * will be zero for mappings to hardware, but consider the example when + * mapping FPGA to application memory: + * The application allocates a block 1kB at address + * 0x843001000 in its address space. The mapping would then have a #dest + * address of 0x843001000 and a #size of 1024. + */ + class Mapping : public graph::Edge { + public: + std::string name; ///< Human-readable name + uintptr_t src; ///< Base address in "from" address space + uintptr_t dest; ///< Base address in "to" address space + size_t size; ///< Size of the mapping + + friend std::ostream& + operator<< (std::ostream& stream, const Mapping& mapping) + { + return stream << static_cast(mapping) << " = " + << mapping.name + << std::hex + << "(src=0x" << mapping.src + << ", dest=0x" << mapping.dest + << ", size=0x" << mapping.size + << ")"; + } + + }; + + + /** + * @brief Custom vertex in memory graph representing an address space + * + * Since most information in the memory graph is stored in the edges (memory + * mappings), this is just a small extension to the default vertex. It only + * associates an additional string #name for human-readability. + */ + class AddressSpace : public graph::Vertex { + public: + std::string name; ///< Human-readable name + + friend std::ostream& + operator<< (std::ostream& stream, const AddressSpace& addrSpace) + { + return stream << static_cast(addrSpace) << " = " + << addrSpace.name; + } + }; + + /// Memory graph with custom edges and vertices for address resolution using MemoryGraph = graph::DirectedGraph; public: using AddressSpaceId = MemoryGraph::VertexIdentifier; using MappingId = MemoryGraph::EdgeIdentifier; - static MemoryManager& get(); + /// Get singleton instance + static MemoryManager& + get(); + AddressSpaceId + getProcessAddressSpace() + { return getOrCreateAddressSpace("villas-fpga"); } - AddressSpaceId createAddressSpace(std::string name); + AddressSpaceId + getOrCreateAddressSpace(std::string name); /// Create a default mapping - MappingId createMapping(uintptr_t src, uintptr_t dest, size_t size, - AddressSpaceId fromAddrSpace, - AddressSpaceId toAddrSpace); + MappingId + createMapping(uintptr_t src, uintptr_t dest, size_t size, std::string name, + AddressSpaceId fromAddrSpace, + AddressSpaceId toAddrSpace); /// Add a mapping /// /// Can be used to derive from Mapping in order to implement custom /// constructor/destructor. - MappingId addMapping(std::shared_ptr mapping, - AddressSpaceId fromAddrSpace, - AddressSpaceId toAddrSpace); + MappingId + addMapping(std::shared_ptr mapping, + AddressSpaceId fromAddrSpace, + AddressSpaceId toAddrSpace); - void dump() + + AddressSpaceId + findAddressSpace(std::string name); + + MemoryTranslation + getTranslation(AddressSpaceId fromAddrSpaceId, AddressSpaceId toAddrSpaceId); + + MemoryTranslation + getTranslationFromProcess(AddressSpaceId foreignAddrSpaceId) + { return getTranslation(getProcessAddressSpace(), foreignAddrSpaceId); } + + static std::string + getSlaveAddrSpaceName(std::string ipInstance, std::string memoryBlock) + { return ipInstance + "/" + memoryBlock; } + + void + dump() { memoryGraph.dump(); } + private: + /// Convert a Mapping to MemoryTranslation for calculations + static MemoryTranslation + getTranslationFromMapping(const Mapping& mapping) + { return MemoryTranslation(mapping.src, mapping.dest, mapping.size); } + + +private: + /// Directed graph that stores address spaces and memory mappings MemoryGraph memoryGraph; + + /// Cache mapping of names to address space ids for fast lookup + std::map addrSpaceLookup; + + /// Logger for universal access in this class + SpdLogger logger; + + /// Static pointer to global instance, because this is a singleton static MemoryManager* instance; }; diff --git a/fpga/lib/memory_manager.cpp b/fpga/lib/memory_manager.cpp index 59269fe04..55f7858b1 100644 --- a/fpga/lib/memory_manager.cpp +++ b/fpga/lib/memory_manager.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include "memory_manager.hpp" @@ -18,20 +20,32 @@ MemoryManager::get() } MemoryManager::AddressSpaceId -MemoryManager::createAddressSpace(std::string name) +MemoryManager::getOrCreateAddressSpace(std::string name) { - std::shared_ptr addrSpace(new AddressSpace); - addrSpace->name = name; + try { + // try fast lookup + return addrSpaceLookup.at(name); + } catch (const std::out_of_range&) { + // does not yet exist, create + std::shared_ptr addrSpace(new AddressSpace); + addrSpace->name = name; - return memoryGraph.addVertex(addrSpace); + // cache it for the next access + addrSpaceLookup[name] = memoryGraph.addVertex(addrSpace); + + return addrSpaceLookup[name]; + } } MemoryManager::MappingId MemoryManager::createMapping(uintptr_t src, uintptr_t dest, size_t size, + std::string name, MemoryManager::AddressSpaceId fromAddrSpace, MemoryManager::AddressSpaceId toAddrSpace) { std::shared_ptr mapping(new Mapping); + + mapping->name = name; mapping->src = src; mapping->dest = dest; mapping->size = size; @@ -47,11 +61,109 @@ MemoryManager::addMapping(std::shared_ptr mapping, return memoryGraph.addEdge(mapping, fromAddrSpace, toAddrSpace); } - -Mapping::~Mapping() +MemoryManager::AddressSpaceId +MemoryManager::findAddressSpace(std::string name) { - + return memoryGraph.findVertex( + [&](const std::shared_ptr& v) { + return v->name == name; + }); } +MemoryTranslation +MemoryManager::getTranslation(MemoryManager::AddressSpaceId fromAddrSpaceId, + MemoryManager::AddressSpaceId toAddrSpaceId) +{ + // find a path through the memory graph + MemoryGraph::Path path; + if(not memoryGraph.getPath(fromAddrSpaceId, toAddrSpaceId, path)) { + auto fromAddrSpace = memoryGraph.getVertex(fromAddrSpaceId); + auto toAddrSpace = memoryGraph.getVertex(toAddrSpaceId); + + logger->error("No translation found from ({}) to ({})", + *fromAddrSpace, *toAddrSpace); + + throw std::out_of_range("no translation found"); + } + + // start with an identity mapping + MemoryTranslation translation(0, 0, SIZE_MAX); + + // iterate through path and merge all mappings into a single translation + for(auto& mappingId : path) { + auto mapping = memoryGraph.getEdge(mappingId); + translation += getTranslationFromMapping(*mapping); + } + + return translation; +} + +uintptr_t +MemoryTranslation::getLocalAddr(uintptr_t addrInForeignAddrSpace) const +{ + assert(addrInForeignAddrSpace >= dst); + assert(addrInForeignAddrSpace < (dst + size)); + return src + addrInForeignAddrSpace - dst; +} + +uintptr_t +MemoryTranslation::getForeignAddr(uintptr_t addrInLocalAddrSpace) const +{ + assert(addrInLocalAddrSpace >= src); + assert(addrInLocalAddrSpace < (src + size)); + return dst + addrInLocalAddrSpace - src; +} + +MemoryTranslation& +MemoryTranslation::operator+=(const MemoryTranslation& other) +{ + auto logger = loggerGetOrCreate("MemoryTranslation"); + // set level to debug to enable debug output + logger->set_level(spdlog::level::info); + + const uintptr_t this_dst_high = this->dst + this->size; + const uintptr_t other_src_high = other.src + other.size; + + // make sure there is a common memory area + assert(other.src < this_dst_high); + assert(this->dst < other_src_high); + + const uintptr_t hi = std::max(this_dst_high, other_src_high); + const uintptr_t lo = std::min(this->dst, other.src); + + const uintptr_t diff_hi = (this_dst_high > other_src_high) + ? (this_dst_high - other_src_high) + : (other_src_high - this_dst_high); + + const uintptr_t diff_lo = (this->dst > other.src) + ? (this->dst - other.src) + : (other.src - this->dst); + + const size_t size = (hi - lo) - diff_hi - diff_lo; + + logger->debug("this->src: 0x{:x}", this->src); + logger->debug("this->dst: 0x{:x}", this->dst); + logger->debug("this->size: 0x{:x}", this->size); + logger->debug("other.src: 0x{:x}", other.src); + logger->debug("other.dst: 0x{:x}", other.dst); + logger->debug("other.size: 0x{:x}", other.size); + logger->debug("this_dst_high: 0x{:x}", this_dst_high); + logger->debug("other_src_high: 0x{:x}", other_src_high); + logger->debug("hi: 0x{:x}", hi); + logger->debug("lo: 0x{:x}", lo); + logger->debug("diff_hi: 0x{:x}", diff_hi); + logger->debug("diff_hi: 0x{:x}", diff_lo); + logger->debug("size: 0x{:x}", size); + + this->src += other.src; + this->dst += other.dst; + this->size = size; + + logger->debug("result src: 0x{:x}", this->src); + logger->debug("result dst: 0x{:x}", this->dst); + logger->debug("result size: 0x{:x}", this->size); + + return *this; +} } // namespace villas diff --git a/fpga/tests/graph.cpp b/fpga/tests/graph.cpp index 409d26537..4be542a5c 100644 --- a/fpga/tests/graph.cpp +++ b/fpga/tests/graph.cpp @@ -112,12 +112,21 @@ Test(graph, path, .description = "Find path") Test(graph, memory_manager, .description = "Global Memory Manager") { + auto logger = loggerGetOrCreate("unittest:mm"); auto& mm = villas::MemoryManager::get(); - auto dmaRegs = mm.createAddressSpace("DMA Registers"); - auto pcieBridge = mm.createAddressSpace("PCIe Bridge"); + logger->info("Create address spaces"); + auto dmaRegs = mm.getOrCreateAddressSpace("DMA Registers"); + auto pcieBridge = mm.getOrCreateAddressSpace("PCIe Bridge"); - mm.createMapping(0x1000, 0, 0x1000, dmaRegs, pcieBridge); + logger->info("Create a mapping"); + mm.createMapping(0x1000, 0, 0x1000, "Testmapping", dmaRegs, pcieBridge); + + logger->info("Find address space by name"); + auto vertex = mm.findAddressSpace("PCIe Bridge"); + logger->info(" found: {}", vertex); mm.dump(); + + logger->info(TXT_GREEN("Passed")); } From c3382b9e18467128264eccc440c482d57123c657 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 13:45:01 +0100 Subject: [PATCH 10/21] lib/card: pass string as const reference to lookupIp() --- fpga/include/villas/fpga/card.hpp | 2 +- fpga/lib/card.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fpga/include/villas/fpga/card.hpp b/fpga/include/villas/fpga/card.hpp index 20c89876d..4d838559c 100644 --- a/fpga/include/villas/fpga/card.hpp +++ b/fpga/include/villas/fpga/card.hpp @@ -75,7 +75,7 @@ public: bool reset() { return true; } void dump() { } - ip::IpCore* lookupIp(std::string name) const; + ip::IpCore* lookupIp(const std::string& name) const; ip::IpCoreList ips; ///< IPs located on this FPGA card diff --git a/fpga/lib/card.cpp b/fpga/lib/card.cpp index da0e2d830..b15328566 100644 --- a/fpga/lib/card.cpp +++ b/fpga/lib/card.cpp @@ -132,7 +132,8 @@ fpga::PCIeCardFactory::create() ip::IpCore* -PCIeCard::lookupIp(std::string name) const { +PCIeCard::lookupIp(const std::string& name) const +{ for(auto& ip : ips) { if(*ip == name) { return ip.get(); From 5b0013b33522767e4767a9c2edb2bc8dfd471db8 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 13:45:38 +0100 Subject: [PATCH 11/21] lib/card: add IP lookup by VLNV --- fpga/include/villas/fpga/card.hpp | 1 + fpga/lib/card.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/fpga/include/villas/fpga/card.hpp b/fpga/include/villas/fpga/card.hpp index 4d838559c..4b64a33ec 100644 --- a/fpga/include/villas/fpga/card.hpp +++ b/fpga/include/villas/fpga/card.hpp @@ -76,6 +76,7 @@ public: void dump() { } ip::IpCore* lookupIp(const std::string& name) const; + ip::IpCore* lookupIp(const Vlnv& vlnv) const; ip::IpCoreList ips; ///< IPs located on this FPGA card diff --git a/fpga/lib/card.cpp b/fpga/lib/card.cpp index b15328566..5b38d210c 100644 --- a/fpga/lib/card.cpp +++ b/fpga/lib/card.cpp @@ -142,6 +142,17 @@ PCIeCard::lookupIp(const std::string& name) const return nullptr; } +ip::IpCore* +PCIeCard::lookupIp(const Vlnv& vlnv) const +{ + for(auto& ip : ips) { + if(*ip == vlnv) { + return ip.get(); + } + } + return nullptr; +} + bool fpga::PCIeCard::init() { From ef5f6fa3a8f05a3d081590f9e16f182f385dbc61 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 14:06:17 +0100 Subject: [PATCH 12/21] lib/card: use memory manager to store vfio mapping --- fpga/include/villas/fpga/card.hpp | 6 +++- fpga/lib/card.cpp | 50 ++++++++++++++++++------------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/fpga/include/villas/fpga/card.hpp b/fpga/include/villas/fpga/card.hpp index 4b64a33ec..4676e0b58 100644 --- a/fpga/include/villas/fpga/card.hpp +++ b/fpga/include/villas/fpga/card.hpp @@ -45,6 +45,8 @@ #include "config.h" +#include "memory_manager.hpp" + #define PCI_FILTER_DEFAULT_FPGA { \ .id = { \ .vendor = FPGA_PCI_VID_XILINX, \ @@ -92,7 +94,9 @@ public: ::vfio_container *vfio_container; struct vfio_device vfio_device; /**< VFIO device handle. */ - char *map; /**< PCI BAR0 mapping for register access */ + /// Address space identifier of the master address space of this FPGA card. + /// This will be used for address resolution of all IPs on this card. + MemoryManager::AddressSpaceId addrSpaceId; size_t maplen; size_t dmalen; diff --git a/fpga/lib/card.cpp b/fpga/lib/card.cpp index 5b38d210c..c4a184fc9 100644 --- a/fpga/lib/card.cpp +++ b/fpga/lib/card.cpp @@ -20,31 +20,18 @@ * along with this program. If not, see . *********************************************************************************/ -#include -#include +#include +#include +#include -#include "config.h" -#include "log.h" -#include "log_config.h" -#include "list.h" -#include "utils.h" +#include "log.hpp" #include "kernel/pci.h" #include "kernel/vfio.h" -#include -#include -#include -#include -#include -#include -#include - #include "fpga/ip.hpp" #include "fpga/card.hpp" -#include "log.hpp" - namespace villas { namespace fpga { @@ -101,7 +88,6 @@ fpga::PCIeCardFactory::make(json_t *json, struct pci* pci, ::vfio_container* vc) } - // TODO: currently fails, fix and remove comment if(not card->init()) { logger->warn("Cannot start FPGA card {}", card_name); continue; @@ -154,13 +140,16 @@ PCIeCard::lookupIp(const Vlnv& vlnv) const } -bool fpga::PCIeCard::init() +bool +fpga::PCIeCard::init() { int ret; struct pci_device *pdev; auto logger = getLogger(); + logger->info("Initializing FPGA card {}", name); + /* Search for FPGA card */ pdev = pci_lookup_device(pci, &filter); if (!pdev) { @@ -176,12 +165,31 @@ bool fpga::PCIeCard::init() } /* Map PCIe BAR */ - map = (char*) vfio_map_region(&vfio_device, VFIO_PCI_BAR0_REGION_INDEX); - if (map == MAP_FAILED) { + const void* bar0_mapped = vfio_map_region(&vfio_device, VFIO_PCI_BAR0_REGION_INDEX); + if (bar0_mapped == MAP_FAILED) { logger->error("Failed to mmap() BAR0"); return false; } + + /* Link mapped BAR0 to global memory graph */ + + // get the address space of the current application + auto villasAddrSpace = MemoryManager::get().getProcessAddressSpace(); + + // create a new address space for this FPGA card + this->addrSpaceId = MemoryManager::get().getOrCreateAddressSpace(name); + + // determine size of BAR0 region + const size_t bar0_size = vfio_region_size(&vfio_device, + VFIO_PCI_BAR0_REGION_INDEX); + + // create a mapping from our address space to the FPGA card via vfio + MemoryManager::get().createMapping(reinterpret_cast(bar0_mapped), + 0, bar0_size, "VFIO_map", + villasAddrSpace, this->addrSpaceId); + + /* Enable memory access and PCI bus mastering for DMA */ ret = vfio_pci_enable(&vfio_device); if (ret) { From acf273e4066b0751685553b6a08154b751b9839a Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 14:12:28 +0100 Subject: [PATCH 13/21] tests: let them fail if no Fifo or Timer is found --- fpga/tests/fifo.cpp | 11 ++++++++--- fpga/tests/graph.cpp | 4 ++++ fpga/tests/timer.cpp | 6 +++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/fpga/tests/fifo.cpp b/fpga/tests/fifo.cpp index 6b01c6758..e3f7a250b 100644 --- a/fpga/tests/fifo.cpp +++ b/fpga/tests/fifo.cpp @@ -34,6 +34,7 @@ Test(fpga, fifo, .description = "FIFO") { ssize_t len; char src[255], dst[255]; + size_t count = 0; auto logger = loggerGetOrCreate("unittest:fifo"); @@ -46,12 +47,14 @@ Test(fpga, fifo, .description = "FIFO") auto fifo = reinterpret_cast(*ip); - if(not fifo.loopbackPossible()) { - logger->info("Loopback test not possible for {}", *ip); + if(not fifo.connectLoopback()) { continue; } - if(not fifo.connectLoopback()) { + count++; + + if(not fifo.loopbackPossible()) { + logger->info("Loopback test not possible for {}", *ip); continue; } @@ -80,4 +83,6 @@ Test(fpga, fifo, .description = "FIFO") logger->info(TXT_GREEN("Passed")); } + + cr_assert(count > 0, "No fifo found"); } diff --git a/fpga/tests/graph.cpp b/fpga/tests/graph.cpp index 4be542a5c..ed14ee33e 100644 --- a/fpga/tests/graph.cpp +++ b/fpga/tests/graph.cpp @@ -44,6 +44,8 @@ Test(graph, basic, .description = "DirectedGraph") g.dump(); cr_assert(g.getVertexCount() == 2); cr_assert(g.vertexGetEdges(v2id).size() == 0); + + logger->info(TXT_GREEN("Passed")); } Test(graph, path, .description = "Find path") @@ -108,6 +110,8 @@ Test(graph, path, .description = "Find path") for(auto& edge : path4) { logger->info(" -> edge {}", edge); } + + logger->info(TXT_GREEN("Passed")); } Test(graph, memory_manager, .description = "Global Memory Manager") diff --git a/fpga/tests/timer.cpp b/fpga/tests/timer.cpp index 278f8f418..4317e51b6 100644 --- a/fpga/tests/timer.cpp +++ b/fpga/tests/timer.cpp @@ -34,6 +34,8 @@ Test(fpga, timer, .description = "Timer Counter") { auto logger = loggerGetOrCreate("unittest:timer"); + size_t count = 0; + for(auto& ip : state.cards.front()->ips) { // skip non-timer IPs if(*ip != villas::fpga::Vlnv("xilinx.com:ip:axi_timer:")) { @@ -42,6 +44,8 @@ Test(fpga, timer, .description = "Timer Counter") logger->info("Testing {}", *ip); + count++; + auto timer = reinterpret_cast(*ip); logger->info("Test simple waiting"); @@ -68,5 +72,5 @@ Test(fpga, timer, .description = "Timer Counter") logger->info(TXT_GREEN("Passed")); } - return; + cr_assert(count > 0, "No timer found"); } From e93b31bbf117ae1b092d961182915acb8d002c04 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 14:13:14 +0100 Subject: [PATCH 14/21] lib/ips: make use of MemoryManager and new config layout --- fpga/include/villas/fpga/ip.hpp | 62 ++++-- fpga/include/villas/fpga/ips/fifo.hpp | 11 +- fpga/include/villas/fpga/ips/intc.hpp | 16 +- fpga/include/villas/fpga/ips/pcie.hpp | 80 ++++++++ fpga/include/villas/fpga/ips/switch.hpp | 11 +- fpga/include/villas/fpga/ips/timer.hpp | 13 +- fpga/lib/CMakeLists.txt | 24 ++- fpga/lib/ip.cpp | 250 +++++++++++++++--------- fpga/lib/ips/fifo.cpp | 28 ++- fpga/lib/ips/intc.cpp | 8 +- fpga/lib/ips/pcie.cpp | 53 +++++ fpga/lib/ips/switch.cpp | 8 +- fpga/lib/ips/timer.cpp | 21 +- 13 files changed, 419 insertions(+), 166 deletions(-) create mode 100644 fpga/include/villas/fpga/ips/pcie.hpp create mode 100644 fpga/lib/ips/pcie.cpp diff --git a/fpga/include/villas/fpga/ip.hpp b/fpga/include/villas/fpga/ip.hpp index b71f5d700..fc3a777e0 100644 --- a/fpga/include/villas/fpga/ip.hpp +++ b/fpga/include/villas/fpga/ip.hpp @@ -41,14 +41,23 @@ #include +#include "memory_manager.hpp" namespace villas { namespace fpga { +// forward declaration class PCIeCard; namespace ip { +// forward declarations +class IpCore; +class IpCoreFactory; +class InterruptController; + +using IpCoreList = std::list>; + class IpIdentifier { public: @@ -58,25 +67,30 @@ public: IpIdentifier(std::string vlnvString, std::string name = "") : vlnv(vlnvString), name(name) {} + const std::string& + getName() const + { return name; } + + const Vlnv& + getVlnv() const + { return vlnv; } + friend std::ostream& operator<< (std::ostream& stream, const IpIdentifier& id) { return stream << TXT_BOLD(id.name) << " vlnv=" << id.vlnv; } +private: Vlnv vlnv; std::string name; }; -using IpDependency = std::pair; - -// forward declarations -class IpCoreFactory; class IpCore { public: friend IpCoreFactory; - IpCore() : card(nullptr), baseaddr(0) {} + IpCore() : card(nullptr) {} virtual ~IpCore() {} // IPs can implement this interface @@ -88,10 +102,10 @@ public: bool operator== (const IpIdentifier& otherId) { - const bool vlnvMatch = id.vlnv == otherId.vlnv; - const bool nameWildcard = id.name.empty() or otherId.name.empty(); + const bool vlnvMatch = id.getVlnv() == otherId.getVlnv(); + const bool nameWildcard = id.getName().empty() or otherId.getName().empty(); - return vlnvMatch and (nameWildcard or id.name == otherId.name); + return vlnvMatch and (nameWildcard or id.getName() == otherId.getName()); } bool @@ -101,45 +115,55 @@ public: bool operator== (const Vlnv& otherVlnv) - { return id.vlnv == otherVlnv; } + { return id.getVlnv() == otherVlnv; } bool operator== (const std::string& otherName) - { return id.name == otherName; } + { return id.getName() == otherName; } friend std::ostream& operator<< (std::ostream& stream, const IpCore& ip) { return stream << ip.id; } + const std::string& + getInstanceName() + { return id.getName(); } + protected: - uintptr_t - getBaseaddr() const - { return getAddrMapped(this->baseaddr); } uintptr_t - getAddrMapped(uintptr_t address) const; + getBaseAddr(const std::string& block) const; + + uintptr_t + getLocalAddr(const std::string& block, uintptr_t address) const; SpdLogger - getLogger() { return loggerGetOrCreate(id.name); } + getLogger() { return loggerGetOrCreate(id.getName()); } struct IrqPort { int num; - std::string controllerName; + InterruptController* irqController; std::string description; }; + InterruptController* + getInterruptController(const std::string& interruptName); + protected: + virtual std::list getMemoryBlocks() const { return {}; } + // populated by FpgaIpFactory PCIeCard* card; ///< FPGA card this IP is instantiated on IpIdentifier id; ///< VLNV and name defined in JSON config - uintptr_t baseaddr; ///< The baseadress of this IP component std::map irqs; ///< Interrupts of this IP component std::map dependencies; ///< dependencies on other IPs + + /// Cached translations from the process address space to each memory block + std::map addressTranslations; }; -using IpCoreList = std::list>; class IpCoreFactory : public Plugin { @@ -164,11 +188,9 @@ private: virtual bool configureJson(IpCore& /* ip */, json_t* /* json */) { return true; } - virtual Vlnv getCompatibleVlnv() const = 0; virtual std::string getName() const = 0; virtual std::string getDescription() const = 0; - virtual std::list getDependencies() const { return {}; } protected: static SpdLogger diff --git a/fpga/include/villas/fpga/ips/fifo.hpp b/fpga/include/villas/fpga/ips/fifo.hpp index 5d9113448..001fbf19b 100644 --- a/fpga/include/villas/fpga/ips/fifo.hpp +++ b/fpga/include/villas/fpga/ips/fifo.hpp @@ -50,8 +50,14 @@ public: size_t read(void* buf, size_t len); private: + static constexpr char registerMemory[] = "Mem0"; + static constexpr char axi4Memory[] = "Mem1"; + static constexpr char irqName[] = "interrupt"; + + std::list getMemoryBlocks() const + { return { registerMemory, axi4Memory }; } + XLlFifo xFifo; - uintptr_t baseaddr_axi4; }; @@ -77,9 +83,6 @@ public: Vlnv getCompatibleVlnv() const { return {"xilinx.com:ip:axi_fifo_mm_s:"}; } - - std::list getDependencies() const - { return { {"intc", Vlnv("acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:") } }; } }; } // namespace ip diff --git a/fpga/include/villas/fpga/ips/intc.hpp b/fpga/include/villas/fpga/ips/intc.hpp index 68819371d..7ccef8038 100644 --- a/fpga/include/villas/fpga/ips/intc.hpp +++ b/fpga/include/villas/fpga/ips/intc.hpp @@ -61,6 +61,13 @@ public: { return waitForInterrupt(irq.num); } private: + + static constexpr char registerMemory[] = "Reg"; + + std::list getMemoryBlocks() const + { return { registerMemory }; } + + struct Interrupt { int eventFd; /**< Event file descriptor */ int number; /**< Interrupt number from /proc/interrupts */ @@ -82,6 +89,10 @@ public: IpCoreFactory(getName()) {} + static constexpr const char* + getCompatibleVlnvString() + { return "acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:"; } + IpCore* create() { return new InterruptController; } @@ -94,7 +105,10 @@ public: { return "Xilinx's programmable interrupt controller"; } Vlnv getCompatibleVlnv() const - { return Vlnv("acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:"); } + { return Vlnv(getCompatibleVlnvString()); } + +// std::list getDependencies() const +// { return { {"pcie", Vlnv("xilinx.com:ip:axi_pcie:") } }; } }; } // namespace ip diff --git a/fpga/include/villas/fpga/ips/pcie.hpp b/fpga/include/villas/fpga/ips/pcie.hpp new file mode 100644 index 000000000..a63db2e20 --- /dev/null +++ b/fpga/include/villas/fpga/ips/pcie.hpp @@ -0,0 +1,80 @@ +/** AXI Stream interconnect related helper functions + * + * These functions present a simpler interface to Xilinx' AXI Stream switch driver (XAxis_Switch_*) + * + * @file + * @author Steffen Vogel + * @author Daniel Krebs + * @copyright 2017, Steffen Vogel + * @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 + * @{ + */ + +#pragma once + +#include +#include + +#include +#include + +#include "fpga/ip_node.hpp" +#include "fpga/vlnv.hpp" + +namespace villas { +namespace fpga { +namespace ip { + +class AxiPciExpressBridge : public IpCore { +public: + friend class AxiPciExpressBridgeFactory; + + bool init(); +}; + + +class AxiPciExpressBridgeFactory : public IpCoreFactory { +public: + AxiPciExpressBridgeFactory() : + IpCoreFactory(getName()) {} + + static constexpr const char* + getCompatibleVlnvString() + { return "xilinx.com:ip:axi_pcie:"; } + + IpCore* create() + { return new AxiPciExpressBridge; } + + std::string getName() const + { return "AxiPciExpressBridge"; } + + std::string getDescription() const + { return "Xilinx's AXI-PCIe Bridge"; } + + Vlnv getCompatibleVlnv() const + { return Vlnv(getCompatibleVlnvString()); } +}; + +} // namespace ip +} // namespace fpga +} // namespace villas + +/** @} */ diff --git a/fpga/include/villas/fpga/ips/switch.hpp b/fpga/include/villas/fpga/ips/switch.hpp index b4418f5dd..796cab711 100644 --- a/fpga/include/villas/fpga/ips/switch.hpp +++ b/fpga/include/villas/fpga/ips/switch.hpp @@ -56,6 +56,11 @@ public: private: static constexpr int PORT_DISABLED = -1; + static constexpr char registerMemory[] = "Reg"; + + std::list getMemoryBlocks() const + { return { registerMemory }; } + struct Path { IpCore* masterOut; IpCore* slaveIn; @@ -72,6 +77,10 @@ public: AxiStreamSwitchFactory() : IpNodeFactory(getName()) {} + static constexpr const char* + getCompatibleVlnvString() + { return "xilinx.com:ip:axis_switch:"; } + bool configureJson(IpCore& ip, json_t *json_ip); IpCore* create() @@ -84,7 +93,7 @@ public: { return "Xilinx's AXI4-Stream switch"; } Vlnv getCompatibleVlnv() const - { return Vlnv("xilinx.com:ip:axis_switch:"); } + { return Vlnv(getCompatibleVlnvString()); } }; } // namespace ip diff --git a/fpga/include/villas/fpga/ips/timer.hpp b/fpga/include/villas/fpga/ips/timer.hpp index 771d2723b..56231069b 100644 --- a/fpga/include/villas/fpga/ips/timer.hpp +++ b/fpga/include/villas/fpga/ips/timer.hpp @@ -43,10 +43,10 @@ namespace ip { class Timer : public IpCore { + friend class TimerFactory; public: bool init(); - bool start(uint32_t ticks); bool wait(); uint32_t remaining(); @@ -62,8 +62,14 @@ public: { return FPGA_AXI_HZ; } private: + + std::list getMemoryBlocks() const + { return { registerMemory }; } + + static constexpr char irqName[] = "generateout0"; + static constexpr char registerMemory[] = "Reg"; + XTmrCtr xTmr; - InterruptController* intc; }; @@ -88,9 +94,6 @@ public: Vlnv getCompatibleVlnv() const { return {"xilinx.com:ip:axi_timer:"}; } - - std::list getDependencies() const - { return { {"intc", Vlnv("acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:") } }; } }; } // namespace ip diff --git a/fpga/lib/CMakeLists.txt b/fpga/lib/CMakeLists.txt index ae55a332f..cedc633f5 100644 --- a/fpga/lib/CMakeLists.txt +++ b/fpga/lib/CMakeLists.txt @@ -1,39 +1,43 @@ set(SOURCES - ip.cpp ip.c - ip_node.cpp - vlnv.cpp vlnv.c card.c + + vlnv.cpp card.cpp + ip.cpp + ip_node.cpp ips/timer.c - ips/timer.cpp ips/model.c ips/switch.c - ips/switch.cpp ips/dft.c ips/fifo.c - ips/fifo.cpp ips/dma.c - ips/intc.cpp ips/intc.c ips/gpio.c ips/rtds_axis.c + ips/timer.cpp + ips/switch.cpp + ips/fifo.cpp + ips/intc.cpp + ips/pcie.cpp + kernel/kernel.c kernel/pci.c kernel/vfio.c - memory_manager.cpp plugin.c - plugin.cpp utils.c - utils.cpp list.c log.c log_config.c log_helper.c + + plugin.cpp + utils.cpp + memory_manager.cpp ) include(FindPkgConfig) diff --git a/fpga/lib/ip.cpp b/fpga/lib/ip.cpp index a6d35bd7c..39e3c4805 100644 --- a/fpga/lib/ip.cpp +++ b/fpga/lib/ip.cpp @@ -20,30 +20,24 @@ * along with this program. If not, see . *********************************************************************************/ -#include "log_config.h" -#include "log.hpp" -#include "plugin.h" -#include "utils.hpp" - -#include "fpga/vlnv.hpp" -#include "fpga/ip.hpp" -#include "fpga/card.hpp" - -#include -#include -#include #include #include #include #include "log.hpp" +#include "utils.hpp" +#include "memory_manager.hpp" +#include "fpga/ip.hpp" +#include "fpga/vlnv.hpp" +#include "fpga/card.hpp" + +// needed to get VLNVs for initialization order list #include "fpga/ips/pcie.hpp" #include "fpga/ips/intc.hpp" #include "fpga/ips/switch.hpp" - namespace villas { namespace fpga { namespace ip { @@ -58,46 +52,20 @@ vlnvInitializationOrder = { Vlnv(AxiStreamSwitchFactory::getCompatibleVlnvString()), }; -void -IpCore::dump() { - auto logger = getLogger(); - - logger->info("Base address = {:08x}", baseaddr); - for(auto& [num, irq] : irqs) { - logger->info("IRQ {}: {}:{}", num, irq.controllerName, irq.num); - } -} - - -IpCoreFactory* IpCoreFactory::lookup(const Vlnv &vlnv) -{ - for(auto& ip : Plugin::lookup(Plugin::Type::FpgaIp)) { - IpCoreFactory* ipCoreFactory = dynamic_cast(ip); - - if(ipCoreFactory->getCompatibleVlnv() == vlnv) - return ipCoreFactory; - } - - return nullptr; -} - -uintptr_t -IpCore::getAddrMapped(uintptr_t address) const -{ - assert(card != nullptr); - return reinterpret_cast(card->map) + address; -} - IpCoreList IpCoreFactory::make(PCIeCard* card, json_t *json_ips) { - IpCoreList configuredIps; + // We only have this logger until we know the factory to build an IP with auto loggerStatic = getStaticLogger(); - std::list allIps; // all IPs available + std::list allIps; // all IPs available in config std::list orderedIps; // IPs ordered in initialization order + IpCoreList configuredIps; // Successfully configured IPs + IpCoreList initializedIps; // Initialized, i.e. ready-to-use IPs + + // parse all IP instance names and their VLNV into list `allIps` const char* ipName; json_t* json_ip; @@ -111,7 +79,6 @@ IpCoreFactory::make(PCIeCard* card, json_t *json_ips) allIps.push_back({vlnv, ipName}); } - // Pick out IPs to be initialized first. // // Reverse order of the initialization order list, because we push to the @@ -132,12 +99,12 @@ IpCoreFactory::make(PCIeCard* card, json_t *json_ips) // insert all other IPs at the end orderedIps.splice(orderedIps.end(), allIps); - loggerStatic->debug("IP initialization order:"); for(auto& id : orderedIps) { loggerStatic->debug(" {}", TXT_BOLD(id.getName())); } + // configure all IPs for(auto& id : orderedIps) { loggerStatic->info("Initializing {}", id); @@ -176,25 +143,38 @@ IpCoreFactory::make(PCIeCard* card, json_t *json_ips) ip->card = card; ip->id = id; - // extract base address if it has one - if(json_unpack(json_ip, "{ s?: i }", "baseaddr", &ip->baseaddr) != 0) { - logger->warn("Problem while parsing base address of IP {}", - TXT_BOLD(ipName)); - continue; - } + json_t* json_ip = json_object_get(json_ips, id.getName().c_str()); json_t* json_irqs = json_object_get(json_ip, "irqs"); if(json_is_object(json_irqs)) { - const char* irq_name; + logger->debug("Parse IRQs of {}", *ip); + + const char* irqName; json_t* json_irq; - json_object_foreach(json_irqs, irq_name, json_irq) { - const char* irq = json_string_value(json_irq); + json_object_foreach(json_irqs, irqName, json_irq) { + const char* irqEntry = json_string_value(json_irq); - auto tokens = utils::tokenize(irq, ":"); + auto tokens = utils::tokenize(irqEntry, ":"); if(tokens.size() != 2) { logger->warn("Cannot parse IRQ '{}' of {}", - irq, TXT_BOLD(ipName)); + irqEntry, TXT_BOLD(id.getName())); + continue; + } + + const std::string& irqControllerName = tokens[0]; + InterruptController* intc = nullptr; + + for(auto& configuredIp : configuredIps) { + if(*configuredIp == irqControllerName) { + intc = dynamic_cast(configuredIp.get()); + break; + } + } + + if(intc == nullptr) { + logger->error("Interrupt Controller {} for IRQ {} not found", + irqControllerName, irqName); continue; } @@ -202,39 +182,66 @@ IpCoreFactory::make(PCIeCard* card, json_t *json_ips) try { num = std::stoi(tokens[1]); } catch(const std::invalid_argument&) { - logger->warn("IRQ number is not an integer: '{}'", irq); + logger->warn("IRQ number is not an integer: '{}'", irqEntry); continue; } - logger->debug("IRQ: {} -> {}:{}", irq_name, tokens[0], num); - ip->irqs[irq_name] = {num, tokens[0], ""}; + logger->debug("IRQ: {} -> {}:{}", irqName, irqControllerName, num); + ip->irqs[irqName] = {num, intc, ""}; } - } else { - logger->debug("IP has no interrupts"); } - bool dependenciesOk = true; - for(auto& [depName, depVlnv] : ipCoreFactory->getDependencies()) { - // lookup dependency IP core in list of already initialized IPs - auto iter = std::find_if(initializedIps.begin(), - initializedIps.end(), - [&](const std::unique_ptr& ip) { - return *ip == depVlnv; - }); + json_t* json_memory_view = json_object_get(json_ip, "memory-view"); + if(json_is_object(json_memory_view)) { + logger->debug("Parse memory view of {}", *ip); - if(iter == initializedIps.end()) { - logger->error("Cannot find '{}' dependency {} of {}", - depName, depVlnv, TXT_BOLD(ipName)); - dependenciesOk = false; - break; + // create a master address space because this IP has a memory view + const MemoryManager::AddressSpaceId myAddrSpaceId = + MemoryManager::get().getOrCreateAddressSpace(id.getName()); + + // now find all slave address spaces this master can access + const char* bus_name; + json_t* json_bus; + json_object_foreach(json_memory_view, bus_name, json_bus) { + + const char* instance_name; + json_t* json_instance; + json_object_foreach(json_bus, instance_name, json_instance) { + + const char* block_name; + json_t* json_block; + json_object_foreach(json_instance, block_name, json_block) { + + int base, high, size; + int ret = json_unpack(json_block, "{ s: i, s: i, s: i }", + "baseaddr", &base, + "highaddr", &high, + "size", &size); + if(ret != 0) { + logger->error("Cannot parse address block {}/{}/{}/{}", + ip->getInstanceName(), + bus_name, instance_name, block_name); + continue; + + } + + // get or create the slave address space + const std::string slaveAddrSpace = + MemoryManager::getSlaveAddrSpaceName(instance_name, block_name); + + const MemoryManager::AddressSpaceId slaveAddrSpaceId = + MemoryManager::get().getOrCreateAddressSpace(slaveAddrSpace); + + // create a new mapping to the slave address space + MemoryManager::get().createMapping(static_cast(base), + 0, + static_cast(size), + bus_name, + myAddrSpaceId, + slaveAddrSpaceId); + } + } } - - logger->debug("Found dependency IP {}", (*iter)->id); - ip->dependencies[depName] = (*iter).get(); - } - - if(not dependenciesOk) { - continue; } // IP-specific setup via JSON config @@ -243,10 +250,11 @@ IpCoreFactory::make(PCIeCard* card, json_t *json_ips) continue; } + // IP has been configured now configuredIps.push_back(std::move(ip)); } - IpCoreList initializedIps; + // Start and check IPs now for(auto& ip : configuredIps) { // Translate all memory blocks that the IP needs to be accessible from @@ -255,38 +263,100 @@ IpCoreFactory::make(PCIeCard* card, json_t *json_ips) for(auto& memoryBlock : ip->getMemoryBlocks()) { // construct the global name of this address block const auto addrSpaceName = - MemoryManager::getSlaveAddrSpaceName(ip->id.getName(), memoryBlock); + MemoryManager::getSlaveAddrSpaceName(ip->getInstanceName(), + memoryBlock); // retrieve its address space identifier - const auto addrSpace = + const auto addrSpaceId = MemoryManager::get().findAddressSpace(addrSpaceName); // get the translation to the address space const auto& translation = - MemoryManager::get().getTranslationFromProcess(addrSpace); + MemoryManager::get().getTranslationFromProcess(addrSpaceId); // cache it in the IP instance only with local name ip->addressTranslations.emplace(memoryBlock, translation); } - if(not ip->init()) { - loggerStatic->error("Cannot start IP {}", ip->id.getName()); + loggerStatic->error("Cannot start IP {}", *ip); continue; } if(not ip->check()) { - loggerStatic->error("Checking of IP {} failed", ip->id.getName()); + loggerStatic->error("Checking failed for IP {}", *ip); continue; } + // will only be reached if the IP successfully was initialized initializedIps.push_back(std::move(ip)); } + loggerStatic->debug("Initialized IPs:"); + for(auto& ip : initializedIps) { + loggerStatic->debug(" {}", *ip); + } + return initializedIps; } + +void +IpCore::dump() { + auto logger = getLogger(); + + logger->info("IP: {}", *this); + for(auto& [num, irq] : irqs) { + logger->info("IRQ {}: {}:{}", + num, irq.irqController->getInstanceName(), irq.num); + } +} + + +IpCoreFactory* +IpCoreFactory::lookup(const Vlnv &vlnv) +{ + for(auto& ip : Plugin::lookup(Plugin::Type::FpgaIp)) { + IpCoreFactory* ipCoreFactory = dynamic_cast(ip); + + if(ipCoreFactory->getCompatibleVlnv() == vlnv) + return ipCoreFactory; + } + + return nullptr; +} + + +uintptr_t +IpCore::getBaseAddr(const std::string& block) const +{ + return getLocalAddr(block, 0); +} + + +uintptr_t +IpCore::getLocalAddr(const std::string& block, uintptr_t address) const +{ + // throws exception if block not present + auto& translation = addressTranslations.at(block); + + return translation.getLocalAddr(address); +} + + +InterruptController* +IpCore::getInterruptController(const std::string& interruptName) +{ + try { + const IrqPort irq = irqs.at(interruptName); + return irq.irqController; + } catch(const std::out_of_range&) { + return nullptr; + } +} + + } // namespace ip } // namespace fpga } // namespace villas diff --git a/fpga/lib/ips/fifo.cpp b/fpga/lib/ips/fifo.cpp index 9c465302e..23766a7d0 100644 --- a/fpga/lib/ips/fifo.cpp +++ b/fpga/lib/ips/fifo.cpp @@ -50,33 +50,32 @@ FifoFactory::configureJson(IpCore &ip, json_t *json_ip) return false; } - auto& fifo = reinterpret_cast(ip); - if(json_unpack(json_ip, "{ s: i }", "axi4_baseaddr", &fifo.baseaddr_axi4) != 0) { - logger->warn("Cannot parse property 'axi4_baseaddr'"); - return false; - } - return true; } bool Fifo::init() { + auto logger = getLogger(); + XLlFifo_Config fifo_cfg; - fifo_cfg.Axi4BaseAddress = getAddrMapped(this->baseaddr_axi4); + fifo_cfg.Axi4BaseAddress = getBaseAddr(axi4Memory); // use AXI4 for Data, AXI4-Lite for control - fifo_cfg.Datainterface = (this->baseaddr_axi4 != static_cast(-1)) ? 1 : 0; + fifo_cfg.Datainterface = (fifo_cfg.Axi4BaseAddress != -1) ? 1 : 0; - if (XLlFifo_CfgInitialize(&xFifo, &fifo_cfg, getBaseaddr()) != XST_SUCCESS) + if (XLlFifo_CfgInitialize(&xFifo, &fifo_cfg, getBaseAddr(registerMemory)) != XST_SUCCESS) return false; + if(irqs.find(irqName) == irqs.end()) { + logger->error("IRQ '{}' not found but required", irqName); + return false; + } + // Receive complete IRQ XLlFifo_IntEnable(&xFifo, XLLF_INT_RC_MASK); - - auto intc = reinterpret_cast(dependencies["intc"]); - intc->enableInterrupt(irqs["interrupt"], false); + irqs[irqName].irqController->enableInterrupt(irqs[irqName], false); return true; } @@ -85,6 +84,7 @@ bool Fifo::stop() { // Receive complete IRQ XLlFifo_IntDisable(&xFifo, XLLF_INT_RC_MASK); + irqs[irqName].irqController->disableInterrupt(irqs[irqName]); return true; } @@ -110,10 +110,8 @@ size_t Fifo::read(void *buf, size_t len) size_t nextlen = 0; size_t rxlen; - auto intc = reinterpret_cast(dependencies["intc"]); - while (!XLlFifo_IsRxDone(&xFifo)) - intc->waitForInterrupt(irqs["interrupt"].num); + irqs[irqName].irqController->waitForInterrupt(irqs[irqName]); XLlFifo_IntClear(&xFifo, XLLF_INT_RC_MASK); diff --git a/fpga/lib/ips/intc.cpp b/fpga/lib/ips/intc.cpp index 90cbd7144..6c841e4da 100644 --- a/fpga/lib/ips/intc.cpp +++ b/fpga/lib/ips/intc.cpp @@ -48,7 +48,7 @@ InterruptController::~InterruptController() bool InterruptController::init() { - const uintptr_t base = getBaseaddr(); + const uintptr_t base = getBaseAddr(registerMemory); auto logger = getLogger(); num_irqs = vfio_pci_msi_init(&card->vfio_device, efds); @@ -86,7 +86,7 @@ bool InterruptController::enableInterrupt(InterruptController::IrqMaskType mask, bool polling) { auto logger = getLogger(); - const uintptr_t base = getBaseaddr(); + const uintptr_t base = getBaseAddr(registerMemory); /* Current state of INTC */ const uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET); @@ -120,7 +120,7 @@ InterruptController::enableInterrupt(InterruptController::IrqMaskType mask, bool bool InterruptController::disableInterrupt(InterruptController::IrqMaskType mask) { - const uintptr_t base = getBaseaddr(); + const uintptr_t base = getBaseAddr(registerMemory); uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET); XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask); @@ -133,7 +133,7 @@ InterruptController::waitForInterrupt(int irq) { assert(irq < maxIrqs); - const uintptr_t base = getBaseaddr(); + const uintptr_t base = getBaseAddr(registerMemory); if (this->polling[irq]) { uint32_t isr, mask = 1 << irq; diff --git a/fpga/lib/ips/pcie.cpp b/fpga/lib/ips/pcie.cpp new file mode 100644 index 000000000..df45d68cb --- /dev/null +++ b/fpga/lib/ips/pcie.cpp @@ -0,0 +1,53 @@ +/** AXI PCIe bridge + * + * @author Daniel Krebs + * @copyright 2018, RWTH Institute for Automation of Complex Power Systems (ACS) + * @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 . + *********************************************************************************/ + +#include +#include + +#include "fpga/ips/pcie.hpp" +#include "fpga/card.hpp" + +#include "log.hpp" +#include "memory_manager.hpp" + +namespace villas { +namespace fpga { +namespace ip { + +static AxiPciExpressBridgeFactory factory; + +bool +AxiPciExpressBridge::init() +{ + // Create an identity mapping from the FPGA card to this IP as an entry + // point to all other IPs in the FPGA, because Vivado will generate a + // memory view for this bridge that can see all others. + auto addrSpace = MemoryManager::get().findAddressSpace(getInstanceName()); + MemoryManager::get().createMapping(0x00, 0x00, SIZE_MAX, "PCIeBridge", + card->addrSpaceId, addrSpace); + + return true; +} + +} // namespace ip +} // namespace fpga +} // namespace villas diff --git a/fpga/lib/ips/switch.cpp b/fpga/lib/ips/switch.cpp index 98c6534e2..d9055c2f8 100644 --- a/fpga/lib/ips/switch.cpp +++ b/fpga/lib/ips/switch.cpp @@ -37,13 +37,16 @@ static AxiStreamSwitchFactory factory; bool AxiStreamSwitch::init() -{ +{ + auto logger = getLogger(); + /* Setup AXI-stream switch */ XAxis_Switch_Config sw_cfg; sw_cfg.MaxNumMI = num_ports; sw_cfg.MaxNumSI = num_ports; - if(XAxisScr_CfgInitialize(&xSwitch, &sw_cfg, getBaseaddr()) != XST_SUCCESS) { + if(XAxisScr_CfgInitialize(&xSwitch, &sw_cfg, getBaseAddr(registerMemory)) != XST_SUCCESS) { + logger->error("Cannot initialize switch"); return false; } @@ -57,7 +60,6 @@ AxiStreamSwitch::init() portMapping[portMaster] = PORT_DISABLED; } - return true; } diff --git a/fpga/lib/ips/timer.cpp b/fpga/lib/ips/timer.cpp index a6e19e257..1c7a023fe 100644 --- a/fpga/lib/ips/timer.cpp +++ b/fpga/lib/ips/timer.cpp @@ -44,28 +44,23 @@ bool Timer::init() XTmrCtr_Config xtmr_cfg; xtmr_cfg.SysClockFreqHz = getFrequency(); - XTmrCtr_CfgInitialize(&xTmr, &xtmr_cfg, getBaseaddr()); + XTmrCtr_CfgInitialize(&xTmr, &xtmr_cfg, getBaseAddr(registerMemory)); XTmrCtr_InitHw(&xTmr); - if(dependencies.find("intc") == dependencies.end()) { - logger->error("No intc"); + if(irqs.find(irqName) == irqs.end()) { + logger->error("IRQ '{}' not found but required", irqName); return false; } - if(irqs.find("generateout0") == irqs.end()) { - logger->error("no irq"); - return false; - } - - intc = reinterpret_cast(dependencies["intc"]); - intc->disableInterrupt(irqs["generateout0"]); + // disable so we don't receive any stray interrupts + irqs[irqName].irqController->disableInterrupt(irqs[irqName]); return true; } bool Timer::start(uint32_t ticks) { - intc->enableInterrupt(irqs["generateout0"], false); + irqs[irqName].irqController->enableInterrupt(irqs[irqName], false); XTmrCtr_SetOptions(&xTmr, 0, XTC_EXT_COMPARE_OPTION | XTC_DOWN_COUNT_OPTION); XTmrCtr_SetResetValue(&xTmr, 0, ticks); @@ -76,8 +71,8 @@ bool Timer::start(uint32_t ticks) bool Timer::wait() { - int count = intc->waitForInterrupt(irqs["generateout0"]); - intc->disableInterrupt(irqs["generateout0"]); + int count = irqs[irqName].irqController->waitForInterrupt(irqs[irqName]); + irqs[irqName].irqController->disableInterrupt(irqs[irqName]); return (count == 1); } From 21333379a9a672a61ba7b8dcac2ae3a28ab06770 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 14:58:58 +0100 Subject: [PATCH 15/21] lib/ips/fifo: fix decision if AXI4 data interface is present --- fpga/lib/ips/fifo.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fpga/lib/ips/fifo.cpp b/fpga/lib/ips/fifo.cpp index 23766a7d0..d608b9958 100644 --- a/fpga/lib/ips/fifo.cpp +++ b/fpga/lib/ips/fifo.cpp @@ -60,10 +60,13 @@ bool Fifo::init() XLlFifo_Config fifo_cfg; - fifo_cfg.Axi4BaseAddress = getBaseAddr(axi4Memory); - - // use AXI4 for Data, AXI4-Lite for control - fifo_cfg.Datainterface = (fifo_cfg.Axi4BaseAddress != -1) ? 1 : 0; + try { + // if this throws an exception, then there's no AXI4 data interface + fifo_cfg.Axi4BaseAddress = getBaseAddr(axi4Memory); + fifo_cfg.Datainterface = 1; + } catch(const std::out_of_range&) { + fifo_cfg.Datainterface = 0; + } if (XLlFifo_CfgInitialize(&xFifo, &fifo_cfg, getBaseAddr(registerMemory)) != XST_SUCCESS) return false; From e66350dbf68ac95821e9138e8ee4d7c97713bfc4 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 15:39:03 +0100 Subject: [PATCH 16/21] tests: minor fixes in logging --- fpga/tests/fpga.cpp | 3 +++ fpga/tests/graph.cpp | 9 +++++---- fpga/tests/main.cpp | 5 +++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/fpga/tests/fpga.cpp b/fpga/tests/fpga.cpp index b5147f3b6..1f718ff51 100644 --- a/fpga/tests/fpga.cpp +++ b/fpga/tests/fpga.cpp @@ -51,6 +51,9 @@ static void init() FILE *f; json_error_t err; + spdlog::set_level(spdlog::level::debug); + spdlog::set_pattern("[%T] [%l] [%n] %v"); + villas::Plugin::dumpList(); ret = pci_init(&pci); diff --git a/fpga/tests/graph.cpp b/fpga/tests/graph.cpp index ed14ee33e..354b10d3e 100644 --- a/fpga/tests/graph.cpp +++ b/fpga/tests/graph.cpp @@ -12,16 +12,17 @@ static void init_graph() } TestSuite(graph, - .description = "Graph library" + .description = "Graph library", + .init = init_graph ); Test(graph, basic, .description = "DirectedGraph") { auto logger = loggerGetOrCreate("test:graph:basic"); - logger->info("Testing basic graph construction and modification"); - villas::graph::DirectedGraph<> g("test:graph:basic"); + logger->info("Testing basic graph construction and modification"); + std::shared_ptr v1(new villas::graph::Vertex); std::shared_ptr v2(new villas::graph::Vertex); std::shared_ptr v3(new villas::graph::Vertex); @@ -116,7 +117,7 @@ Test(graph, path, .description = "Find path") Test(graph, memory_manager, .description = "Global Memory Manager") { - auto logger = loggerGetOrCreate("unittest:mm"); + auto logger = loggerGetOrCreate("test:graph:mm"); auto& mm = villas::MemoryManager::get(); logger->info("Create address spaces"); diff --git a/fpga/tests/main.cpp b/fpga/tests/main.cpp index beb56f6b9..c57333cd3 100644 --- a/fpga/tests/main.cpp +++ b/fpga/tests/main.cpp @@ -63,9 +63,10 @@ ReportHook(PRE_ALL)(struct criterion_test_set *tests) int main(int argc, char *argv[]) { int ret; - + spdlog::set_level(spdlog::level::debug); - + spdlog::set_pattern("[%T] [%l] [%n] %v"); + /* Run criterion tests */ auto tests = criterion_initialize(); From 503d6b7f075f1e9685de84749c737c1a3fe81a56 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 16:38:51 +0100 Subject: [PATCH 17/21] lib/ip: cleanup formatting and comments of IpCore's member variables --- fpga/include/villas/fpga/ip.hpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/fpga/include/villas/fpga/ip.hpp b/fpga/include/villas/fpga/ip.hpp index fc3a777e0..aff0ed891 100644 --- a/fpga/include/villas/fpga/ip.hpp +++ b/fpga/include/villas/fpga/ip.hpp @@ -150,14 +150,20 @@ protected: InterruptController* getInterruptController(const std::string& interruptName); -protected: - virtual std::list getMemoryBlocks() const { return {}; } + /// Each IP can declare via this function which memory blocks it requires + virtual std::list + getMemoryBlocks() const + { return {}; } - // populated by FpgaIpFactory - PCIeCard* card; ///< FPGA card this IP is instantiated on - IpIdentifier id; ///< VLNV and name defined in JSON config - std::map irqs; ///< Interrupts of this IP component - std::map dependencies; ///< dependencies on other IPs +protected: + /// FPGA card this IP is instantiated on (populated by FpgaIpFactory) + PCIeCard* card; + + /// Identifier of this IP with its instance name and VLNV + IpIdentifier id; + + /// All interrupts of this IP with their associated interrupt controller + std::map irqs; /// Cached translations from the process address space to each memory block std::map addressTranslations; From 41e90bfda0ae0be40b9afcd9e66bcfb1fe567768 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 17:26:18 +0100 Subject: [PATCH 18/21] lib/ip: cleanup operators --- fpga/include/villas/fpga/ip.hpp | 63 ++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/fpga/include/villas/fpga/ip.hpp b/fpga/include/villas/fpga/ip.hpp index aff0ed891..8c23b38e6 100644 --- a/fpga/include/villas/fpga/ip.hpp +++ b/fpga/include/villas/fpga/ip.hpp @@ -79,6 +79,21 @@ public: operator<< (std::ostream& stream, const IpIdentifier& id) { return stream << TXT_BOLD(id.name) << " vlnv=" << id.vlnv; } + bool + operator==(const IpIdentifier& otherId) const { + const bool vlnvWildcard = otherId.getVlnv() == Vlnv::getWildcard(); + const bool nameWildcard = this->getName().empty() or otherId.getName().empty(); + + const bool vlnvMatch = vlnvWildcard or this->getVlnv() == otherId.getVlnv(); + const bool nameMatch = nameWildcard or this->getName() == otherId.getName(); + + return vlnvMatch and nameMatch; + } + + bool + operator!=(const IpIdentifier& otherId) const + { return !(*this == otherId); } + private: Vlnv vlnv; std::string name; @@ -86,10 +101,9 @@ private: class IpCore { -public: - friend IpCoreFactory; +public: IpCore() : card(nullptr) {} virtual ~IpCore() {} @@ -101,37 +115,46 @@ public: virtual void dump(); bool - operator== (const IpIdentifier& otherId) { - const bool vlnvMatch = id.getVlnv() == otherId.getVlnv(); - const bool nameWildcard = id.getName().empty() or otherId.getName().empty(); - - return vlnvMatch and (nameWildcard or id.getName() == otherId.getName()); - } - - bool - operator!= (const IpIdentifier& otherId) { - return !(*this == otherId); - } - - bool - operator== (const Vlnv& otherVlnv) + operator==(const Vlnv& otherVlnv) const { return id.getVlnv() == otherVlnv; } bool - operator== (const std::string& otherName) - { return id.getName() == otherName; } + operator!=(const Vlnv& otherVlnv) const + { return id.getVlnv() != otherVlnv; } + bool + operator==(const IpIdentifier& otherId) const + { return this->id == otherId; } + + bool + operator!=(const IpIdentifier& otherId) const + { return this->id != otherId; } + + bool + operator==(const std::string& otherName) const + { return getInstanceName() == otherName; } + + bool + operator!=(const std::string& otherName) const + { return getInstanceName() != otherName; } + + bool + operator==(const IpCore& otherIp) const + { return this->id == otherIp.id; } + + bool + operator!=(const IpCore& otherIp) const + { return this->id != otherIp.id; } friend std::ostream& operator<< (std::ostream& stream, const IpCore& ip) { return stream << ip.id; } const std::string& - getInstanceName() + getInstanceName() const { return id.getName(); } protected: - uintptr_t getBaseAddr(const std::string& block) const; From 817d20624313a5bd81d50e58881b9f07b37db122 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 17:40:40 +0100 Subject: [PATCH 19/21] lib/ip: formatting cleanup and more comments --- fpga/include/villas/fpga/ip.hpp | 55 +++++++++++++++++++++------------ fpga/lib/ip.cpp | 2 +- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/fpga/include/villas/fpga/ip.hpp b/fpga/include/villas/fpga/ip.hpp index 8c23b38e6..b708ed1e4 100644 --- a/fpga/include/villas/fpga/ip.hpp +++ b/fpga/include/villas/fpga/ip.hpp @@ -105,15 +105,40 @@ class IpCore { public: IpCore() : card(nullptr) {} - virtual ~IpCore() {} + virtual ~IpCore() = default; - // IPs can implement this interface +public: + /* Generic management interface for IPs */ + + /// Runtime setup of IP, should access and initialize hardware + virtual bool init() + { return true; } + + /// Runtime check of IP, should verify basic functionality virtual bool check() { return true; } - virtual bool init() { return true; } + + /// Generic disabling of IP, meaning may depend on IP virtual bool stop() { return true; } + + /// Reset the IP, it should behave like freshly initialized afterwards virtual bool reset() { return true; } + + /// Print some debug information about the IP virtual void dump(); +protected: + /// Each IP can declare via this function which memory blocks it requires + virtual std::list + getMemoryBlocks() const + { return {}; } + +public: + const std::string& + getInstanceName() const + { return id.getName(); } + + /* Operators */ + bool operator==(const Vlnv& otherVlnv) const { return id.getVlnv() == otherVlnv; } @@ -150,10 +175,6 @@ public: operator<< (std::ostream& stream, const IpCore& ip) { return stream << ip.id; } - const std::string& - getInstanceName() const - { return id.getName(); } - protected: uintptr_t getBaseAddr(const std::string& block) const; @@ -162,23 +183,19 @@ protected: getLocalAddr(const std::string& block, uintptr_t address) const; SpdLogger - getLogger() { return loggerGetOrCreate(id.getName()); } + getLogger() const + { return loggerGetOrCreate(getInstanceName()); } + InterruptController* + getInterruptController(const std::string& interruptName) const; + +protected: struct IrqPort { int num; InterruptController* irqController; std::string description; }; - InterruptController* - getInterruptController(const std::string& interruptName); - - /// Each IP can declare via this function which memory blocks it requires - virtual std::list - getMemoryBlocks() const - { return {}; } - -protected: /// FPGA card this IP is instantiated on (populated by FpgaIpFactory) PCIeCard* card; @@ -194,7 +211,6 @@ protected: - class IpCoreFactory : public Plugin { public: IpCoreFactory(std::string concreteName) : @@ -207,7 +223,8 @@ public: protected: SpdLogger - getLogger() { return loggerGetOrCreate(getName()); } + getLogger() const + { return loggerGetOrCreate(getName()); } private: /// Create a concrete IP instance diff --git a/fpga/lib/ip.cpp b/fpga/lib/ip.cpp index 39e3c4805..7b93320a8 100644 --- a/fpga/lib/ip.cpp +++ b/fpga/lib/ip.cpp @@ -346,7 +346,7 @@ IpCore::getLocalAddr(const std::string& block, uintptr_t address) const InterruptController* -IpCore::getInterruptController(const std::string& interruptName) +IpCore::getInterruptController(const std::string& interruptName) const { try { const IrqPort irq = irqs.at(interruptName); From 3a8e332b426d2c9bea091f1eb993e6c2cbb5dddd Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 17:42:01 +0100 Subject: [PATCH 20/21] lib/vlnv: add != operator and minor cleanup --- fpga/include/villas/fpga/vlnv.hpp | 17 +++++------------ fpga/lib/vlnv.cpp | 12 ++++++++++++ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/fpga/include/villas/fpga/vlnv.hpp b/fpga/include/villas/fpga/vlnv.hpp index 52066a8d7..cce08f654 100644 --- a/fpga/include/villas/fpga/vlnv.hpp +++ b/fpga/include/villas/fpga/vlnv.hpp @@ -34,10 +34,8 @@ namespace villas { namespace fpga { - class Vlnv { public: - static constexpr char delimiter = ':'; Vlnv() : @@ -52,20 +50,15 @@ public: { return Vlnv(); } std::string - toString() const - { - std::stringstream stream; - std::string string; - - stream << *this; - stream >> string; - - return string; - } + toString() const; bool operator==(const Vlnv& other) const; + bool + operator!=(const Vlnv& other) const + { return !(*this == other); } + friend std::ostream& operator<< (std::ostream& stream, const Vlnv& vlnv) { diff --git a/fpga/lib/vlnv.cpp b/fpga/lib/vlnv.cpp index f9eaa6bb1..5d65d1329 100644 --- a/fpga/lib/vlnv.cpp +++ b/fpga/lib/vlnv.cpp @@ -66,5 +66,17 @@ Vlnv::parseFromString(std::string vlnv) if(version == "*") version = ""; } +std::string +Vlnv::toString() const +{ + std::stringstream stream; + std::string string; + + stream << *this; + stream >> string; + + return string; +} + } // namespace fpga } // namespace villas From 5940dcc0e5d397d98b37339a399712c932511d16 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 17:55:25 +0100 Subject: [PATCH 21/21] lib/ips/fifo: remove useless code and old cruft --- fpga/include/villas/fpga/ips/fifo.hpp | 6 +-- fpga/lib/ips/fifo.cpp | 77 ++------------------------- 2 files changed, 5 insertions(+), 78 deletions(-) diff --git a/fpga/include/villas/fpga/ips/fifo.hpp b/fpga/include/villas/fpga/ips/fifo.hpp index 001fbf19b..9d8528237 100644 --- a/fpga/include/villas/fpga/ips/fifo.hpp +++ b/fpga/include/villas/fpga/ips/fifo.hpp @@ -64,11 +64,7 @@ private: class FifoFactory : public IpNodeFactory { public: - FifoFactory() : - IpNodeFactory(getName()) - {} - - bool configureJson(IpCore& ip, json_t *json_ip); + FifoFactory(); IpCore* create() { return new Fifo; } diff --git a/fpga/lib/ips/fifo.cpp b/fpga/lib/ips/fifo.cpp index d608b9958..79e770b9c 100644 --- a/fpga/lib/ips/fifo.cpp +++ b/fpga/lib/ips/fifo.cpp @@ -40,17 +40,11 @@ namespace ip { // instantiate factory to make available to plugin infrastructure static FifoFactory factory; -bool -FifoFactory::configureJson(IpCore &ip, json_t *json_ip) + +FifoFactory::FifoFactory() : + IpNodeFactory(getName()) { - auto logger = getLogger(); - - if(not IpNodeFactory::configureJson(ip, json_ip)) { - logger->error("Configuring IpNode failed"); - return false; - } - - return true; + // nothing to do } @@ -128,69 +122,6 @@ size_t Fifo::read(void *buf, size_t len) return nextlen; } -#if 0 - - -ssize_t fifo_write(struct fpga_ip *c, char *buf, size_t len) -{ - struct fifo *fifo = (struct fifo *) c->_vd; - - XLlFifo *xllfifo = &fifo->inst; - -} - -ssize_t fifo_read(struct fpga_ip *c, char *buf, size_t len) -{ - struct fifo *fifo = (struct fifo *) c->_vd; - - XLlFifo *xllfifo = &fifo->inst; - - size_t nextlen = 0; - uint32_t rxlen; - - while (!XLlFifo_IsRxDone(xllfifo)) - intc_wait(c->card->intc, c->irq); - XLlFifo_IntClear(xllfifo, XLLF_INT_RC_MASK); - - /* Get length of next frame */ - rxlen = XLlFifo_RxGetLen(xllfifo); - nextlen = MIN(rxlen, len); - - /* Read from FIFO */ - XLlFifo_Read(xllfifo, buf, nextlen); - - return nextlen; -} - -int fifo_parse(struct fpga_ip *c, json_t *cfg) -{ - struct fifo *fifo = (struct fifo *) c->_vd; - - int baseaddr_axi4 = -1, ret; - - json_error_t err; - - fifo->baseaddr_axi4 = -1; - - ret = json_unpack_ex(cfg, &err, 0, "{ s?: i }", "baseaddr_axi4", &baseaddr_axi4); - if (ret) - jerror(&err, "Failed to parse configuration of FPGA IP '%s'", c->name); - - fifo->baseaddr_axi4 = baseaddr_axi4; - - return 0; -} - -int fifo_reset(struct fpga_ip *c) -{ - struct fifo *fifo = (struct fifo *) c->_vd; - - XLlFifo_Reset(&fifo->inst); - - return 0; -} -#endif - } // namespace ip } // namespace fpga } // namespace villas