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