libnfb examples
Simple access to the control registers
This example will perform some writes and reads to the user component in the firmware.
#include <nfb/nfb.h>
#define SUPERCORE_REG_CMD 0x00
#define SUPERCORE_REG_CMD_ADD (1 << 0)
#define SUPERCORE_REG_CMD_MULT (1 << 1)
#define SUPERCORE_REG_STATUS 0x04
#define SUPERCORE_REG_DATA 0x08
int main(int argc, char *argv[])
{
int node;
/* This is the path to device node, you can use
- NULL or default: nfb_default_dev_path()
- full path: "/dev/nfb0"
or its shortcut: "0"
- persistent path: "/dev/nfb/by-pci-slot/0000:03:00.0"
"/dev/nfb/by-serial-no/COMBO-400G1/15432"
*/
const char *path = nfb_default_dev_path();
struct nfb_device *dev;
struct nfb_comp *comp;
/* Get handle to NFB device for futher operation */
dev = nfb_open(path);
if (!dev)
errx(1, "Can't open device file");
/* Find first supercore unit in Device Tree and get its FDT node offset */
node = nfb_comp_find(dev, "mycompany,supercore", 0);
/* Get access to the component described with Device Tree node */
comp = nfb_comp_open(dev, node);
if (comp == NULL)
errx(2, "Can't open component");
/* Perform some writes and reads to the acceleration core */
nfb_comp_write64(comp, SUPERCORE_REG_DATA, 0xBEEFBEEFBEEFBEEFll);
nfb_comp_write32(comp, SUPERCORE_REG_CMD, SUPERCORE_REG_CMD_ADD);
if (nfb_comp_read8(comp, SUPERCORE_REG_STATUS) != 0)
errx(3, "Operation ADD failed");
/* Cleanup */
nfb_comp_close(comp);
nfb_close(dev);
return 0;
}
Do not forget to compile with -lnfb switch.
NDP data transmit example
#include <stdio.h>
#include <nfb/nfb.h>
#include <nfb/ndp.h>
#define NDP_PACKET_COUNT 16
int main(int argc, char *argv[])
{
int i, ret, bursts;
struct nfb_device *dev;
struct ndp_queue *rxq, *txq;
struct ndp_packet pkts[NDP_PACKET_COUNT];
/* Get handle to NFB device for futher operation */
if ((dev = nfb_open("0")) == NULL)
errx(1, "Can't open device file");
/* Open one RX and one TX NDP queue for data transmit */
rxq = ndp_open_rx_queue(dev, 0);
txq = ndp_open_tx_queue(dev, 0);
if (rxq == NULL || txq == NULL)
errx(1, "Can't open queue");
/* Start transmission on both queues */
ndp_queue_start(rxq);
ndp_queue_start(txq);
for (i = 0; i < NDP_PACKET_COUNT; i++) {
/* Request space for some packets */
pkts[i].data_length = 64 + i;
pkts[i].header_length = 0;
}
/* Request placeholders for packets with specified length */
ret = ndp_tx_burst_get(txq, pkts, NDP_PACKET_COUNT);
if (ret != NDP_PACKET_COUNT)
warnx("Requested %d packet placeholders to send, got %d", NDP_PACKET_COUNT, ret);
for (i = 0; i < ret; i++) {
/* Fill data space with some values */
memset(pkts[i].data, 0, pkts[i].data_length);
/* Pretend IPv4 */
pkts[i].data[13] = 0x08;
}
/* Optional PUT (rather just for symmetricity)
Beware the PUT operation may not send immediately,
it can wait for more packets to be PUTed for best throughput */
// ndp_tx_burst_put(txq);
/* Force send immediately (implies PUT) */
ndp_tx_burst_flush(txq);
/* Let try to receive some packets */
for (bursts = 0; bursts < 32; bursts++) {
/* Let the library fill at most NDP_PACKET_COUNT, but it may be less */
ret = ndp_rx_burst_get(rxq, pkts, NDP_PACKET_COUNT);
if (ret == 0) {
usleep(10000);
continue;
}
for (i = 0; i < ret; i++) {
/* If the metadata is present, it typically holds packet timestamp at offset 0 */
if (pkts[i].header_length >= 8)
printf("Timestamp: %lld\n", *((uint64_t*) (pkts[i].header + 0)));
}
/* We must ensure the return of the processed packets
(although this doesn't have to be done for every GET) */
if ((bursts % 5) == 4)
ndp_rx_burst_put(rxq);
}
ndp_rx_burst_put(rxq);
/* Cleanup */
ndp_close_tx_queue(txq);
ndp_close_rx_queue(rxq);
nfb_close(dev);
return 0;
}