From 02e873e8ffdc02f1034133810ef11f8c3be659c8 Mon Sep 17 00:00:00 2001 From: Daniel Krebs Date: Tue, 13 Feb 2018 10:16:02 +0100 Subject: [PATCH] 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; }