mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
293 lines
No EOL
6.1 KiB
C
293 lines
No EOL
6.1 KiB
C
/** Node type: VILLASfpga
|
|
*
|
|
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
|
* @copyright 2017, Steffen Vogel
|
|
*********************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <inttypes.h>
|
|
|
|
#include "kernel/kernel.h"
|
|
#include "kernel/pci.h"
|
|
|
|
#include "nodes/fpga.h"
|
|
|
|
#include "config.h"
|
|
#include "utils.h"
|
|
#include "timing.h"
|
|
#include "plugin.h"
|
|
|
|
#include "fpga/card.h"
|
|
|
|
static struct list cards;
|
|
static struct pci pci;
|
|
static struct vfio_container vc;
|
|
|
|
void fpga_dump(struct fpga *f)
|
|
{
|
|
struct fpga_card *c = f->ip->card;
|
|
|
|
fpga_card_dump(c);
|
|
}
|
|
|
|
int fpga_init(struct super_node *sn)
|
|
{
|
|
int ret;
|
|
config_setting_t *cfg, *cfg_fpgas;
|
|
|
|
ret = pci_init(&pci);
|
|
if (ret)
|
|
error("Failed to initialize PCI sub-system");
|
|
|
|
ret = vfio_init(&vc);
|
|
if (ret)
|
|
error("Failed to initiliaze VFIO sub-system");
|
|
|
|
/* Parse FPGA configuration */
|
|
cfg = config_root_setting(&sn->cfg);
|
|
cfg_fpgas = config_setting_lookup(cfg, "fpgas");
|
|
if (!cfg_fpgas)
|
|
cerror(cfg, "Config file is missing 'fpgas' section");
|
|
|
|
ret = fpga_card_parse_list(&cards, cfg_fpgas);
|
|
if (ret)
|
|
cerror(cfg, "Failed to parse VILLASfpga config");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fpga_deinit()
|
|
{
|
|
int ret;
|
|
|
|
list_destroy(&cards, (dtor_cb_t) fpga_card_destroy, true);
|
|
|
|
pci_destroy(&pci);
|
|
|
|
ret = vfio_destroy(&vc);
|
|
if (ret)
|
|
error("Failed to deinitialize VFIO sub-system");
|
|
|
|
ret = pci_destroy(&pci);
|
|
if (ret)
|
|
error("Failed to deinitialize PCI sub-system");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fpga_parse(struct node *n, config_setting_t *cfg)
|
|
{
|
|
struct fpga *f = n->_vd;
|
|
struct fpga_card *card;
|
|
struct fpga_ip *ip;
|
|
|
|
char *cpy, *card_name, *ip_name;
|
|
const char *dm;
|
|
|
|
if (!config_setting_lookup_string(cfg, "datamover", &dm))
|
|
cerror(cfg, "Node '%s' is missing the 'datamover' setting", node_name(n));
|
|
|
|
if (!config_setting_lookup_bool(cfg, "use_irqs", &f->use_irqs))
|
|
f->use_irqs = false;
|
|
|
|
cpy = strdup(dm); /* strtok can not operate on type const char * */
|
|
|
|
card_name = strtok(cpy, ":");
|
|
ip_name = strtok(NULL, ":");
|
|
|
|
card = list_lookup(&cards, card_name);
|
|
if (!card)
|
|
cerror(cfg, "There is no FPGA card named '%s", card_name);
|
|
|
|
ip = list_lookup(&card->ips, ip_name);
|
|
if (!ip)
|
|
cerror(cfg, "There is no datamover named '%s' on the FPGA card '%s'", ip_name, card_name);
|
|
if (ip->_vt->type != FPGA_IP_TYPE_DM_DMA && ip->_vt->type != FPGA_IP_TYPE_DM_FIFO)
|
|
cerror(cfg, "The IP '%s' on FPGA card '%s' is not a datamover", ip_name, card_name);
|
|
|
|
free(cpy);
|
|
|
|
f->ip = ip;
|
|
|
|
return 0;
|
|
}
|
|
|
|
char * fpga_print(struct node *n)
|
|
{
|
|
struct fpga *f = n->_vd;
|
|
struct fpga_card *c = f->ip->card;
|
|
|
|
if (f->ip)
|
|
return strf("dm=%s (%s:%s:%s:%s) baseaddr=%#jx port=%u slot=%02"PRIx8":%02"PRIx8".%"PRIx8" id=%04"PRIx16":%04"PRIx16,
|
|
f->ip->name, f->ip->vlnv.vendor, f->ip->vlnv.library, f->ip->vlnv.name, f->ip->vlnv.version,
|
|
f->ip->baseaddr, f->ip->port,
|
|
c->filter.slot.bus, c->filter.slot.device, c->filter.slot.function,
|
|
c->filter.id.vendor, c->filter.id.device);
|
|
else
|
|
return strf("dm=%s", f->ip->name);
|
|
}
|
|
|
|
int fpga_start(struct node *n)
|
|
{
|
|
int ret;
|
|
|
|
struct fpga *f = n->_vd;
|
|
struct fpga_card *c = f->ip->card;
|
|
|
|
fpga_card_init(c, f->pci, f->vfio_container);
|
|
|
|
int flags = 0;
|
|
if (!f->use_irqs)
|
|
flags |= INTC_POLLING;
|
|
|
|
switch (f->ip->_vt->type) {
|
|
case FPGA_IP_TYPE_DM_DMA:
|
|
/* Map DMA accessible memory */
|
|
ret = dma_alloc(f->ip, &f->dma, 0x1000, 0);
|
|
if (ret)
|
|
return ret;
|
|
|
|
intc_enable(c->intc, (1 << (f->ip->irq )), flags); /* MM2S */
|
|
intc_enable(c->intc, (1 << (f->ip->irq + 1)), flags); /* S2MM */
|
|
break;
|
|
|
|
case FPGA_IP_TYPE_DM_FIFO:
|
|
intc_enable(c->intc, (1 << f->ip->irq), flags); /* MM2S & S2MM */
|
|
break;
|
|
|
|
default: { }
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fpga_stop(struct node *n)
|
|
{
|
|
int ret;
|
|
|
|
struct fpga *f = n->_vd;
|
|
struct fpga_card *c = f->ip->card;
|
|
|
|
switch (f->ip->_vt->type) {
|
|
case FPGA_IP_TYPE_DM_DMA:
|
|
intc_disable(c->intc, f->ip->irq); /* MM2S */
|
|
intc_disable(c->intc, f->ip->irq + 1); /* S2MM */
|
|
|
|
ret = dma_free(f->ip, &f->dma);
|
|
if (ret)
|
|
return ret;
|
|
|
|
case FPGA_IP_TYPE_DM_FIFO:
|
|
if (f->use_irqs)
|
|
intc_disable(c->intc, f->ip->irq); /* MM2S & S2MM */
|
|
|
|
default: { }
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fpga_read(struct node *n, struct sample *smps[], unsigned cnt)
|
|
{
|
|
int ret;
|
|
|
|
struct fpga *f = n->_vd;
|
|
struct sample *smp = smps[0];
|
|
|
|
size_t recvlen;
|
|
|
|
size_t len = SAMPLE_DATA_LEN(64);
|
|
|
|
/* We dont get a sequence no from the FPGA. Lets fake it */
|
|
smp->sequence = -1;
|
|
smp->ts.origin = time_now();
|
|
|
|
/* Read data from RTDS */
|
|
switch (f->ip->_vt->type) {
|
|
case FPGA_IP_TYPE_DM_DMA:
|
|
ret = dma_read(f->ip, f->dma.base_phys + 0x800, len);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = dma_read_complete(f->ip, NULL, &recvlen);
|
|
if (ret)
|
|
return ret;
|
|
|
|
memcpy(smp->data, f->dma.base_virt + 0x800, recvlen);
|
|
|
|
smp->length = recvlen / 4;
|
|
return 1;
|
|
case FPGA_IP_TYPE_DM_FIFO:
|
|
recvlen = fifo_read(f->ip, (char *) smp->data, len);
|
|
|
|
smp->length = recvlen / 4;
|
|
return 1;
|
|
|
|
default: { }
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int fpga_write(struct node *n, struct sample *smps[], unsigned cnt)
|
|
{
|
|
int ret;
|
|
struct fpga *f = n->_vd;
|
|
struct sample *smp = smps[0];
|
|
|
|
size_t sentlen;
|
|
size_t len = smp->length * sizeof(smp->data[0]);
|
|
|
|
/* Send data to RTDS */
|
|
switch (f->ip->_vt->type) {
|
|
case FPGA_IP_TYPE_DM_DMA:
|
|
memcpy(f->dma.base_virt, smp->data, len);
|
|
|
|
ret = dma_write(f->ip, f->dma.base_phys, len);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = dma_write_complete(f->ip, NULL, &sentlen);
|
|
if (ret)
|
|
return ret;
|
|
|
|
//info("Sent %u bytes to FPGA", sentlen);
|
|
|
|
return 1;
|
|
case FPGA_IP_TYPE_DM_FIFO:
|
|
sentlen = fifo_write(f->ip, (char *) smp->data, len);
|
|
return sentlen / sizeof(smp->data[0]);
|
|
break;
|
|
|
|
default: { }
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
struct fpga_card * fpga_lookup_card(const char *name)
|
|
{
|
|
return (struct fpga_card *) list_lookup(&cards, name);
|
|
}
|
|
|
|
static struct plugin p = {
|
|
.name = "fpga",
|
|
.description = "VILLASfpga PCIe card (libxil)",
|
|
.type = PLUGIN_TYPE_NODE,
|
|
.node = {
|
|
.size = sizeof(struct fpga),
|
|
.vectorize = 1,
|
|
.parse = fpga_parse,
|
|
.print = fpga_print,
|
|
.start = fpga_start,
|
|
.stop = fpga_stop,
|
|
.read = fpga_read,
|
|
.write = fpga_write,
|
|
.init = fpga_init,
|
|
.deinit = fpga_deinit,
|
|
.instances = LIST_INIT()
|
|
}
|
|
};
|
|
|
|
REGISTER_PLUGIN(&p) |