/** Node type: VILLASfpga * * @author Steffen Vogel * @copyright 2017, Steffen Vogel *********************************************************************************/ #include #include #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)