Commit 17de658b authored by Daniel Krebs's avatar Daniel Krebs

lib/ips: implement fifo driver and adapt test

parent 1c29f4ca
Pipeline #34524 failed with stages
in 1 second
/** 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
......
/** 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
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);
ret = intc_enable(card->intc, (1 << fifo->irq), 0);
cr_assert_eq(ret, 0, "Failed to enable interrupt");
for(auto& ip : fpga->ips) {
// skip non-fifo IPs
if(*ip != villas::fpga::Vlnv("xilinx.com:ip:axi_fifo_mm_s:"))
continue;
ret = switch_connect(card->sw, fifo, fifo);
cr_assert_eq(ret, 0, "Failed to configure switch");
auto fifo = reinterpret_cast<villas::fpga::ip::Fifo&>(*ip);
/* 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");
if(not fifo.loopbackPossible()) {
cpp_info << "Loopback test not possible for " << *ip;
continue;
}
len = fifo_write(fifo, (char *) src, sizeof(src));
if (len != sizeof(src))
error("Failed to send to FIFO");
fifo.connectLoopback();
len = fifo_read(fifo, (char *) dst, sizeof(dst));
if (len != sizeof(dst))
error("Failed to read from 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");
ret = intc_disable(card->intc, (1 << fifo->irq));
cr_assert_eq(ret, 0, "Failed to disable interrupt");
len = fifo.write(src, sizeof(src));
if (len != sizeof(src))
cpp_error << "Failed to send to FIFO";
ret = switch_disconnect(card->sw, fifo, fifo);
cr_assert_eq(ret, 0, "Failed to configure switch");
len = fifo.read(dst, sizeof(dst));
if (len != sizeof(dst))
cpp_error << "Failed to read from FIFO";
/* 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);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment