mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
lib/ips: implement fifo driver and adapt test
This commit is contained in:
parent
e2e78cf8b3
commit
71a54eeab6
6 changed files with 325 additions and 32 deletions
89
fpga/include/villas/fpga/ips/fifo.hpp
Normal file
89
fpga/include/villas/fpga/ips/fifo.hpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
/** Timer related helper functions
|
||||
*
|
||||
* These functions present a simpler interface to Xilinx' Timer Counter driver (XTmrCtr_*)
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @author Daniel Krebs <github@daniel-krebs.net>
|
||||
* @copyright 2017, Steffen Vogel
|
||||
* @license GNU General Public License (version 3)
|
||||
*
|
||||
* VILLASfpga
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
/** @addtogroup fpga VILLASfpga
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fpga/ip_node.hpp"
|
||||
#include <xilinx/xllfifo.h>
|
||||
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
|
||||
class Fifo : public IpNode
|
||||
{
|
||||
public:
|
||||
friend class FifoFactory;
|
||||
|
||||
bool start();
|
||||
bool stop();
|
||||
|
||||
size_t write(const void* buf, size_t len);
|
||||
size_t read(void* buf, size_t len);
|
||||
|
||||
private:
|
||||
XLlFifo xFifo;
|
||||
uintptr_t baseaddr_axi4;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class FifoFactory : public IpNodeFactory {
|
||||
public:
|
||||
FifoFactory() :
|
||||
IpNodeFactory(getName())
|
||||
{}
|
||||
|
||||
bool configureJson(IpCore& ip, json_t *json_ip);
|
||||
|
||||
IpCore* create()
|
||||
{ return new Fifo; }
|
||||
|
||||
std::string
|
||||
getName() const
|
||||
{ return "Fifo"; }
|
||||
|
||||
std::string
|
||||
getDescription() const
|
||||
{ return "Xilinx's AXI4 FIFO data mover"; }
|
||||
|
||||
Vlnv getCompatibleVlnv() const
|
||||
{ return {"xilinx.com:ip:axi_fifo_mm_s:"}; }
|
||||
|
||||
std::list<IpDependency> getDependencies() const
|
||||
{ return { {"intc", Vlnv("acs.eonerc.rwth-aachen.de:user:axi_pcie_intc:") } }; }
|
||||
};
|
||||
|
||||
} // namespace ip
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
||||
|
||||
/** @} */
|
|
@ -14,6 +14,7 @@ set(SOURCES
|
|||
ips/switch.cpp
|
||||
ips/dft.c
|
||||
ips/fifo.c
|
||||
ips/fifo.cpp
|
||||
ips/dma.c
|
||||
ips/intc.cpp
|
||||
ips/intc.c
|
||||
|
|
193
fpga/lib/ips/fifo.cpp
Normal file
193
fpga/lib/ips/fifo.cpp
Normal file
|
@ -0,0 +1,193 @@
|
|||
/** FIFO related helper functions
|
||||
*
|
||||
* These functions present a simpler interface to Xilinx' FIFO driver (XLlFifo_*)
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @author Daniel Krebs <github@daniel-krebs.net>
|
||||
* @copyright 2017, Steffen Vogel
|
||||
* @license GNU General Public License (version 3)
|
||||
*
|
||||
* VILLASfpga
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <xilinx/xstatus.h>
|
||||
#include <xilinx/xllfifo.h>
|
||||
|
||||
#include "log.hpp"
|
||||
#include "fpga/ips/fifo.hpp"
|
||||
#include "fpga/ips/intc.hpp"
|
||||
|
||||
|
||||
namespace villas {
|
||||
namespace fpga {
|
||||
namespace ip {
|
||||
|
||||
// instantiate factory to make available to plugin infrastructure
|
||||
static FifoFactory factory;
|
||||
|
||||
bool
|
||||
FifoFactory::configureJson(IpCore &ip, json_t *json_ip)
|
||||
{
|
||||
if(not IpNodeFactory::configureJson(ip, json_ip)) {
|
||||
cpp_error << "Configuring IpNode failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& fifo = reinterpret_cast<Fifo&>(ip);
|
||||
if(json_unpack(json_ip, "{ s: i }", "baseaddr_axi4", &fifo.baseaddr_axi4) != 0) {
|
||||
cpp_warn << "Cannot parse property 'baseaddr_axi4' of " << ip;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Fifo::start()
|
||||
{
|
||||
XLlFifo_Config fifo_cfg;
|
||||
|
||||
fifo_cfg.Axi4BaseAddress = getAddrMapped(this->baseaddr_axi4);
|
||||
|
||||
// use AXI4 for Data, AXI4-Lite for control
|
||||
fifo_cfg.Datainterface = (this->baseaddr_axi4 != -1) ? 1 : 0;
|
||||
|
||||
if (XLlFifo_CfgInitialize(&xFifo, &fifo_cfg, getBaseaddr()) != XST_SUCCESS)
|
||||
return false;
|
||||
|
||||
// Receive complete IRQ
|
||||
XLlFifo_IntEnable(&xFifo, XLLF_INT_RC_MASK);
|
||||
|
||||
auto intc = reinterpret_cast<InterruptController*>(dependencies["intc"]);
|
||||
intc->enableInterrupt(irqs[0], false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Fifo::stop()
|
||||
{
|
||||
// Receive complete IRQ
|
||||
XLlFifo_IntDisable(&xFifo, XLLF_INT_RC_MASK);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Fifo::write(const void *buf, size_t len)
|
||||
{
|
||||
|
||||
uint32_t tdfv;
|
||||
|
||||
tdfv = XLlFifo_TxVacancy(&xFifo);
|
||||
if (tdfv < len)
|
||||
return -1;
|
||||
|
||||
// buf has to be re-casted because Xilinx driver doesn't use const
|
||||
XLlFifo_Write(&xFifo, (void*) buf, len);
|
||||
XLlFifo_TxSetLen(&xFifo, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t Fifo::read(void *buf, size_t len)
|
||||
{
|
||||
size_t nextlen = 0;
|
||||
uint32_t rxlen;
|
||||
|
||||
auto intc = reinterpret_cast<InterruptController*>(dependencies["intc"]);
|
||||
|
||||
while (!XLlFifo_IsRxDone(&xFifo))
|
||||
intc->waitForInterrupt(irqs[0].num);
|
||||
|
||||
XLlFifo_IntClear(&xFifo, XLLF_INT_RC_MASK);
|
||||
|
||||
/* Get length of next frame */
|
||||
rxlen = XLlFifo_RxGetLen(&xFifo);
|
||||
nextlen = MIN(rxlen, len);
|
||||
|
||||
/* Read from FIFO */
|
||||
XLlFifo_Read(&xFifo, buf, nextlen);
|
||||
|
||||
return nextlen;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
ssize_t fifo_write(struct fpga_ip *c, char *buf, size_t len)
|
||||
{
|
||||
struct fifo *fifo = (struct fifo *) c->_vd;
|
||||
|
||||
XLlFifo *xllfifo = &fifo->inst;
|
||||
|
||||
}
|
||||
|
||||
ssize_t fifo_read(struct fpga_ip *c, char *buf, size_t len)
|
||||
{
|
||||
struct fifo *fifo = (struct fifo *) c->_vd;
|
||||
|
||||
XLlFifo *xllfifo = &fifo->inst;
|
||||
|
||||
size_t nextlen = 0;
|
||||
uint32_t rxlen;
|
||||
|
||||
while (!XLlFifo_IsRxDone(xllfifo))
|
||||
intc_wait(c->card->intc, c->irq);
|
||||
XLlFifo_IntClear(xllfifo, XLLF_INT_RC_MASK);
|
||||
|
||||
/* Get length of next frame */
|
||||
rxlen = XLlFifo_RxGetLen(xllfifo);
|
||||
nextlen = MIN(rxlen, len);
|
||||
|
||||
/* Read from FIFO */
|
||||
XLlFifo_Read(xllfifo, buf, nextlen);
|
||||
|
||||
return nextlen;
|
||||
}
|
||||
|
||||
int fifo_parse(struct fpga_ip *c, json_t *cfg)
|
||||
{
|
||||
struct fifo *fifo = (struct fifo *) c->_vd;
|
||||
|
||||
int baseaddr_axi4 = -1, ret;
|
||||
|
||||
json_error_t err;
|
||||
|
||||
fifo->baseaddr_axi4 = -1;
|
||||
|
||||
ret = json_unpack_ex(cfg, &err, 0, "{ s?: i }", "baseaddr_axi4", &baseaddr_axi4);
|
||||
if (ret)
|
||||
jerror(&err, "Failed to parse configuration of FPGA IP '%s'", c->name);
|
||||
|
||||
fifo->baseaddr_axi4 = baseaddr_axi4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fifo_reset(struct fpga_ip *c)
|
||||
{
|
||||
struct fifo *fifo = (struct fifo *) c->_vd;
|
||||
|
||||
XLlFifo_Reset(&fifo->inst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace ip
|
||||
} // namespace fpga
|
||||
} // namespace villas
|
|
@ -1,12 +1,12 @@
|
|||
set(SOURCES
|
||||
main.cpp
|
||||
dma.c
|
||||
fifo.c
|
||||
hls.c
|
||||
intc.c
|
||||
rtds_rtt.c
|
||||
tmrctr.c
|
||||
xsg.c
|
||||
# dma.c
|
||||
fifo.cpp
|
||||
# hls.c
|
||||
# intc.c
|
||||
# rtds_rtt.c
|
||||
# tmrctr.c
|
||||
# xsg.c
|
||||
)
|
||||
|
||||
add_executable(unit-tests ${SOURCES})
|
||||
|
|
|
@ -31,7 +31,10 @@
|
|||
|
||||
#include <villas/fpga/ips/fifo.h>
|
||||
|
||||
extern struct fpga_card *card;
|
||||
#include <villas/fpga/card.hpp>
|
||||
#include <villas/fpga/ips/fifo.hpp>
|
||||
|
||||
extern villas::fpga::PCIeCard* fpga;
|
||||
|
||||
Test(fpga, fifo, .description = "FIFO")
|
||||
{
|
||||
|
@ -40,35 +43,35 @@ Test(fpga, fifo, .description = "FIFO")
|
|||
char src[255], dst[255];
|
||||
struct fpga_ip *fifo;
|
||||
|
||||
fifo = fpga_vlnv_lookup(&card->ips, &(struct fpga_vlnv) { "xilinx.com", "ip", "axi_fifo_mm_s", NULL });
|
||||
cr_assert(fifo);
|
||||
for(auto& ip : fpga->ips) {
|
||||
// skip non-fifo IPs
|
||||
if(*ip != villas::fpga::Vlnv("xilinx.com:ip:axi_fifo_mm_s:"))
|
||||
continue;
|
||||
|
||||
ret = intc_enable(card->intc, (1 << fifo->irq), 0);
|
||||
cr_assert_eq(ret, 0, "Failed to enable interrupt");
|
||||
auto fifo = reinterpret_cast<villas::fpga::ip::Fifo&>(*ip);
|
||||
|
||||
ret = switch_connect(card->sw, fifo, fifo);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
if(not fifo.loopbackPossible()) {
|
||||
cpp_info << "Loopback test not possible for " << *ip;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get some random data to compare */
|
||||
memset(dst, 0, sizeof(dst));
|
||||
len = read_random((char *) src, sizeof(src));
|
||||
if (len != sizeof(src))
|
||||
error("Failed to get random data");
|
||||
fifo.connectLoopback();
|
||||
|
||||
len = fifo_write(fifo, (char *) src, sizeof(src));
|
||||
if (len != sizeof(src))
|
||||
error("Failed to send to FIFO");
|
||||
/* Get some random data to compare */
|
||||
memset(dst, 0, sizeof(dst));
|
||||
len = read_random((char *) src, sizeof(src));
|
||||
if (len != sizeof(src))
|
||||
error("Failed to get random data");
|
||||
|
||||
len = fifo_read(fifo, (char *) dst, sizeof(dst));
|
||||
if (len != sizeof(dst))
|
||||
error("Failed to read from FIFO");
|
||||
len = fifo.write(src, sizeof(src));
|
||||
if (len != sizeof(src))
|
||||
cpp_error << "Failed to send to FIFO";
|
||||
|
||||
ret = intc_disable(card->intc, (1 << fifo->irq));
|
||||
cr_assert_eq(ret, 0, "Failed to disable interrupt");
|
||||
len = fifo.read(dst, sizeof(dst));
|
||||
if (len != sizeof(dst))
|
||||
cpp_error << "Failed to read from FIFO";
|
||||
|
||||
ret = switch_disconnect(card->sw, fifo, fifo);
|
||||
cr_assert_eq(ret, 0, "Failed to configure switch");
|
||||
|
||||
/* Compare data */
|
||||
cr_assert_eq(memcmp(src, dst, sizeof(src)), 0);
|
||||
/* Compare data */
|
||||
cr_assert_eq(memcmp(src, dst, sizeof(src)), 0);
|
||||
}
|
||||
}
|
|
@ -43,6 +43,7 @@
|
|||
struct pci pci;
|
||||
struct vfio_container vc;
|
||||
villas::fpga::CardList fpgaCards;
|
||||
villas::fpga::PCIeCard* fpga;
|
||||
|
||||
// keep to make it compile with old C tests
|
||||
struct fpga_card* card;
|
||||
|
@ -85,6 +86,12 @@ static void init()
|
|||
// create all FPGA card instances using the corresponding plugin
|
||||
fpgaCards = fpgaCardPlugin->make(fpgas, &pci, &vc);
|
||||
|
||||
if(fpgaCards.size() == 0) {
|
||||
cpp_error << "No FPGA cards found!";
|
||||
} else {
|
||||
fpga = fpgaCards.front().get();
|
||||
}
|
||||
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue