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

wip implementing dependency parsing and proper memeory handling

works and compiles so for. next is to implement different IP interfaces
(Model, Interface, DataMover, Infrastructure, ...)
This commit is contained in:
daniel-k 2017-12-19 19:06:30 +01:00
parent 6a721847e4
commit a5b5e317d4
11 changed files with 387 additions and 175 deletions

View file

@ -26,10 +26,64 @@
"slot": "03:00.0",
"do_reset": true,
"ips": {
"axi_reset_0": {
"vlnv": "xilinx.com:ip:axi_gpio:2.0",
"baseaddr": 28672
},
"timer_0": {
"vlnv": "xilinx.com:ip:axi_timer:2.0",
"baseaddr": 16384,
"irq": "axi_pcie_intc_0:0"
},
"dma_0": {
"vlnv": "xilinx.com:ip:axi_dma:7.1",
"baseaddr": 12288,
"port": "switch_0:1",
"irq": "axi_pcie_intc_0:3"
},
"axi_pcie_intc_0": {
"vlnv": "acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:1.0",
"baseaddr": 45056
},
"dma_1": {
"vlnv": "xilinx.com:ip:axi_dma:7.1",
"baseaddr": 8192,
"port": "switch_0:6",
"irq": "axi_pcie_intc_0:3"
},
"fifo_mm_s_0": {
"vlnv": "xilinx.com:ip:axi_fifo_mm_s:4.1",
"baseaddr": 24576,
"baseaddr_axi4": 49152,
"port": "switch_0:2",
"irq": "axi_pcie_intc_0:2"
},
"rtds_axis_0": {
"vlnv": "acs.eonerc.rwth-aachen.de:user:rtds_axis:1.0",
"baseaddr": 32768,
"port": "switch_0:0",
"irq": "axi_pcie_intc_0:5"
},
"hls_dft_0": {
"vlnv": "acs.eonerc.rwth-aachen.de:hls:hls_dft:1.0",
"baseaddr": 36864,
"port": "switch_0:5",
"irq": "axi_pcie_intc_0:1",
"period": 400,
"harmonics": [
0,
1,
3,
5,
7
],
"decimation": 0
},
"axis_data_fifo_0": {
"vlnv": "xilinx.com:ip:axis_data_fifo:1.1",
"port": "switch_0:3"
},
"switch_0": {
"vlnv": "xilinx.com:ip:axis_interconnect:2.1",
"baseaddr": 20480,
@ -42,62 +96,9 @@
}
]
},
"axi_reset_0": {
"vlnv": "xilinx.com:ip:axi_gpio:2.0",
"baseaddr": 28672
},
"timer_0": {
"vlnv": "xilinx.com:ip:axi_timer:2.0",
"baseaddr": 16384,
"irq": 0
},
"dma_0": {
"vlnv": "xilinx.com:ip:axi_dma:7.1",
"baseaddr": 12288,
"port": 1,
"irq": 3
},
"dma_1": {
"vlnv": "xilinx.com:ip:axi_dma:7.1",
"baseaddr": 8192,
"port": 6,
"irq": 3
},
"fifo_mm_s_0": {
"vlnv": "xilinx.com:ip:axi_fifo_mm_s:4.1",
"baseaddr": 24576,
"baseaddr_axi4": 49152,
"port": 2,
"irq": 2
},
"rtds_axis_0": {
"vlnv": "acs.eonerc.rwth-aachen.de:user:rtds_axis:1.0",
"baseaddr": 32768,
"port": 0,
"irq": 5
},
"hls_dft_0": {
"vlnv": "acs.eonerc.rwth-aachen.de:hls:hls_dft:1.0",
"baseaddr": 36864,
"port": 5,
"irq": 1,
"period": 400,
"harmonics": [
0,
1,
3,
5,
7
],
"decimation": 0
},
"axis_data_fifo_0": {
"vlnv": "xilinx.com:ip:axis_data_fifo:1.1",
"port": 3
},
"axis_data_fifo_1": {
"vlnv": "xilinx.com:ip:axis_data_fifo:1.1",
"port": 6
"port": "switch_0:6"
}
}
}

View file

@ -41,6 +41,7 @@
#include <string>
#include "plugin.hpp"
#include "fpga/ip.hpp"
#include "config.h"
@ -59,11 +60,6 @@ namespace fpga {
struct vfio_container;
class PCIeCardFactory;
namespace ip {
class IpCore;
}
class PCIeCard {
public:
@ -78,8 +74,7 @@ public:
void dump() { }
using IpList = std::list<ip::IpCore*>;
IpList ips; ///< IPs located on this FPGA card
ip::IpCoreList ips; ///< IPs located on this FPGA card
bool do_reset; /**< Reset VILLASfpga during startup? */
int affinity; /**< Affinity for MSI interrupts */
@ -99,7 +94,7 @@ public:
size_t dmalen;
};
using CardList = std::list<std::unique_ptr<PCIeCard>>;
class PCIeCardFactory : public Plugin {
public:
@ -108,7 +103,7 @@ public:
Plugin("FPGA Card plugin")
{ pluginType = Plugin::Type::FpgaCard; }
static std::list<PCIeCard*>
static CardList
make(json_t *json, struct pci* pci, ::vfio_container* vc);
static PCIeCard*

View file

@ -41,16 +41,41 @@
#include "fpga/vlnv.hpp"
#include "plugin.hpp"
#include "card.hpp"
#include <map>
#include <list>
#include <memory>
#include <jansson.h>
namespace villas {
namespace fpga {
class PCIeCard;
namespace ip {
class IpIdentifier {
public:
IpIdentifier(Vlnv vlnv = Vlnv::getWildcard(), std::string name = "") :
vlnv(vlnv), name(name) {}
IpIdentifier(std::string vlnvString, std::string name = "") :
vlnv(vlnvString), name(name) {}
friend std::ostream&
operator<< (std::ostream& stream, const IpIdentifier& id)
{ return stream << "VLNV: " << id.vlnv << " Name: " << id.name; }
Vlnv vlnv;
std::string name;
};
using IpDependency = std::pair<std::string, Vlnv>;
// forward declarations
class IpCoreFactory;
class IpCore {
@ -58,7 +83,7 @@ public:
friend IpCoreFactory;
IpCore() : card(nullptr), baseaddr(0), irq(-1), port(-1) {}
IpCore() : card(nullptr), baseaddr(0) {} //, irq(-1), port(-1) {}
virtual ~IpCore() {}
// IPs can implement this interface
@ -68,25 +93,41 @@ public:
virtual bool reset() { return true; }
virtual void dump();
bool
operator== (const IpIdentifier& otherId) {
const bool vlnvMatch = id.vlnv == otherId.vlnv;
const bool nameWildcard = id.name.empty() or otherId.name.empty();
return vlnvMatch and (nameWildcard or id.name == otherId.name);
}
bool
operator== (const Vlnv& otherVlnv)
{ return id.vlnv == otherVlnv; }
friend std::ostream&
operator<< (std::ostream& stream, const IpCore& ip)
{ return stream << ip.id; }
protected:
uintptr_t
getBaseaddr() const
{
assert(card != nullptr);
return reinterpret_cast<uintptr_t>(card->map) + this->baseaddr;
}
getBaseaddr() const;
protected:
// populated by FpgaIpFactory
PCIeCard* card; /**< FPGA card this IP is instantiated on */
std::string name; /**< Name defined in JSON config */
Vlnv vlnv; /**< VLNV defined in JSON config */
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 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. */
// 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. */
std::map<std::string, IpCore*> dependencies;
};
using IpCoreList = std::list<std::unique_ptr<IpCore>>;
class IpCoreFactory : public Plugin {
public:
IpCoreFactory(std::string concreteName) :
@ -94,20 +135,21 @@ public:
{ pluginType = Plugin::Type::FpgaIp; }
/// Returns a running and checked FPGA IP
static IpCore*
make(PCIeCard* card, json_t *json, std::string name);
static IpCoreList
make(PCIeCard* card, json_t *json_ips);
private:
/// Create a concrete IP instance
virtual IpCore* create() = 0;
/// Configure IP instance from JSON config
virtual bool configureJson(IpCore* ip, json_t *json)
virtual bool configureJson(const std::unique_ptr<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 = 0;
private:
static IpCoreFactory*

View file

@ -85,6 +85,9 @@ public:
Vlnv getCompatibleVlnv() const
{ return Vlnv("acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:"); }
std::list<IpDependency> getDependencies() const
{ return {}; }
};
} // namespace ip

View file

@ -47,6 +47,10 @@ public:
parseFromString(s);
}
static Vlnv
getWildcard()
{ return Vlnv(); }
std::string
toString() const
{

View file

@ -3,12 +3,23 @@
#include <iostream>
#include <string>
#define _ESCAPE "\x1b"
#define TXT_BOLD(s) _ESCAPE "[1m" + std::string(s) + _ESCAPE "[0m"
class LoggerIndent;
class Logger {
friend LoggerIndent;
public:
enum class LogLevel : int {
Debug,
Info,
Warning,
Error,
Disabled
};
class LoggerNewline {
public:
LoggerNewline(bool enabled = true) : enabled(enabled) {}
@ -36,7 +47,7 @@ public:
Logger* logger;
};
Logger(int level, std::string prefix = "") : level(level), prefix(prefix) {}
Logger(LogLevel level, std::string prefix = "") : level(level), prefix(prefix) {}
Indenter indent()
{ return Indenter(this); }
@ -80,11 +91,16 @@ public:
depthCurrent = --depth;
}
static
void
setLogLevel(LogLevel level)
{ global_level = level; }
private:
int level;
LogLevel level;
std::string prefix;
static int depth;
static int global_level;
static LogLevel global_level;
static int depthCurrent;
};

View file

@ -33,6 +33,12 @@
#include "kernel/vfio.h"
#include <string>
#include <map>
#include <algorithm>
#include <list>
#include <vector>
#include <memory>
#include <utility>
#include "fpga/ip.hpp"
#include "fpga/card.hpp"
@ -45,10 +51,11 @@ namespace fpga {
// instantiate factory to register
static PCIeCardFactory PCIeCardFactory;
std::list<fpga::PCIeCard*>
CardList
fpga::PCIeCardFactory::make(json_t *json, struct pci* pci, ::vfio_container* vc)
{
std::list<fpga::PCIeCard*> cards;
CardList cards;
const char *card_name;
json_t *json_card;
@ -74,7 +81,7 @@ fpga::PCIeCardFactory::make(json_t *json, struct pci* pci, ::vfio_container* vc)
continue;
}
fpga::PCIeCard* card = create();
auto card = std::unique_ptr<PCIeCard>(create());
// populate generic properties
card->name = std::string(card_name);
@ -87,44 +94,32 @@ fpga::PCIeCardFactory::make(json_t *json, struct pci* pci, ::vfio_container* vc)
if (pci_slot != nullptr and pci_device_parse_slot(&card->filter, pci_slot, &error) != 0) {
cpp_warn << "Failed to parse PCI slot: " << error;
// cpp_info << "... ignoring";
}
if (pci_id != nullptr and pci_device_parse_id(&card->filter, pci_id, &error) != 0) {
cpp_warn << "Failed to parse PCI ID: " << error;
// cpp_info << "ignoring ...";
}
// TODO: currently fails, fix and remove comment
// if(not card->start()) {
// cpp_warn << " cannot start, destroying ...";
// cpp_warn << "Cannot start FPGA card " << card_name;
// delete card;
// continue;
// }
const char *ip_name;
json_t *json_ip;
json_object_foreach(json_ips, ip_name, json_ip) {
cpp_info << "Found IP: " << ip_name;
Logger::Indenter indent = cpp_debug.indent();
ip::IpCore* ip = ip::IpCoreFactory::make(card, json_ip, ip_name);
if(ip == nullptr) {
cpp_warn << "Cannot initialize, ignoring ...";
continue;
}
card->ips.push_back(ip);
card->ips = ip::IpCoreFactory::make(card.get(), json_ips);
if(card->ips.empty()) {
cpp_error << "Cannot initialize IPs";
continue;
}
if(not card->check()) {
cpp_warn << "Checking failed, destroying ...";
delete card;
continue;
}
cards.push_back(card);
cards.push_back(std::move(card));
}
return cards;

View file

@ -21,24 +21,112 @@
*********************************************************************************/
#include "log_config.h"
#include "log.h"
#include "log.hpp"
#include "plugin.h"
#include "dependency_graph.hpp"
#include "utils.hpp"
#include "fpga/ip.hpp"
#include "fpga/card.hpp"
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <utility>
#include "log.hpp"
using DependencyGraph = villas::utils::DependencyGraph<std::string>;
static
std::list<std::string>
dependencyTokens = {"irq", "port", "memory"};
static
bool
buildDependencyGraph(DependencyGraph& dependencyGraph, json_t* json_ips, std::string name)
{
// cpp_debug << "preparse " << name;
const bool nodeExists = dependencyGraph.addNode(name);
// 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) {
cpp_error << "IP " << name << " not found in config";
return false;
}
for(auto& dependencyToken : dependencyTokens) {
json_t* json_dependency = json_object_get(json_ip, dependencyToken.c_str());
if(json_dependency == nullptr) {
cpp_debug << "Property " << dependencyToken << " of " << TXT_BOLD(name)
<< " not present";
continue;
}
const char* value = json_string_value(json_dependency);
if(value == nullptr) {
cpp_warn << "Property " << dependencyToken << " of " << TXT_BOLD(name)
<< " is invalid";
continue;
}
auto mapping = villas::utils::tokenize(value, ":");
if(mapping.size() != 2) {
cpp_error << "Invalid " << dependencyToken << " mapping"
<< " of " << TXT_BOLD(name);
dependencyGraph.removeNode(name);
return false;
}
if(name == mapping[0]) {
cpp_error << "IP " << TXT_BOLD(name)<< " cannot depend on itself";
dependencyGraph.removeNode(name);
return false;
}
// already add dependency, if adding it fails, removing the dependency
// will also remove the current one
dependencyGraph.addDependency(name, mapping[0]);
if(not buildDependencyGraph(dependencyGraph, json_ips, mapping[0])) {
cpp_error << "Dependency " << mapping[0] << " of " << TXT_BOLD(name)
<< " not satified";
dependencyGraph.removeNode(mapping[0]);
return false;
}
}
return true;
}
namespace villas {
namespace fpga {
namespace ip {
void IpCore::dump() {
info("IP %s: vlnv=%s baseaddr=%#jx, irq=%d, port=%d",
name.c_str(), vlnv.toString().c_str(), baseaddr, irq, port);
cpp_info << id;
{
Logger::Indenter indent = cpp_info.indent();
cpp_info << " Baseaddr: 0x" << std::hex << baseaddr << std::dec;
// cpp_info << " IRQ: " << irq;
// cpp_info << " Port: " << port;
}
}
@ -54,80 +142,145 @@ IpCoreFactory* IpCoreFactory::lookup(const Vlnv &vlnv)
return nullptr;
}
IpCore *IpCoreFactory::make(fpga::PCIeCard* card, json_t *json, std::string name)
uintptr_t
IpCore::getBaseaddr() const {
assert(card != nullptr);
return reinterpret_cast<uintptr_t>(card->map) + this->baseaddr;
}
IpCoreList
IpCoreFactory::make(PCIeCard* card, json_t *json_ips)
{
int ret;
const char* vlnv_raw;
DependencyGraph dependencyGraph;
IpCoreList initializedIps;
// extract VLNV from JSON
ret = json_unpack(json, "{ s: s }",
"vlnv", &vlnv_raw);
if(ret != 0) {
cpp_warn << "IP " << name << " has no entry 'vlnv'";
return nullptr;
{
Logger::Indenter indent = cpp_debug.indent();
cpp_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);
}
}
// parse VLNV
Vlnv vlnv(vlnv_raw);
{
Logger::Indenter indent = cpp_debug.indent();
cpp_debug << "IP initialization order:";
// 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(vlnv);
if(ipCoreFactory == nullptr) {
cpp_warn << "No plugin found to handle " << vlnv;
return nullptr;
for(auto& ipName : dependencyGraph.getEvaluationOrder()) {
cpp_debug << TXT_BOLD(ipName);
}
}
cpp_debug << "Using " << ipCoreFactory->getName() << " for IP " << vlnv;
// Create new IP instance. Since this function is virtual, it will construct
// the right, specialized type without knowing it here because we have
// already picked the right factory.
IpCore* ip = ipCoreFactory->create();
if(ip == nullptr) {
cpp_warn << "Cannot create an instance of " << ipCoreFactory->getName();
goto fail;
cpp_info << "Initializing IP cores";
Logger::Indenter indent = cpp_info.indent();
for(auto& ipName : dependencyGraph.getEvaluationOrder()) {
cpp_debug << TXT_BOLD(ipName);
json_t* json_ip = json_object_get(json_ips, ipName.c_str());
// extract VLNV from JSON
const char* vlnv;
if(json_unpack(json_ip, "{ s: s }", "vlnv", &vlnv) != 0) {
cpp_warn << "IP " << ipName << " has no entry 'vlnv'";
continue;
}
IpIdentifier id(Vlnv(vlnv), ipName);
// 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);
if(ipCoreFactory == nullptr) {
cpp_warn << "No plugin found to handle " << vlnv;
continue;
} else {
cpp_debug << "Using " << ipCoreFactory->getName()
<< " for IP " << vlnv;
}
// Create new IP instance. Since this function is virtual, it will
// construct the right, specialized type without knowing it here
// because we have already picked the right factory.
// If something goes wrong with initialization, the shared_ptr will
// take care to desctruct the IpCore again as it is not pushed to
// the list and will run out of scope.
auto ip = std::unique_ptr<IpCore>(ipCoreFactory->create());
if(ip == nullptr) {
cpp_warn << "Cannot create an instance of "
<< ipCoreFactory->getName();
continue;
}
// setup generic IP type properties
ip->card = card;
ip->id = id;
// extract some optional properties
int ret = json_unpack(json_ip, "{ s?: i}", //, s?: i, s?: i }",
"baseaddr", &ip->baseaddr);
// "irq", &ip->irq,
// "port", &ip->port);
if(ret != 0) {
cpp_warn << "Problem while parsing JSON for IP "
<< TXT_BOLD(ipName);
continue;
}
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;
});
if(iter == initializedIps.end()) {
cpp_error << "Cannot find '" << depName << "' dependency "
<< depVlnv.toString()
<< "of " << TXT_BOLD(ipName);
dependenciesOk = false;
break;
}
cpp_debug << "Found dependency IP " << (*iter)->id;
ip->dependencies[depName] = (*iter).get();
}
if(not dependenciesOk) {
continue;
}
// IP-specific setup via JSON config
ipCoreFactory->configureJson(ip, json_ip);
// TODO: currently fails, fix and remove comment
// if(not ip->start()) {
// cpp_error << "Cannot start IP" << ip->id.name;
// continue;
// }
if(not ip->check()) {
cpp_error << "Checking IP " << ip->id.name << " failed";
continue;
}
initializedIps.push_back(std::move(ip));
}
// setup generic IP type properties
ip->card = card;
ip->name = name;
ip->vlnv = vlnv;
// extract some optional properties
ret = json_unpack(json, "{ s?: i, s?: i, s?: i }",
"baseaddr", &ip->baseaddr,
"irq", &ip->irq,
"port", &ip->port);
if(ret != 0) {
cpp_warn << "Problem while parsing JSON";
goto fail;
}
// IP-specific setup via JSON config
ipCoreFactory->configureJson(ip, json);
// TODO: currently fails, fix and remove comment
// if(not ip->start()) {
// cpp_error << "Cannot start IP" << ip->name;
// goto fail;
// }
if(not ip->check()) {
cpp_error << "Checking IP " << ip->name << " failed";
goto fail;
}
return ip;
fail:
delete ip;
return nullptr;
return initializedIps;
}
} // namespace ip

View file

@ -29,8 +29,7 @@
#include "kernel/vfio.h"
#include "kernel/kernel.h"
#include "fpga/ip.h"
#include "fpga/card.h"
#include "fpga/card.hpp"
#include "fpga/ips/intc.hpp"
namespace villas {
@ -48,6 +47,7 @@ InterruptController::~InterruptController()
bool InterruptController::start()
{
return true;
const uintptr_t base = getBaseaddr();
num_irqs = vfio_pci_msi_init(&card->vfio_device, efds);

View file

@ -3,12 +3,12 @@
int Logger::depth;
int Logger::depthCurrent;
int Logger::global_level = 0;
Logger::LogLevel Logger::global_level = Logger::LogLevel::Info;
Logger cpp_debug(0, CLR_BLU("Debug: "));
Logger cpp_info(20);
Logger cpp_warn(80, CLR_YEL("Warning: "));
Logger cpp_error(100, CLR_RED("Error: "));
Logger cpp_debug(Logger::LogLevel::Debug, "" CLR_BLU(" Debug ") "| ");
Logger cpp_info(Logger::LogLevel::Info);
Logger cpp_warn(Logger::LogLevel::Warning, "" CLR_YEL("Warning") "| ");
Logger cpp_error(Logger::LogLevel::Error, "" CLR_RED(" Error ") "| ");
void test()
{

View file

@ -29,6 +29,7 @@
#include <villas/fpga/card.h>
#include <villas/fpga/vlnv.h>
#include <villas/log.hpp>
#include <villas/plugin.hpp>
#include <villas/fpga/card.hpp>
@ -53,6 +54,8 @@ static void init()
villas::Plugin::dumpList();
Logger::setLogLevel(Logger::LogLevel::Debug);
ret = pci_init(&pci);
cr_assert_eq(ret, 0, "Failed to initialize PCI sub-system");
@ -80,7 +83,7 @@ static void init()
// create an FPGA card instance using the corresponding plugin
// villas::FpgaCard* fpgaCard = fpgaCardPlugin->make(json_);
std::list<villas::fpga::PCIeCard*> fpgaCards = fpgaCardPlugin->make(fpgas, &pci, &vc);
auto fpgaCards = fpgaCardPlugin->make(fpgas, &pci, &vc);
json_t *json_card = json_object_get(fpgas, FPGA_CARD);
cr_assert_not_null(json_card, "FPGA card " FPGA_CARD " not found");