1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00

first port to C++ of plugin and fpga ip infrastructure

This commit is contained in:
daniel-k 2017-12-05 17:49:27 +01:00
parent f0c089f719
commit 5d4040aded
9 changed files with 731 additions and 68 deletions

View file

@ -0,0 +1,128 @@
/** Interlectual Property component.
*
* This class represents a module within the FPGA.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @author Daniel Krebs <github@daniel-krebs.net>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
/** @addtogroup fpga VILLASfpga
* @{
*/
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include "common.h"
#include "log.h"
#include "utils.h"
#include "fpga/vlnv.hpp"
#include "plugin.hpp"
#include "card.h"
#include <jansson.h>
#include <vector>
namespace villas {
//enum fpga_ip_types {
// FPGA_IP_TYPE_DM_DMA, /**< A datamover IP exchanges streaming data between the FPGA and the CPU. */
// FPGA_IP_TYPE_DM_FIFO, /**< A datamover IP exchanges streaming data between the FPGA and the CPU. */
// FPGA_IP_TYPE_MODEL, /**< A model IP simulates a system on the FPGA. */
// FPGA_IP_TYPE_MATH, /**< A math IP performs some kind of mathematical operation on the streaming data */
// FPGA_IP_TYPE_MISC, /**< Other IP components like timer, counters, interrupt conctrollers or routing. */
// FPGA_IP_TYPE_INTERFACE /**< A interface IP connects the FPGA to another system or controller. */
//} type;
class FpgaIpFactory;
class FpgaIp {
public:
friend FpgaIpFactory;
FpgaIp() : card(nullptr), baseaddr(0), irq(-1), port(-1) {}
virtual ~FpgaIp() {}
// IPs can implement this interface
virtual bool check() { return true; }
virtual bool start() { return true; }
virtual bool stop() { return true; }
virtual bool reset() { return true; }
virtual void dump()
{
info("IP %s: vlnv=%s baseaddr=%#jx, irq=%d, port=%d",
name.c_str(), vlnv.toString().c_str(), baseaddr, irq, port);
}
protected:
uintptr_t
getBaseaddr() const
{
assert(card != nullptr);
return reinterpret_cast<uintptr_t>(card->map) + this->baseaddr;
}
protected:
// populated by FpgaIpFactory
struct fpga_card *card; /**< FPGA card this IP is instantiated on */
std::string name; /**< Name defined in JSON config */
FpgaVlnv vlnv; /**< VLNV defined in JSON config */
uintptr_t baseaddr; /**< The baseadress of this FPGA IP component */
int irq; /**< The interrupt number of the FPGA IP component. */
int port; /**< The port of the AXI4-Stream switch to which this FPGA IP component is connected. */
};
class FpgaIpFactory : public Plugin {
public:
FpgaIpFactory()
{ pluginType = Plugin::Type::FpgaIp; }
/// Returns a running and checked FPGA IP
static FpgaIp* make(struct fpga_card* card, json_t *json, std::string name);
private:
/// Create a concrete IP instance
virtual FpgaIp* create() = 0;
/// Configure IP instance from JSON config
virtual bool configureJson(FpgaIp* ip, json_t *json) = 0;
virtual FpgaVlnv getCompatibleVlnv() const = 0;
virtual std::string getName() const = 0;
virtual std::string getDescription() const = 0;
private:
static FpgaIpFactory*
lookup(const FpgaVlnv& vlnv);
};
/** @} */
} // namespace villas

View file

@ -0,0 +1,88 @@
/** AXI-PCIe Interrupt controller
*
* @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 "fpga/ip.hpp"
#include <xilinx/xintc.h>
namespace villas {
class InterruptController : public FpgaIp
{
public:
using IrqMaskType = uint32_t;
static constexpr int maxIrqs = 32;
~InterruptController();
bool start();
int enableInterrupt(IrqMaskType mask, bool polling);
int disableInterrupt(IrqMaskType mask);
uint64_t waitForInterrupt(int irq);
private:
struct Interrupt {
int eventFd; /**< Event file descriptor */
int number; /**< Interrupt number from /proc/interrupts */
bool polling; /**< Polled or not */
};
int num_irqs; /**< Number of available MSI vectors */
int efds[maxIrqs];
int nos[maxIrqs];
bool polling[maxIrqs];
// Interrupt irqs[maxIrqs]; /**< State of available interrupts */
};
class InterruptControllerFactory : public FpgaIpFactory {
public:
FpgaIp* create()
{ return new InterruptController; }
std::string
getName() const
{ return "InterruptController"; }
std::string
getDescription() const
{ return "Xilinx's programmable interrupt controller"; }
FpgaVlnv getCompatibleVlnv() const
{ return FpgaVlnv("acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:"); }
bool configureJson(FpgaIp* ip, json_t *json);
};
} // namespace villas
/** @} */

View file

@ -0,0 +1,86 @@
/** Vendor, Library, Name, Version (VLNV) tag.
*
* @file
* @author Daniel Krebs <github@daniel-krebs.net>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
/** @addtogroup fpga VILLASfpga
* @{
*/
#pragma once
#include <string>
#include <sstream>
#include <iostream>
namespace villas {
class FpgaVlnv {
public:
static constexpr char delimiter = ':';
static constexpr char wildcard[] = "*";
FpgaVlnv() :
vendor(""), library(""), name(""), version("") {}
FpgaVlnv(std::string s) {
parseFromString(s);
}
std::string
toString() const
{
std::stringstream stream;
std::string string;
stream << *this;
stream >> string;
return string;
}
bool
operator==(const FpgaVlnv& other) const;
friend std::ostream&
operator<< (std::ostream& stream, const FpgaVlnv& vlnv)
{
return stream
<< (vlnv.vendor.empty() ? "*" : vlnv.vendor) << ":"
<< (vlnv.library.empty() ? "*" : vlnv.library) << ":"
<< (vlnv.name.empty() ? "*" : vlnv.name) << ":"
<< (vlnv.version.empty() ? "*" : vlnv.version);
}
private:
void
parseFromString(std::string vlnv);
std::string vendor;
std::string library;
std::string name;
std::string version;
};
} // namespace villas
/** _FPGA_VLNV_HPP_ @} */

View file

@ -0,0 +1,81 @@
/** Loadable / plugin support.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @author Daniel Krebs <github@daniel-krebs.net>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#include <list>
#include <string>
#include <jansson.h>
#include "utils.h"
namespace villas {
class Plugin {
public:
enum class Type {
Unknown,
FpgaIp,
};
Plugin();
virtual ~Plugin();
// each plugin is a singleton, so copying is not allowed
Plugin(Plugin const&) = delete;
void operator=(Plugin const&) = delete;
int load();
int unload();
virtual int parse(json_t *cfg);
virtual void dump();
/** Find registered and loaded plugin with given name and type. */
static Plugin *
lookup(Type type, std::string name);
static std::list<Plugin*>
lookup(Type type);
// check if this makes sense! (no intermediate plugins)
bool
operator==(const Plugin& other) const;
Type pluginType;
std::string name;
std::string description;
std::string path;
void *handle;
enum state state;
private:
using PluginList = std::list<Plugin *>;
static std::list<Plugin *> pluginList;
};
} // namespace villas

View file

@ -1,6 +1,6 @@
set(SOURCES
ip.c
vlnv.c
ip.cpp
vlnv.cpp
card.c
ips/timer.c
@ -17,7 +17,7 @@ set(SOURCES
kernel/pci.c
kernel/vfio.c
plugin.c
plugin.cpp
utils.c
list.c
log.c

95
fpga/lib/ip.cpp Normal file
View file

@ -0,0 +1,95 @@
/** FPGA IP component.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include "log_config.h"
#include "log.h"
#include "plugin.h"
#include "fpga/ip.hpp"
#include <algorithm>
namespace villas {
FpgaIpFactory* FpgaIpFactory::lookup(const FpgaVlnv &vlnv)
{
for(auto& ip : Plugin::lookup(Plugin::Type::FpgaIp)) {
FpgaIpFactory* fpgaIpFactory = dynamic_cast<FpgaIpFactory *>(ip);
if(fpgaIpFactory->getCompatibleVlnv() == vlnv)
return fpgaIpFactory;
}
return nullptr;
}
FpgaIp *FpgaIpFactory::make(fpga_card *card, json_t *json, std::string name)
{
// extract VLNV from JSON
const char* vlnv_raw;
if(json_unpack(json, "{ s: s }", "vlnv", &vlnv_raw) != 0)
error("IP '%s' has no entry 'vlnv'", name.c_str());
// 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.
FpgaVlnv vlnv(vlnv_raw);
FpgaIpFactory* fpgaIpFactory = lookup(vlnv);
if(fpgaIpFactory == nullptr) {
error("No ip factory registered to handle VLNV '%s'", vlnv.toString().c_str());
} else {
info("Using %s for IP %s", fpgaIpFactory->getName().c_str(), vlnv.toString().c_str());
}
// create new IP instance
FpgaIp* ip = fpgaIpFactory->create();
// setup generic IP type properties
ip->card = card;
ip->name = name;
ip->vlnv = vlnv;
// extract some optional properties
int ret = json_unpack(json, "{ s?: i, s?: i, s?: i }",
"baseaddr", &ip->baseaddr,
"irq", &ip->irq,
"port", &ip->port);
if(ret != 0)
error("Problem while parsing JSON");
// IP-specific setup via JSON config
fpgaIpFactory->configureJson(ip, json);
if(not ip->start())
error("Cannot start IP");
if(not ip->check())
error("Checking IP failed");
return ip;
}
} // namespace villas

View file

@ -24,40 +24,37 @@
#include "config.h"
#include "log.h"
#include "plugin.h"
#include "plugin.hpp"
#include "kernel/vfio.h"
#include "kernel/kernel.h"
#include "fpga/ip.h"
#include "fpga/card.h"
#include "fpga/ips/intc.h"
#include "fpga/ips/intc.hpp"
int intc_start(struct fpga_ip *c)
namespace villas {
InterruptController::~InterruptController()
{
int ret;
vfio_pci_msi_deinit(&card->vfio_device , this->efds);
}
struct fpga_card *f = c->card;
struct intc *intc = (struct intc *) c->_vd;
bool InterruptController::start()
{
const uintptr_t base = getBaseaddr();
uintptr_t base = (uintptr_t) f->map + c->baseaddr;
num_irqs = vfio_pci_msi_init(&card->vfio_device, efds);
if (num_irqs < 0)
return false;
if (c != f->intc)
error("There can be only one interrupt controller per FPGA");
intc->num_irqs = vfio_pci_msi_init(&f->vfio_device, intc->efds);
if (intc->num_irqs < 0)
return -1;
ret = vfio_pci_msi_find(&f->vfio_device, intc->nos);
if (ret)
return -2;
if(vfio_pci_msi_find(&card->vfio_device, nos) != 0)
return false;
/* For each IRQ */
for (int i = 0; i < intc->num_irqs; i++) {
for (int i = 0; i < num_irqs; i++) {
/* Pin to core */
ret = kernel_irq_setaffinity(intc->nos[i], f->affinity, NULL);
if (ret)
if(kernel_irq_setaffinity(nos[i], card->affinity, NULL) !=0)
serror("Failed to change affinity of VFIO-MSI interrupt");
/* Setup vector */
@ -72,26 +69,14 @@ int intc_start(struct fpga_ip *c)
debug(4, "FPGA: enabled interrupts");
return 0;
return true;
}
int intc_destroy(struct fpga_ip *c)
int
InterruptController::enableInterrupt(InterruptController::IrqMaskType mask, bool polling)
{
struct fpga_card *f = c->card;
struct intc *intc = (struct intc *) c->_vd;
vfio_pci_msi_deinit(&f->vfio_device, intc->efds);
return 0;
}
int intc_enable(struct fpga_ip *c, uint32_t mask, int flags)
{
struct fpga_card *f = c->card;
struct intc *intc = (struct intc *) c->_vd;
uint32_t ier, imr;
uintptr_t base = (uintptr_t) f->map + c->baseaddr;
const uintptr_t base = getBaseaddr();
/* Current state of INTC */
ier = XIntc_In32(base + XIN_IER_OFFSET);
@ -100,12 +85,12 @@ int intc_enable(struct fpga_ip *c, uint32_t mask, int flags)
/* Clear pending IRQs */
XIntc_Out32(base + XIN_IAR_OFFSET, mask);
for (int i = 0; i < intc->num_irqs; i++) {
for (int i = 0; i < num_irqs; i++) {
if (mask & (1 << i))
intc->flags[i] = flags;
this->polling[i] = polling;
}
if (flags & INTC_POLLING) {
if (polling) {
XIntc_Out32(base + XIN_IMR_OFFSET, imr & ~mask);
XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask);
}
@ -118,31 +103,29 @@ int intc_enable(struct fpga_ip *c, uint32_t mask, int flags)
debug(3, "New imr = %#x", XIntc_In32(base + XIN_IMR_OFFSET));
debug(3, "New isr = %#x", XIntc_In32(base + XIN_ISR_OFFSET));
debug(8, "FPGA: Interupt enabled: mask=%#x flags=%#x", mask, flags);
debug(8, "FPGA: Interupts enabled: mask=%#x polling=%d", mask, polling);
return 0;
return true;
}
int intc_disable(struct fpga_ip *c, uint32_t mask)
int
InterruptController::disableInterrupt(InterruptController::IrqMaskType mask)
{
struct fpga_card *f = c->card;
uintptr_t base = (uintptr_t) f->map + c->baseaddr;
const uintptr_t base = getBaseaddr();
uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET);
XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask);
return 0;
return true;
}
uint64_t intc_wait(struct fpga_ip *c, int irq)
uint64_t InterruptController::waitForInterrupt(int irq)
{
struct fpga_card *f = c->card;
struct intc *intc = (struct intc *) c->_vd;
assert(irq < maxIrqs);
uintptr_t base = (uintptr_t) f->map + c->baseaddr;
const uintptr_t base = getBaseaddr();
if (intc->flags[irq] & INTC_POLLING) {
if (this->polling[irq]) {
uint32_t isr, mask = 1 << irq;
do {
@ -156,7 +139,7 @@ uint64_t intc_wait(struct fpga_ip *c, int irq)
}
else {
uint64_t cnt;
ssize_t ret = read(intc->efds[irq], &cnt, sizeof(cnt));
ssize_t ret = read(efds[irq], &cnt, sizeof(cnt));
if (ret != sizeof(cnt))
return 0;
@ -164,17 +147,11 @@ uint64_t intc_wait(struct fpga_ip *c, int irq)
}
}
static struct plugin p = {
.name = "Xilinx's programmable interrupt controller",
.description = "",
.type = PLUGIN_TYPE_FPGA_IP,
.ip = {
.vlnv = { "acs.eonerc.rwth-aachen.de", "user", "axi_pcie_intc", NULL },
.type = FPGA_IP_TYPE_MISC,
.start = intc_start,
.destroy = intc_destroy,
.size = sizeof(struct intc)
}
};
REGISTER_PLUGIN(&p)
bool InterruptControllerFactory::configureJson(FpgaIp *ip, json_t *json)
{
// parse json and configure instance here
return true;
}
} // namespace villas

139
fpga/lib/plugin.cpp Normal file
View file

@ -0,0 +1,139 @@
/** Loadable / plugin support.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <dlfcn.h>
#include <string.h>
#include <string>
#include <iostream>
#include <iostream>
#include "plugin.hpp"
namespace villas {
// list of all registered plugins
Plugin::PluginList Plugin::pluginList;
Plugin::Plugin() :
name(""),
description(""),
path(""),
pluginType(Plugin::Type::Unknown),
state(STATE_INITIALIZED)
{
// push to global plugin list
pluginList.push_back(this);
}
Plugin::~Plugin()
{
// clean from global plugin list
pluginList.remove(this);
}
int
Plugin::parse(json_t *cfg)
{
const char *path;
path = json_string_value(cfg);
if (!path)
return -1;
this->path = std::string(path);
this->state = STATE_PARSED;
return 0;
}
int
Plugin::load()
{
assert(this->state == STATE_PARSED);
assert(not this->path.empty());
this->handle = dlopen(this->path.c_str(), RTLD_NOW);
if (this->handle == nullptr)
return -1;
this->state = STATE_LOADED;
return 0;
}
int
Plugin::unload()
{
int ret;
assert(this->state == STATE_LOADED);
ret = dlclose(this->handle);
if (ret != 0)
return -1;
this->state = STATE_UNLOADED;
return 0;
}
void
Plugin::dump()
{
std::cout << " - " << this->name << ": " << this->description << std::endl;
}
Plugin*
Plugin::lookup(Plugin::Type type, std::string name)
{
for(auto& p : pluginList) {
if(p->pluginType == type and p->name == name)
return p;
}
return nullptr;
}
std::list<Plugin*>
Plugin::lookup(Plugin::Type type)
{
std::list<Plugin*> list;
for(auto& p : pluginList) {
if(p->pluginType == type)
list.push_back(p);
}
return list;
}
bool
Plugin::operator==(const Plugin &other) const
{
return (this->pluginType == other.pluginType) and (this->name == other.name);
}
} // namespace villas

69
fpga/lib/vlnv.cpp Normal file
View file

@ -0,0 +1,69 @@
/** Vendor, Library, Name, Version (VLNV) tag
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <string>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include "fpga/vlnv.hpp"
#include "fpga/ip.hpp"
namespace villas {
bool
FpgaVlnv::operator==(const FpgaVlnv &other) const
{
// if a field is empty, it means wildcard matching everything
const bool vendorWildcard = vendor.empty() or other.vendor.empty();
const bool libraryWildcard = library.empty() or other.library.empty();
const bool nameWildcard = name.empty() or other.name.empty();
const bool versionWildcard = version.empty() or other.version.empty();
const bool vendorMatch = vendorWildcard or vendor == other.vendor;
const bool libraryMatch = libraryWildcard or library == other.library;
const bool nameMatch = nameWildcard or name == other.name;
const bool versionMatch = versionWildcard or version == other.version;
return vendorMatch and libraryMatch and nameMatch and versionMatch;
}
void
FpgaVlnv::parseFromString(std::string vlnv)
{
// tokenize by delimiter
std::stringstream sstream(vlnv);
std::getline(sstream, vendor, delimiter);
std::getline(sstream, library, delimiter);
std::getline(sstream, name, delimiter);
std::getline(sstream, version, delimiter);
// represent wildcard internally as empty string
if(vendor == wildcard) vendor = "";
if(library == wildcard) library = "";
if(name == wildcard) name = "";
if(version == wildcard) version = "";
}
} // namespace villas