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