mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
lib/ips: make use of MemoryManager and new config layout
This commit is contained in:
parent
acf273e406
commit
e93b31bbf1
13 changed files with 419 additions and 166 deletions
|
@ -41,14 +41,23 @@
|
|||
|
||||
#include <jansson.h>
|
||||
|
||||
#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<std::unique_ptr<IpCore>>;
|
||||
|
||||
|
||||
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<std::string, Vlnv>;
|
||||
|
||||
// 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<std::string> 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<std::string, IrqPort> irqs; ///< Interrupts of this IP component
|
||||
std::map<std::string, IpCore*> dependencies; ///< dependencies on other IPs
|
||||
|
||||
/// Cached translations from the process address space to each memory block
|
||||
std::map<std::string, MemoryTranslation> addressTranslations;
|
||||
};
|
||||
|
||||
|
||||
using IpCoreList = std::list<std::unique_ptr<IpCore>>;
|
||||
|
||||
|
||||
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<IpDependency> getDependencies() const { return {}; }
|
||||
|
||||
protected:
|
||||
static SpdLogger
|
||||
|
|
|
@ -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<std::string> 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<IpDependency> getDependencies() const
|
||||
{ return { {"intc", Vlnv("acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:") } }; }
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -61,6 +61,13 @@ public:
|
|||
{ return waitForInterrupt(irq.num); }
|
||||
|
||||
private:
|
||||
|
||||
static constexpr char registerMemory[] = "Reg";
|
||||
|
||||
std::list<std::string> 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<IpDependency> getDependencies() const
|
||||
// { return { {"pcie", Vlnv("xilinx.com:ip:axi_pcie:") } }; }
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
80
fpga/include/villas/fpga/ips/pcie.hpp
Normal file
80
fpga/include/villas/fpga/ips/pcie.hpp
Normal file
|
@ -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 <stvogel@eonerc.rwth-aachen.de>
|
||||
* @author Daniel Krebs <github@daniel-krebs.net>
|
||||
* @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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga VILLASfpga
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <jansson.h>
|
||||
#include <xilinx/xaxis_switch.h>
|
||||
|
||||
#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
|
||||
|
||||
/** @} */
|
|
@ -56,6 +56,11 @@ public:
|
|||
private:
|
||||
static constexpr int PORT_DISABLED = -1;
|
||||
|
||||
static constexpr char registerMemory[] = "Reg";
|
||||
|
||||
std::list<std::string> 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
|
||||
|
|
|
@ -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<std::string> 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<IpDependency> getDependencies() const
|
||||
{ return { {"intc", Vlnv("acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:") } }; }
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
|
|
|
@ -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)
|
||||
|
|
250
fpga/lib/ip.cpp
250
fpga/lib/ip.cpp
|
@ -20,30 +20,24 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#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 <algorithm>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#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<IpCoreFactory*>(ip);
|
||||
|
||||
if(ipCoreFactory->getCompatibleVlnv() == vlnv)
|
||||
return ipCoreFactory;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
IpCore::getAddrMapped(uintptr_t address) const
|
||||
{
|
||||
assert(card != nullptr);
|
||||
return reinterpret_cast<uintptr_t>(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<IpIdentifier> allIps; // all IPs available
|
||||
std::list<IpIdentifier> allIps; // all IPs available in config
|
||||
std::list<IpIdentifier> 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<InterruptController*>(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<IpCore>& 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<uintptr_t>(base),
|
||||
0,
|
||||
static_cast<uintptr_t>(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<IpCoreFactory*>(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
|
||||
|
|
|
@ -50,33 +50,32 @@ FifoFactory::configureJson(IpCore &ip, json_t *json_ip)
|
|||
return false;
|
||||
}
|
||||
|
||||
auto& fifo = reinterpret_cast<Fifo&>(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<size_t>(-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<InterruptController*>(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<InterruptController*>(dependencies["intc"]);
|
||||
|
||||
while (!XLlFifo_IsRxDone(&xFifo))
|
||||
intc->waitForInterrupt(irqs["interrupt"].num);
|
||||
irqs[irqName].irqController->waitForInterrupt(irqs[irqName]);
|
||||
|
||||
XLlFifo_IntClear(&xFifo, XLLF_INT_RC_MASK);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
53
fpga/lib/ips/pcie.cpp
Normal file
53
fpga/lib/ips/pcie.cpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
/** AXI PCIe bridge
|
||||
*
|
||||
* @author Daniel Krebs <github@daniel-krebs.net>
|
||||
* @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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <limits>
|
||||
#include <jansson.h>
|
||||
|
||||
#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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<InterruptController*>(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);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue