2020-06-15 22:21:56 +02:00
|
|
|
/** Communicate with VILLASfpga Xilinx FPGA boards
|
|
|
|
*
|
2022-12-14 17:41:58 +01:00
|
|
|
* @author Steffen Vogel <post@steffenvogel.de>
|
2022-03-15 09:28:57 -04:00
|
|
|
* @copyright 2014-2022, Institute for Automation of Complex Power Systems, EONERC
|
2022-07-04 18:20:03 +02:00
|
|
|
* @license Apache 2.0
|
2020-06-15 22:21:56 +02:00
|
|
|
*********************************************************************************/
|
|
|
|
|
|
|
|
#include <csignal>
|
|
|
|
#include <iostream>
|
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
|
|
|
#include <algorithm>
|
2023-01-13 11:28:53 +01:00
|
|
|
#include <memory>
|
|
|
|
|
2020-06-15 22:21:56 +02:00
|
|
|
#include <jansson.h>
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
#include <villas/node_compat.hpp>
|
2020-06-15 22:21:56 +02:00
|
|
|
#include <villas/nodes/fpga.hpp>
|
|
|
|
#include <villas/log.hpp>
|
|
|
|
#include <villas/utils.hpp>
|
2020-07-04 22:03:56 +02:00
|
|
|
#include <villas/exceptions.hpp>
|
2021-08-10 10:12:48 -04:00
|
|
|
#include <villas/sample.hpp>
|
2020-06-15 22:21:56 +02:00
|
|
|
#include <villas/super_node.hpp>
|
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
#include <villas/fpga/card.hpp>
|
2020-06-15 22:21:56 +02:00
|
|
|
#include <villas/fpga/core.hpp>
|
|
|
|
#include <villas/fpga/vlnv.hpp>
|
|
|
|
#include <villas/fpga/ips/dma.hpp>
|
|
|
|
|
|
|
|
using namespace villas;
|
|
|
|
using namespace villas::node;
|
2021-02-24 15:33:47 +01:00
|
|
|
using namespace villas::fpga;
|
2020-06-15 22:21:56 +02:00
|
|
|
using namespace villas::utils;
|
|
|
|
|
|
|
|
/* Global state */
|
2023-01-13 11:28:53 +01:00
|
|
|
static std::list<std::shared_ptr<fpga::PCIeCard>> cards;
|
2022-11-03 07:51:23 -04:00
|
|
|
static std::map<fpga::ip::Dma, FpgaNode *> dmaMap;
|
2020-06-15 22:21:56 +02:00
|
|
|
|
|
|
|
static std::shared_ptr<kernel::pci::DeviceList> pciDevices;
|
|
|
|
static std::shared_ptr<kernel::vfio::Container> vfioContainer;
|
|
|
|
|
|
|
|
using namespace villas;
|
2021-08-10 10:12:48 -04:00
|
|
|
using namespace villas::node;
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2023-06-30 11:30:05 +02:00
|
|
|
FpgaNode::FpgaNode(const uuid_t &id, const std::string &name) :
|
|
|
|
Node(id, name),
|
2021-02-24 15:33:47 +01:00
|
|
|
irqFd(-1),
|
|
|
|
coalesce(0),
|
|
|
|
polling(true)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
FpgaNode::~FpgaNode()
|
|
|
|
{ }
|
|
|
|
|
2023-06-30 10:51:01 +02:00
|
|
|
int FpgaNode::parse(json_t *json)
|
2020-06-15 22:21:56 +02:00
|
|
|
{
|
2023-06-30 10:48:43 +02:00
|
|
|
int ret = Node::parse(json);
|
2022-11-03 07:51:23 -04:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2020-06-15 22:21:56 +02:00
|
|
|
|
|
|
|
json_error_t err;
|
|
|
|
|
|
|
|
const char *card = nullptr;
|
|
|
|
const char *intf = nullptr;
|
|
|
|
const char *dma = nullptr;
|
2021-02-24 15:33:47 +01:00
|
|
|
int poll = polling;
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2023-06-30 10:48:43 +02:00
|
|
|
ret = json_unpack_ex(json, &err, 0, "{ s?: s, s?: s, s?: s, s?: i, s?: b }",
|
2020-06-15 22:21:56 +02:00
|
|
|
"card", &card,
|
|
|
|
"interface", &intf,
|
|
|
|
"dma", &dma,
|
2021-02-24 15:33:47 +01:00
|
|
|
"coalesce", &coalesce,
|
2020-06-15 22:21:56 +02:00
|
|
|
"polling", &polling
|
|
|
|
);
|
|
|
|
if (ret)
|
2023-06-30 10:48:43 +02:00
|
|
|
throw ConfigError(json, err, "node-config-fpga", "Failed to parse configuration of node {}", this->getName());
|
2020-06-15 22:21:56 +02:00
|
|
|
|
|
|
|
if (card)
|
2021-02-24 15:33:47 +01:00
|
|
|
cardName = card;
|
2020-06-15 22:21:56 +02:00
|
|
|
|
|
|
|
if (intf)
|
2021-02-24 15:33:47 +01:00
|
|
|
intfName = intf;
|
2020-06-15 22:21:56 +02:00
|
|
|
|
|
|
|
if (dma)
|
2021-02-24 15:33:47 +01:00
|
|
|
dmaName = dma;
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2021-02-24 15:33:47 +01:00
|
|
|
polling = poll; // cast int to bool
|
2020-06-15 22:21:56 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
const std::string & FpgaNode::getDetails()
|
2020-06-15 22:21:56 +02:00
|
|
|
{
|
2022-11-03 07:51:23 -04:00
|
|
|
if (details.empty()) {
|
|
|
|
auto &name = card ? card->name : cardName;
|
|
|
|
|
|
|
|
details = fmt::format("fpga={}, dma={}, if={}, polling={}, coalesce={}",
|
|
|
|
name,
|
|
|
|
dma->getInstanceName(),
|
|
|
|
polling ? "yes" : "no",
|
|
|
|
coalesce
|
|
|
|
);
|
|
|
|
}
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
return details;
|
2020-06-15 22:21:56 +02:00
|
|
|
}
|
|
|
|
|
2021-02-24 15:33:47 +01:00
|
|
|
int FpgaNode::check()
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int FpgaNode::prepare()
|
2020-06-15 22:21:56 +02:00
|
|
|
{
|
2021-02-24 15:33:47 +01:00
|
|
|
auto it = cardName.empty()
|
2020-06-15 22:21:56 +02:00
|
|
|
? cards.begin()
|
2023-01-13 11:28:53 +01:00
|
|
|
: std::find_if(cards.begin(), cards.end(), [this](std::shared_ptr<fpga::PCIeCard> c) {
|
2021-02-24 15:33:47 +01:00
|
|
|
return c->name == cardName;
|
2020-06-15 22:21:56 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
if (it == cards.end())
|
2022-11-03 07:51:23 -04:00
|
|
|
throw ConfigError(json_object_get(config, "fpga"), "node-config-fpga-card", "Invalid FPGA card name: {}", cardName);
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2021-02-24 15:33:47 +01:00
|
|
|
card = *it;
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
auto intfCore = intfName.empty()
|
2021-02-24 15:33:47 +01:00
|
|
|
? card->lookupIp(fpga::Vlnv(FPGA_AURORA_VLNV))
|
|
|
|
: card->lookupIp(intfName);
|
2022-11-03 07:51:23 -04:00
|
|
|
if (!intfCore)
|
|
|
|
throw ConfigError(config, "node-config-fpga-interface", "There is no interface IP with the name: {}", intfName);
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
intf = std::dynamic_pointer_cast<fpga::ip::Node>(intfCore);
|
2021-02-24 15:33:47 +01:00
|
|
|
if (!intf)
|
2023-01-10 16:31:01 +00:00
|
|
|
throw RuntimeError("The IP {} is not a interface", *intfCore);
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
auto dmaCore = dmaName.empty()
|
2021-02-24 15:33:47 +01:00
|
|
|
? card->lookupIp(fpga::Vlnv(FPGA_DMA_VLNV))
|
|
|
|
: card->lookupIp(dmaName);
|
2022-11-03 07:51:23 -04:00
|
|
|
if (!dmaCore)
|
|
|
|
throw ConfigError(config, "node-config-fpga-dma", "There is no DMA IP with the name: {}", dmaName);
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
dma = std::dynamic_pointer_cast<fpga::ip::Dma>(dmaCore);
|
2021-02-24 15:33:47 +01:00
|
|
|
if (!dma)
|
2023-01-10 16:31:01 +00:00
|
|
|
throw RuntimeError("The IP {} is not a DMA controller", *dmaCore);
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
int ret = intf->connect(*(dma), true);
|
2020-06-15 22:21:56 +02:00
|
|
|
if (ret)
|
2022-11-03 07:51:23 -04:00
|
|
|
throw RuntimeError("Failed to connect: {} -> {}", *(intf), *(dma));
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
auto &alloc = HostDmaRam::getAllocator();
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2023-07-25 18:16:51 +02:00
|
|
|
const std::shared_ptr<villas::MemoryBlock> blockRx = alloc.allocateBlock(0x100 * sizeof(float));
|
|
|
|
const std::shared_ptr<villas::MemoryBlock> blockTx = alloc.allocateBlock(0x100 * sizeof(float));
|
|
|
|
villas::MemoryAccessor<float> memRx = *blockRx;
|
|
|
|
villas::MemoryAccessor<float> memTx = *blockTx;
|
2022-11-03 07:51:23 -04:00
|
|
|
|
2023-07-25 18:16:51 +02:00
|
|
|
dma->makeAccesibleFromVA(blockRx);
|
|
|
|
dma->makeAccesibleFromVA(blockTx);
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
// Show some debugging infos
|
|
|
|
auto &mm = MemoryManager::get();
|
2021-08-10 10:12:48 -04:00
|
|
|
|
2021-02-24 15:33:47 +01:00
|
|
|
dma->dump();
|
|
|
|
intf->dump();
|
2022-11-03 07:51:23 -04:00
|
|
|
mm.getGraph().dump();
|
|
|
|
|
|
|
|
return Node::prepare();
|
|
|
|
}
|
|
|
|
|
|
|
|
int FpgaNode::_read(Sample *smps[], unsigned cnt)
|
2020-06-15 22:21:56 +02:00
|
|
|
{
|
2020-07-03 10:04:51 +02:00
|
|
|
unsigned read;
|
2022-11-03 07:51:23 -04:00
|
|
|
Sample *smp = smps[0];
|
2020-06-15 22:21:56 +02:00
|
|
|
|
|
|
|
assert(cnt == 1);
|
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
dma->read(*blockRx, blockRx->getSize()); // TODO: calc size
|
2023-07-25 18:16:51 +02:00
|
|
|
const size_t bytesRead = dma->readComplete().bytes;
|
2020-06-15 22:21:56 +02:00
|
|
|
read = bytesRead / sizeof(int32_t);
|
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
auto mem = MemoryAccessor<uint32_t>(*blockRx);
|
|
|
|
|
2021-02-24 15:33:47 +01:00
|
|
|
for (unsigned i = 0; i < MIN(read, smp->capacity); i++)
|
2022-11-03 07:51:23 -04:00
|
|
|
smp->data[i].i = mem[i];
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
smp->signals = in.signals;
|
2020-07-06 15:07:05 +02:00
|
|
|
|
2020-06-15 22:21:56 +02:00
|
|
|
return read;
|
|
|
|
}
|
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
int FpgaNode::_write(Sample *smps[], unsigned cnt)
|
2020-06-15 22:21:56 +02:00
|
|
|
{
|
|
|
|
int written;
|
2022-11-03 07:51:23 -04:00
|
|
|
Sample *smp = smps[0];
|
2020-06-15 22:21:56 +02:00
|
|
|
|
|
|
|
assert(cnt == 1);
|
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
auto mem = MemoryAccessor<uint32_t>(*blockTx);
|
|
|
|
|
2021-02-24 15:33:47 +01:00
|
|
|
for (unsigned i = 0; i < smps[0]->length; i++)
|
2022-11-03 07:51:23 -04:00
|
|
|
mem[i] = smps[0]->data[i].i;
|
2021-08-10 10:12:48 -04:00
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
bool state = dma->write(*blockTx, smp->length * sizeof(int32_t));
|
2020-06-15 22:21:56 +02:00
|
|
|
if (!state)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
written = 0; /* The number of samples written */
|
|
|
|
|
|
|
|
return written;
|
|
|
|
}
|
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
std::vector<int> FpgaNode::getPollFDs()
|
2020-06-15 22:21:56 +02:00
|
|
|
{
|
2022-11-03 07:51:23 -04:00
|
|
|
std::vector<int> fds;
|
2020-06-15 22:21:56 +02:00
|
|
|
|
2022-11-03 07:51:23 -04:00
|
|
|
if (!polling)
|
|
|
|
fds.push_back(irqFd);
|
|
|
|
|
|
|
|
return fds;
|
|
|
|
}
|
|
|
|
|
|
|
|
int FpgaNodeFactory::start(SuperNode *sn)
|
|
|
|
{
|
2022-12-07 18:51:39 +01:00
|
|
|
vfioContainer = std::make_shared<kernel::vfio::Container>();
|
2022-11-03 07:51:23 -04:00
|
|
|
pciDevices = std::make_shared<kernel::pci::DeviceList>();
|
|
|
|
|
2022-12-02 18:36:07 +01:00
|
|
|
// Get the FPGA card plugin
|
2022-11-03 07:51:23 -04:00
|
|
|
auto pcieCardPlugin = plugin::registry->lookup<fpga::PCIeCardFactory>("pcie");
|
|
|
|
if (!pcieCardPlugin)
|
|
|
|
throw RuntimeError("No FPGA PCIe plugin found");
|
|
|
|
|
|
|
|
json_t *json_cfg = sn->getConfig();
|
|
|
|
json_t *json_fpgas = json_object_get(json_cfg, "fpgas");
|
|
|
|
if (!json_fpgas)
|
|
|
|
throw ConfigError(json_cfg, "node-config-fpgas", "No section 'fpgas' found in config");
|
|
|
|
|
2022-12-07 18:51:25 +01:00
|
|
|
// Create all FPGA card instances using the corresponding plugin
|
2022-12-02 18:36:07 +01:00
|
|
|
auto piceCards = fpga::PCIeCardFactory::make(json_fpgas, pciDevices, vfioContainer);
|
2022-11-03 07:51:23 -04:00
|
|
|
|
2022-12-02 18:36:07 +01:00
|
|
|
cards.splice(cards.end(), piceCards);
|
2022-11-03 07:51:23 -04:00
|
|
|
|
2023-04-04 13:37:35 +00:00
|
|
|
return NodeFactory::start(sn);
|
2022-11-03 07:51:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static FpgaNodeFactory p;
|