@@ -218,33 +218,74 @@ parameters defined in ``fpga_5gnr_fec_conf`` structure:
time_out = flr_time_out x 16.384us. For instance, if you want to set 10ms for
the FLR time out then set this setting to 0x262=610.
+A companion application pf_config_app is provided as a standalone application
+described in next section.
-An example configuration code calling the function ``fpga_5gnr_fec_configure()`` is shown
-below:
+PF Config App
+-------------
-.. code-block:: c
+The PF Configuration Application ``pf_config_app`` provides a means to
+configure the baseband device at the host-level. The program sets the various
+parameters through memory-mapped IO read/writes. Then the BBDEV driver can be
+used to run the workload.
+
+The parameters are parsed from a given configuration file (with .cfg extentions)
+that is specific to particular BBDEV device, although they follow same format.
+
+External Dependencies
+~~~~~~~~~~~~~~~~~~~~~
+
+The third party .INI parser is a pre-requisite for building the application.
+It can be downloaded from [github]:
+
+.. code-block:: console
- struct fpga_5gnr_fec_conf conf;
- unsigned int i;
+ git clone https://github.com/benhoyt/inih
- memset(&conf, 0, sizeof(struct fpga_5gnr_fec_conf));
- conf.pf_mode_en = 1;
+Use the version release 44 tracked by tag 'r44':
+
+.. code-block:: console
+
+ git checkout r44
+
+The application features a makefile in the `extra/` directory which generates
+the library, `libinih.a`. To compile the inih library, run make as:
+
+.. code-block:: console
+
+ make -f Makefile.static
+
+Building PF Config App
+~~~~~~~~~~~~~~~~~~~~~~
+
+Before building the application, set the following environment variables with
+the location where the INI library is located:
+
+.. code-block:: console
+
+ export INIH_PATH=<path-to-inih-lib>
+
+If not set, makefile will look into current folder.
+
+Next, build the program by typing ``make`` from the pf_Config_app subdirectory
+to produce the binary ``pf_config_app_fpga_5gnr``.
+
+Usage
+~~~~~
+
+The application executes as the following:
+
+.. code-block:: console
- for (i = 0; i < FPGA_5GNR_FEC_NUM_VFS; ++i) {
- conf.vf_ul_queues_number[i] = 4;
- conf.vf_dl_queues_number[i] = 4;
- }
- conf.ul_bandwidth = 12;
- conf.dl_bandwidth = 5;
- conf.dl_load_balance = 64;
- conf.ul_load_balance = 64;
+ ./pf_config_app_fpga_5gnr [-h] [-a] [-c CFG_FILE] [-f NUM_VFS] [-p PCI_ID]
- /* setup FPGA PF */
- ret = fpga_5gnr_fec_configure(info->dev_name, &conf);
- TEST_ASSERT_SUCCESS(ret,
- "Failed to configure 4G FPGA PF for bbdev %s",
- info->dev_name);
+* ``-c CFG_FILE``: Specifies configuration file to use
+* ``-f NUM_VFS``: Specifies number of Virtual Functions to enable through SRIOV
+* ``-p PCI_ID``: Specifies PCI ID of device to configure
+* ``-a``: Configures all PCI devices
+* ``-h``: Prints help
+Default configure file is provided: ``fpga_5gnr_config.cfg``.
Test Application
----------------
new file mode 100644
@@ -0,0 +1,36 @@
+
+CC=gcc
+CFLAGS=-O0 -g -Wall
+ODIR=build
+DEPS=
+
+ifeq ($(INIH_PATH),)
+INCLUDE=-I.
+LDFLAGS=-L.
+else
+INCLUDE=-I. -I$(INIH_PATH)
+LDFLAGS=-L. -L$(INIH_PATH)
+endif
+
+LDLIBS=-linih
+
+SRC = config_app.c fpga_5gnr_cfg_app.c fpga_5gnr_cfg_parser.c
+OBJ = $(patsubst %.c,$(ODIR)/%.o,$(SRC))
+
+.PHONY: clean
+
+all: pf_config_app
+
+$(ODIR):
+ mkdir -p $(ODIR)
+
+$(OBJ): $(ODIR)/%.o: ./%.c | $(DEPS) $(ODIR)
+ @mkdir -p $(@D)
+ $(CC) -c -o $@ $< $(CFLAGS) $(INCLUDE)
+
+pf_config_app: $(OBJ)
+ $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LDLIBS)
+
+clean:
+ rm -rf $(ODIR)
+ rm -rf pf_config_app
new file mode 100644
@@ -0,0 +1,382 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <linux/vfio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "fpga_5gnr_cfg_app.h"
+
+#define SYS_DIR "/sys/bus/pci/devices"
+#define CUR_DIR "."
+#define PREV_DIR ".."
+
+#define DRIVER_LINK "driver"
+#define DEVICE_FILE "device"
+#define VENDOR_FILE "vendor"
+#define BAR0_FILE "resource0"
+#define MAX_VFS_FILE "max_vfs"
+
+#define PCI_STR_SIZE 15
+#define DEV_STR_SIZE 10
+#define NULL_PAD 2
+
+/* Function Pointer for device specific configuration file */
+typedef int (*configuration)(void *bar0addr, const char *arg_cfg_filename);
+
+typedef struct hw_device {
+ const char *device_name;
+ char *config_file;
+ int vendor_id;
+ int device_id;
+ char pci_address[PCI_STR_SIZE];
+ bool driver_found;
+ configuration conf;
+ char *num_vfs;
+ int config_all;
+} hw_device;
+
+static int
+enable_vfs(const char *pci_addr, char *num_vfs)
+{
+ char maxvfspath[PATH_MAX];
+ char fs_num_vfs[4] = {0, 0, 0, 0};
+ int maxvfsfd;
+
+ snprintf(maxvfspath, sizeof(maxvfspath),
+ "%s/%s/%s", SYS_DIR, pci_addr, MAX_VFS_FILE);
+ maxvfsfd = open(maxvfspath, O_RDWR | O_SYNC);
+
+ if (maxvfsfd < 0) {
+ printf("Unable to enable VFs. Was device bound under igb_uio?\n");
+ return -1;
+ }
+
+ /* read current num of VFs */
+ read(maxvfsfd, (void *)fs_num_vfs, 3);
+ strtok(fs_num_vfs, "\n");
+
+ if (!strncmp(fs_num_vfs, num_vfs, 3)) {
+ /* value is same */
+ close(maxvfsfd);
+ return 0;
+ }
+
+ /* update num of VFs */
+ write(maxvfsfd, "0", 1);
+ write(maxvfsfd, num_vfs, strlen(num_vfs));
+ close(maxvfsfd);
+
+ return 0;
+}
+
+static void *
+get_bar0_mapping(const char *pci_addr, unsigned int bar_size)
+{
+ char bar0path[PATH_MAX];
+ int bar0addrfd;
+ void *map;
+
+ snprintf(bar0path, sizeof(bar0path),
+ "%s/%s/%s", SYS_DIR, pci_addr, BAR0_FILE);
+ bar0addrfd = open(bar0path, O_RDWR | O_SYNC);
+ if (bar0addrfd < 0) {
+ printf("\nFailed to open BAR %s\n", bar0path);
+ exit(1);
+ }
+ map = mmap(0, bar_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ bar0addrfd, 0);
+ close(bar0addrfd);
+ return map;
+}
+
+static unsigned long
+get_file_val(const char *file_path)
+{
+ char content[BUFSIZ];
+ FILE *f;
+
+ f = fopen(file_path, "r");
+ if (f == NULL) {
+ printf("\nFailed to open %s\n", file_path);
+ exit(1);
+ }
+ if (fgets(content, sizeof(content), f) == NULL) {
+ fclose(f);
+ return false;
+ }
+ fclose(f);
+ const unsigned long content_val = strtoul(content, NULL, 16);
+
+ return content_val;
+}
+
+static bool
+get_device_id(hw_device *device, const char *location)
+{
+ unsigned long vendor_id = -1, device_id = -1;
+ struct dirent *dirent;
+ DIR *dir;
+ char pci_path[PATH_MAX];
+
+ snprintf(pci_path, sizeof(pci_path), "%s/%s", SYS_DIR, location);
+ dir = opendir(pci_path);
+ if (dir == NULL) {
+ printf("Failed to open %s (%s)\n", pci_path, strerror(errno));
+ return false;
+ }
+
+ while ((dirent = readdir(dir)) != NULL) {
+ char file_path[PATH_MAX];
+
+ /* Omit Current Directory & Previous Directory Lookup */
+ if (strncmp(dirent->d_name, CUR_DIR,
+ strlen(dirent->d_name)) == 0 ||
+ strncmp(dirent->d_name, PREV_DIR,
+ strlen(dirent->d_name)) == 0)
+ continue;
+
+ /* Set filepath as next PCI folder (xxxx:xx:xx.x) */
+ snprintf(file_path, sizeof(file_path), "%s/%s",
+ pci_path, dirent->d_name);
+
+ /* Get Device ID */
+ if (strncmp(dirent->d_name, DEVICE_FILE,
+ strlen(dirent->d_name)) == 0 &&
+ dirent->d_type == DT_REG)
+ device_id = get_file_val(file_path);
+
+ /* Get Vendor ID */
+ if (strncmp(dirent->d_name, VENDOR_FILE,
+ strlen(dirent->d_name)) == 0 &&
+ dirent->d_type == DT_REG)
+ vendor_id = get_file_val(file_path);
+ }
+
+ closedir(dir);
+ /* Check if device is found */
+ return (vendor_id == device->vendor_id &&
+ device_id == device->device_id);
+}
+
+static int
+probe_pci_bus(hw_device *device, char **found_devices)
+{
+ struct dirent *dirent;
+ DIR *dir;
+ int num_devices = 0;
+
+ /* Locate PCI Devices */
+ dir = opendir(SYS_DIR);
+ if (dir == NULL)
+ return -1;
+
+ /* Iterate Through Directories */
+ while ((dirent = readdir(dir)) != NULL) {
+
+ /* Omit Current Directory and Previous Directory Lookup */
+ if (strncmp(dirent->d_name, CUR_DIR,
+ strlen(dirent->d_name)) == 0 ||
+ strncmp(dirent->d_name, PREV_DIR,
+ strlen(dirent->d_name)) == 0)
+ continue;
+ /* Check if current device matches requested device */
+ if (get_device_id(device, dirent->d_name)) {
+ found_devices[num_devices] =
+ (char *) malloc(PCI_STR_SIZE);
+ /* Copy PCI slot of device */
+ strncpy(found_devices[num_devices], dirent->d_name,
+ sizeof(device->pci_address) - NULL_PAD);
+ num_devices++;
+ }
+ }
+
+ return num_devices;
+}
+
+static int
+match_device(char *pci_address, char **found_devices, int num_devices)
+{
+ int i;
+ for (i = 0; i < num_devices; i++) {
+ if (!strncmp(pci_address, found_devices[i],
+ sizeof(pci_address) - NULL_PAD))
+ return 0;
+ }
+
+ printf("Given PCI ID is not available\n");
+ return -1;
+}
+
+static int
+select_device(hw_device *device, char **found_devices, int num_devices)
+{
+ int i, selected;
+ /* If more than one device found, get user input on which to use */
+ if (num_devices >= 2) {
+ printf("More than one device found. Please select which device you would like to use from the list:\n");
+
+ /*Print PCI Slots */
+ for (i = 0; i < num_devices; i++)
+ printf("[%i]: %s\n", i+1, found_devices[i]);
+
+ printf("> ");
+ scanf("%d", &selected);
+ if (selected >= 1 && selected <= num_devices) {
+ strncpy(device->pci_address, found_devices[selected-1],
+ sizeof(device->pci_address) - NULL_PAD);
+ return 0;
+ }
+
+ printf("Invalid Option, please try again..\n");
+ return -1;
+ }
+
+ strncpy(device->pci_address, found_devices[0],
+ sizeof(device->pci_address) - NULL_PAD);
+ return 0;
+}
+
+void set_device(hw_device *device)
+{
+ device->vendor_id = FPGA_5GNR_FEC_VENDOR_ID;
+ device->device_id = FPGA_5GNR_FEC_DEVICE_ID;
+ device->conf = fpga_5gnr_configure;
+ if (device->config_file == NULL) {
+ device->config_file = getenv("FPGA_5GNR_CONFIG_FILE");
+ if (device->config_file == NULL)
+ device->config_file = "fpga_5gnr_config.cfg";
+ }
+}
+
+static void
+print_helper(const char *prgname)
+{
+ printf("Usage: %s [-h] [-a] [-c CFG_FILE] [-f NUM_VFS] [-p PCI_ID]\n\n"
+ " -c CFG_FILE \t specifies configuration file to use\n"
+ " -f NUM_VFS \t specifies number of Virtual Functions to enable through SRIOV\n"
+ " -p PCI_ID \t specifies PCI ID of device to configure\n"
+ " -a \t\t configures all PCI devices matching the given DEVICE_NAME\n"
+ " -h \t\t prints this helper\n\n", prgname);
+}
+
+static int
+parse_args(int argc, char **argv,
+ struct hw_device *device)
+{
+ int opt;
+ char *prgname = argv[0];
+ device->device_name = FPGA_5GNR_FEC_DEV_NAME;
+
+ while ((opt = getopt(argc, argv, "c:f:p:ah")) != -1) {
+ switch (opt) {
+ case 'c':
+ device->config_file = optarg;
+ break;
+ case 'f':
+ device->num_vfs = optarg;
+ break;
+ case 'p':
+ strncpy(device->pci_address, optarg,
+ sizeof(device->pci_address)
+ - NULL_PAD);
+ break;
+ case 'a':
+ device->config_all = 1;
+ break;
+ case 'h':
+ default:
+ print_helper(prgname);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+configure_device(hw_device *device)
+{
+ /* Get BAR0 Mapping for device */
+ void *bar0addr = get_bar0_mapping(device->pci_address,
+ FPGA_5GNR_FEC_BAR_SIZE);
+
+ if (device->num_vfs != 0)
+ if (enable_vfs(device->pci_address, device->num_vfs) < 0)
+ return -1;
+
+ /* Call device specific configuration function */
+ if (device->conf(bar0addr, device->config_file) == 0) {
+
+ if (device->num_vfs != 0)
+ printf("Enabled %s %s VFs\n",
+ device->num_vfs, device->device_name);
+
+ printf("%s PF [%s] configuration complete!\n\n",
+ device->device_name, device->pci_address
+ - NULL_PAD);
+ return 0;
+ }
+ printf("Configuration error!!\n");
+ return -1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i, num_devices;
+ hw_device device;
+ char *found_devices[DEV_STR_SIZE];
+
+ memset(&device, 0, sizeof(device));
+
+ if (parse_args(argc, argv, &device) > 0)
+ return 0;
+
+ /* Set Device Info */
+ set_device(&device);
+ /* Check if device is installed */
+ num_devices = probe_pci_bus(&device, found_devices);
+ if (num_devices == 0) {
+ printf("No devices found!!\n");
+ return -1;
+ } else if (num_devices < 0) {
+ return num_devices;
+ }
+
+ if (device.pci_address[0] != 0) {
+ if (match_device(device.pci_address, found_devices,
+ num_devices) < 0)
+ return -1;
+ }
+
+ if (device.config_all) {
+ for (i = 0; i < num_devices; i++) {
+ strncpy(device.pci_address, found_devices[i],
+ sizeof(device.pci_address) - NULL_PAD);
+ configure_device(&device);
+ }
+ } else {
+ select_device(&device, found_devices, num_devices);
+ configure_device(&device);
+ }
+
+ /* Free memory for stored PCI slots */
+ for (i = 0; i < num_devices; i++)
+ free(found_devices[i]);
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,351 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include "fpga_5gnr_cfg_app.h"
+
+extern int
+fpga_5gnr_parse_conf_file(const char *file_name,
+ struct fpga_5gnr_fec_conf *fpga_conf);
+
+/* Read 8-bit register of FPGA 5GNR FEC device */
+static uint8_t
+fpga_reg_read_8(void *mmio_base, uint32_t offset)
+{
+ void *reg_addr = mmio_base + offset;
+ return *((volatile uint8_t *)(reg_addr));
+}
+
+/* Read 16-bit register of FPGA 5GNR FEC device */
+static uint16_t
+fpga_reg_read_16(void *mmio_base, uint32_t offset)
+{
+ void *reg_addr = mmio_base + offset;
+ uint16_t ret = *((volatile uint16_t *)(reg_addr));
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ return __bswap_16(ret);
+#else
+ return ret;
+#endif
+}
+
+/* Read 32-bit register of FPGA 5GNR FEC device */
+static uint32_t
+fpga_reg_read_32(void *mmio_base, uint32_t offset)
+{
+ void *reg_addr = mmio_base + offset;
+ uint32_t ret = *((volatile uint32_t *)(reg_addr));
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ return __bswap_32(ret);
+#else
+ return ret;
+#endif
+}
+
+static inline void
+fpga_reg_write_16(void *mmio_base, uint32_t offset,
+ uint16_t payload) {
+ void *reg_addr = mmio_base + offset;
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ payload = __bswap_16(payload);
+#endif
+ *((volatile uint16_t *) (reg_addr)) = payload;
+}
+
+static inline void
+fpga_reg_write_32(void *mmio_base, uint32_t offset,
+ uint32_t payload)
+{
+ void *reg_addr = mmio_base + offset;
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ payload = __bswap_32(payload);
+#endif
+ *((volatile uint32_t *) (reg_addr)) = payload;
+}
+
+static inline void
+set_default_fpga_conf(struct fpga_5gnr_fec_conf *def_conf)
+{
+ /* Set pf mode to true */
+ def_conf->pf_mode_en = true;
+
+ /* Set ratio between UL and DL to 1:1 (unit of weight is 3 CBs) */
+ def_conf->ul_bandwidth = 3;
+ def_conf->dl_bandwidth = 3;
+
+ /* Set Load Balance Factor to 64 */
+ def_conf->dl_load_balance = 64;
+ def_conf->ul_load_balance = 64;
+}
+
+static int
+fpga_read_config_file(const char *arg_cfg_filename,
+ struct fpga_5gnr_fec_conf *fpga_5gnr_fec_conf)
+{
+ const char *cfg_filename;
+
+ if (arg_cfg_filename == NULL) {
+ cfg_filename = getenv(FPGA_5GNR_FEC_CONFIG_FILE_ENV);
+ if (cfg_filename == NULL) {
+ cfg_filename = FPGA_5GNR_FEC_CONFIG_FILE_NAME;
+ printf("'%s' was not set. %s will be used\n",
+ FPGA_5GNR_FEC_CONFIG_FILE_NAME,
+ cfg_filename);
+ } else
+ printf("'%s=%s' config file will be used\n",
+ FPGA_5GNR_FEC_CONFIG_FILE_ENV,
+ cfg_filename);
+ } else
+ cfg_filename = arg_cfg_filename;
+
+ return fpga_5gnr_parse_conf_file(cfg_filename, fpga_5gnr_fec_conf);
+}
+
+/* Read Static Register of FPGA 5GNR FEC device */
+static inline void
+print_static_reg_debug_info(void *mmio_base)
+{
+ uint8_t i, q_id;
+ uint32_t fid;
+ uint32_t version_id = fpga_reg_read_32(mmio_base,
+ FPGA_5GNR_FEC_VERSION_ID);
+ uint16_t config = fpga_reg_read_16(mmio_base,
+ FPGA_5GNR_FEC_CONFIGURATION);
+ uint8_t qmap_done = fpga_reg_read_8(mmio_base,
+ FPGA_5GNR_FEC_QUEUE_PF_VF_MAP_DONE);
+ uint16_t lb_factor = fpga_reg_read_16(mmio_base,
+ FPGA_5GNR_FEC_LOAD_BALANCE_FACTOR);
+ uint16_t ring_desc_len = fpga_reg_read_16(mmio_base,
+ FPGA_5GNR_FEC_RING_DESC_LEN);
+ uint16_t flr_time_out = fpga_reg_read_16(mmio_base,
+ FPGA_5GNR_FEC_FLR_TIME_OUT);
+
+ printf("FEC FPGA RTL v%u.%u\n",
+ ((uint16_t)(version_id >> 16)), ((uint16_t)version_id));
+ printf("UL.DL Weights = %u.%u\n",
+ ((uint8_t)config), ((uint8_t)(config >> 8)));
+ printf("UL.DL Load Balance = %u.%u\n",
+ ((uint8_t)lb_factor), ((uint8_t)(lb_factor >> 8)));
+ printf("Queue-PF/VF Mapping Table = %s\n",
+ (qmap_done > 0) ? "READY" : "NOT-READY");
+ printf("Ring Descriptor Size = %u bytes\n",
+ ring_desc_len*FPGA_RING_DESC_LEN_UNIT_BYTES);
+ printf("FLR Timeout = %f usec\n",
+ (float)flr_time_out*FPGA_FLR_TIMEOUT_UNIT);
+
+ printf("\n--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n");
+ printf(" | PF | VF0 | VF1 | VF2 | VF3 | VF4 | VF5 | VF6 | VF7 |\n");
+ printf("--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n");
+
+ for (q_id = 0; q_id < FPGA_TOTAL_NUM_QUEUES; q_id++) {
+
+ printf("%s-Q'%02u |",
+ (q_id < FPGA_NUM_UL_QUEUES) ? "UL" : "DL", q_id);
+
+ fid = fpga_reg_read_32(mmio_base,
+ FPGA_5GNR_FEC_QUEUE_MAP + (q_id << 2));
+
+ for (i = 0; i < 9; ++i) {
+
+ if (!((fid >> 16) & (0x80)) && i == 0) {
+ printf(" X |");
+ continue;
+ }
+
+ if (((((fid >> 16) & (0x7f)) + 1) == i) &&
+ ((fid >> 16) & (0x80)))
+ printf(" X |");
+ else
+ printf(" |");
+ }
+ printf("\n");
+ }
+ printf("--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n\n");
+}
+
+static int
+fpga_write_config(void *mapaddr, struct fpga_5gnr_fec_conf *conf)
+{
+
+ uint32_t payload_32, address;
+ uint16_t payload_16;
+ uint16_t q_id, vf_id, total_q_id, total_ul_q_id, total_dl_q_id;
+
+ uint32_t *bar0addr = mapaddr;
+
+ /*
+ * Configure UL:DL ratio.
+ * [7:0]: UL weight
+ * [15:8]: DL weight
+ */
+ payload_16 = (conf->dl_bandwidth << 8) | conf->ul_bandwidth;
+ address = FPGA_5GNR_FEC_CONFIGURATION;
+ fpga_reg_write_16(bar0addr, address, payload_16);
+
+ /* Clear all queues registers */
+ payload_32 = FPGA_INVALID_HW_QUEUE_ID;
+ for (q_id = 0; q_id < FPGA_TOTAL_NUM_QUEUES; ++q_id) {
+ address = (q_id << 2) + FPGA_5GNR_FEC_QUEUE_MAP;
+ fpga_reg_write_32(bar0addr, address, payload_32);
+ }
+
+ /*
+ * If PF mode is enabled allocate all queues for PF only.
+ *
+ * For VF mode each VF can have different number of UL and DL queues.
+ * Total number of queues to configure cannot exceed FPGA
+ * capabilities - 64 queues - 32 queues for UL and 32 queues for DL.
+ * Queues mapping is done according to configuration:
+ *
+ * UL queues:
+ * | Q_ID | VF_ID |
+ * | 0 | 0 |
+ * | ... | 0 |
+ * | conf->vf_dl_queues_number[0] - 1 | 0 |
+ * | conf->vf_dl_queues_number[0] | 1 |
+ * | ... | 1 |
+ * | conf->vf_dl_queues_number[1] - 1 | 1 |
+ * | ... | ... |
+ * | conf->vf_dl_queues_number[7] - 1 | 7 |
+ *
+ * DL queues:
+ * | Q_ID | VF_ID |
+ * | 32 | 0 |
+ * | ... | 0 |
+ * | conf->vf_ul_queues_number[0] - 1 | 0 |
+ * | conf->vf_ul_queues_number[0] | 1 |
+ * | ... | 1 |
+ * | conf->vf_ul_queues_number[1] - 1 | 1 |
+ * | ... | ... |
+ * | conf->vf_ul_queues_number[7] - 1 | 7 |
+ *
+ * Example of configuration:
+ * conf->vf_ul_queues_number[0] = 4; -> 4 UL queues for VF0
+ * conf->vf_dl_queues_number[0] = 4; -> 4 DL queues for VF0
+ * conf->vf_ul_queues_number[1] = 2; -> 2 UL queues for VF1
+ * conf->vf_dl_queues_number[1] = 2; -> 2 DL queues for VF1
+ *
+ * UL:
+ * | Q_ID | VF_ID |
+ * | 0 | 0 |
+ * | 1 | 0 |
+ * | 2 | 0 |
+ * | 3 | 0 |
+ * | 4 | 1 |
+ * | 5 | 1 |
+ *
+ * DL:
+ * | Q_ID | VF_ID |
+ * | 32 | 0 |
+ * | 33 | 0 |
+ * | 34 | 0 |
+ * | 35 | 0 |
+ * | 36 | 1 |
+ * | 37 | 1 |
+ */
+ if (conf->pf_mode_en) {
+ payload_32 = 0x1;
+ for (q_id = 0; q_id < FPGA_TOTAL_NUM_QUEUES; ++q_id) {
+ address = (q_id << 2) + FPGA_5GNR_FEC_QUEUE_MAP;
+ fpga_reg_write_32(bar0addr, address, payload_32);
+ }
+ } else {
+ /* Calculate total number of UL and DL queues to configure */
+ total_ul_q_id = total_dl_q_id = 0;
+ for (vf_id = 0; vf_id < FPGA_5GNR_FEC_NUM_VFS; ++vf_id) {
+ total_ul_q_id += conf->vf_ul_queues_number[vf_id];
+ total_dl_q_id += conf->vf_dl_queues_number[vf_id];
+ }
+ total_q_id = total_dl_q_id + total_ul_q_id;
+ /*
+ * Check if total number of queues to configure does not exceed
+ * FPGA capabilities (64 queues - 32 UL and 32 DL queues)
+ */
+ if ((total_ul_q_id > FPGA_NUM_UL_QUEUES) ||
+ (total_dl_q_id > FPGA_NUM_DL_QUEUES) ||
+ (total_q_id > FPGA_TOTAL_NUM_QUEUES)) {
+ printf(
+ "FPGA Configuration failed. Too many queues to configure: UL_Q %u, DL_Q %u, FPGA_Q %u",
+ total_ul_q_id, total_dl_q_id,
+ FPGA_TOTAL_NUM_QUEUES);
+ return -EINVAL;
+ }
+ total_ul_q_id = 0;
+ for (vf_id = 0; vf_id < FPGA_5GNR_FEC_NUM_VFS; ++vf_id) {
+ for (q_id = 0; q_id < conf->vf_ul_queues_number[vf_id];
+ ++q_id, ++total_ul_q_id) {
+ address = (total_ul_q_id << 2) +
+ FPGA_5GNR_FEC_QUEUE_MAP;
+ payload_32 = ((0x80 + vf_id) << 16) | 0x1;
+ fpga_reg_write_32(bar0addr, address,
+ payload_32);
+ }
+ }
+ total_dl_q_id = 0;
+ for (vf_id = 0; vf_id < FPGA_5GNR_FEC_NUM_VFS; ++vf_id) {
+ for (q_id = 0; q_id < conf->vf_dl_queues_number[vf_id];
+ ++q_id, ++total_dl_q_id) {
+ address = ((total_dl_q_id + FPGA_NUM_UL_QUEUES)
+ << 2) + FPGA_5GNR_FEC_QUEUE_MAP;
+ payload_32 = ((0x80 + vf_id) << 16) | 0x1;
+ fpga_reg_write_32(bar0addr, address,
+ payload_32);
+ }
+ }
+ }
+
+ /* Setting Load Balance Factor */
+ payload_16 = (conf->dl_load_balance << 8) | (conf->ul_load_balance);
+ address = FPGA_5GNR_FEC_LOAD_BALANCE_FACTOR;
+ fpga_reg_write_16(bar0addr, address, payload_16);
+
+ /* Setting length of ring descriptor entry */
+ payload_16 = FPGA_RING_DESC_ENTRY_LENGTH;
+ address = FPGA_5GNR_FEC_RING_DESC_LEN;
+ fpga_reg_write_16(bar0addr, address, payload_16);
+
+ /* Setting FLR timeout value */
+ payload_16 = conf->flr_time_out;
+ address = FPGA_5GNR_FEC_FLR_TIME_OUT;
+ fpga_reg_write_16(bar0addr, address, payload_16);
+
+ /* Queue PF/VF mapping table is ready */
+ payload_16 = 0x1;
+ address = FPGA_5GNR_FEC_QUEUE_PF_VF_MAP_DONE;
+ fpga_reg_write_16(bar0addr, address, payload_16);
+
+ print_static_reg_debug_info(bar0addr);
+ printf("Mode of operation = %s-mode\n",
+ conf->pf_mode_en ? "PF" : "VF");
+
+ return 0;
+}
+
+int
+fpga_5gnr_configure(void *bar0addr, const char *cfg_filename)
+{
+ struct fpga_5gnr_fec_conf fpga_conf;
+ int ret;
+
+ ret = fpga_read_config_file(cfg_filename, &fpga_conf);
+ if (ret != 0) {
+ printf("Error reading config file.\n");
+ return -1;
+ }
+
+ ret = fpga_write_config(bar0addr, &fpga_conf);
+ if (ret != 0) {
+ printf("Error writing configuration for FPGA.\n");
+ return -1;
+ }
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _FPGA_5GNR_CFG_APP_H_
+#define _FPGA_5GNR_CFG_APP_H_
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <errno.h>
+
+/**< Number of Virtual Functions FGPA 5GNR FEC supports */
+#define FPGA_5GNR_FEC_NUM_VFS 8
+#define FPGA_5GNR_FEC_VENDOR_ID 0x8086
+#define FPGA_5GNR_FEC_DEVICE_ID 0x0D8F
+#define FPGA_5GNR_FEC_BAR_SIZE 0x1000
+#define FPGA_5GNR_FEC_DEV_NAME "FPGA_5GNR_FEC"
+#define FPGA_5GNR_FEC_CONFIG_FILE_ENV "FPGA_5GNR_FEC_CONFIG_FILE"
+#define FPGA_5GNR_FEC_CONFIG_FILE_NAME "fpga_5gnr_fec_config.cfg"
+
+/* Multiplier of 256 bits (32 bytes) */
+#define FPGA_RING_DESC_ENTRY_LENGTH (8)
+#define FPGA_RING_DESC_LEN_UNIT_BYTES (32)
+/* Maximum size of queue */
+#define FPGA_RING_MAX_SIZE (1024)
+#define FPGA_FLR_TIMEOUT_UNIT (16.384)
+
+#define FPGA_NUM_UL_QUEUES (32)
+#define FPGA_NUM_DL_QUEUES (32)
+#define FPGA_TOTAL_NUM_QUEUES (FPGA_NUM_UL_QUEUES + FPGA_NUM_DL_QUEUES)
+
+#define FPGA_INVALID_HW_QUEUE_ID (0xFFFFFFFF)
+
+/* FPGA 5GNR FEC Register mapping on BAR0 */
+enum {
+ FPGA_5GNR_FEC_VERSION_ID = 0x00000000, /* len: 4B */
+ FPGA_5GNR_FEC_CONFIGURATION = 0x00000004, /* len: 2B */
+ FPGA_5GNR_FEC_QUEUE_PF_VF_MAP_DONE = 0x00000008, /* len: 1B */
+ FPGA_5GNR_FEC_LOAD_BALANCE_FACTOR = 0x0000000a, /* len: 2B */
+ FPGA_5GNR_FEC_RING_DESC_LEN = 0x0000000c, /* len: 2B */
+ FPGA_5GNR_FEC_FLR_TIME_OUT = 0x0000000e, /* len: 2B */
+ FPGA_5GNR_FEC_VFQ_FLUSH_STATUS_LW = 0x00000018, /* len: 4B */
+ FPGA_5GNR_FEC_VFQ_FLUSH_STATUS_HI = 0x0000001c, /* len: 4B */
+ FPGA_5GNR_FEC_QUEUE_MAP = 0x00000040, /* len: 256B */
+ FPGA_5GNR_FEC_RING_CTRL_REGS = 0x00000200, /* len: 2048B */
+ FPGA_5GNR_FEC_DDR4_WR_ADDR_REGS = 0x00000A00, /* len: 4B */
+ FPGA_5GNR_FEC_DDR4_WR_DATA_REGS = 0x00000A08, /* len: 8B */
+ FPGA_5GNR_FEC_DDR4_WR_DONE_REGS = 0x00000A10, /* len: 1B */
+ FPGA_5GNR_FEC_DDR4_RD_ADDR_REGS = 0x00000A18, /* len: 4B */
+ FPGA_5GNR_FEC_DDR4_RD_DONE_REGS = 0x00000A20, /* len: 1B */
+ FPGA_5GNR_FEC_DDR4_RD_RDY_REGS = 0x00000A28, /* len: 1B */
+ FPGA_5GNR_FEC_DDR4_RD_DATA_REGS = 0x00000A30, /* len: 8B */
+ FPGA_5GNR_FEC_DDR4_ADDR_RDY_REGS = 0x00000A38, /* len: 1B */
+ FPGA_5GNR_FEC_HARQ_BUF_SIZE_RDY_REGS = 0x00000A40, /* len: 1B */
+ FPGA_5GNR_FEC_HARQ_BUF_SIZE_REGS = 0x00000A48, /* len: 4B */
+ FPGA_5GNR_FEC_MUTEX = 0x00000A60, /* len: 4B */
+ FPGA_5GNR_FEC_MUTEX_RESET = 0x00000A68 /* len: 4B */
+};
+
+/* FIXME Add HARQ/DDR Registers */
+
+/* FPGA 5GNR FEC Ring Control Registers */
+enum {
+ FPGA_5GNR_FEC_RING_HEAD_ADDR = 0x00000008,
+ FPGA_5GNR_FEC_RING_SIZE = 0x00000010,
+ FPGA_5GNR_FEC_RING_MISC = 0x00000014,
+ FPGA_5GNR_FEC_RING_ENABLE = 0x00000015,
+ FPGA_5GNR_FEC_RING_FLUSH_QUEUE_EN = 0x00000016,
+ FPGA_5GNR_FEC_RING_SHADOW_TAIL = 0x00000018,
+ FPGA_5GNR_FEC_RING_HEAD_POINT = 0x0000001C
+};
+
+struct
+fpga_5gnr_fec_conf {
+ /**< 1 if PF is used for dataplane, 0 for VFs */
+ bool pf_mode_en;
+ /**< Number of UL queues per VF */
+ uint8_t vf_ul_queues_number[FPGA_5GNR_FEC_NUM_VFS];
+ /**< Number of DL queues per VF */
+ uint8_t vf_dl_queues_number[FPGA_5GNR_FEC_NUM_VFS];
+ /**< UL bandwidth. Needed for schedule algorithm */
+ uint8_t ul_bandwidth;
+ /**< DL bandwidth. Needed for schedule algorithm */
+ uint8_t dl_bandwidth;
+ /**< UL Load Balance */
+ uint8_t ul_load_balance;
+ /**< DL Load Balance */
+ uint8_t dl_load_balance;
+ /**< FLR timeout value */
+ uint16_t flr_time_out;
+};
+
+/**
+ * Configure FPGA
+ */
+int fpga_5gnr_configure(void *bar0addr, const char *arg_cfg_filename);
+
+#endif /* _FPGA_5GNR_CFG_APP_H_ */
new file mode 100644
@@ -0,0 +1,187 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include <ini.h>
+#include "fpga_5gnr_cfg_app.h"
+
+/* Names of sections used in the configuration file */
+#define MODE "MODE"
+#define UL "UL"
+#define DL "DL"
+#define FLR "FLR"
+
+/* Names of entries in sections used in the configuration file */
+#define PFMODE "pf_mode_en"
+#define BANDWIDTH "bandwidth"
+#define LOAD_BALANCE "load_balance"
+#define QUEUE_MAP "vfqmap"
+#define FLR_TIME_OUT "flr_time_out"
+
+/* Default values for FPGA device configuration variables */
+#define DEFAULT_PF_MODE_EN 1
+#define DEFAULT_UL_BANDWIDTH 3
+#define DEFAULT_DL_BANDWIDTH 3
+#define DEFAULT_UL_LOAD_BALANCE 64
+#define DEFAULT_DL_LOAD_BALANCE 64
+#define DEFAULT_NUM_VF_QUEUE 8
+
+/* Possible values for MODE and LLR_SIGN */
+#define ZERO "0"
+#define ONE "1"
+
+static int
+parse_number8(const char *str, uint8_t *value)
+{
+ uint64_t val = 0;
+ char *end;
+
+ if (str == NULL)
+ return -EINVAL;
+
+ val = strtoul(str, &end, 0);
+ if (val > UINT8_MAX || str == end) {
+ printf("ERROR: Invalid value %" PRIu64 "\n", val);
+ return -ERANGE;
+ }
+
+ *value = (uint8_t) val;
+ return 1;
+}
+
+static int
+parse_number16(const char *str, uint16_t *value)
+{
+ uint64_t val = 0;
+ char *end;
+
+ if (str == NULL)
+ return -EINVAL;
+
+ val = strtoul(str, &end, 0);
+ if (val > UINT16_MAX || str == end) {
+ printf("ERROR: Invalid value %" PRIu64 "\n", val);
+ return -ERANGE;
+ }
+
+ *value = (uint16_t) val;
+ return 1;
+}
+
+static int
+parse_array8(const char *str, uint8_t *array)
+{
+ int i;
+ uint64_t val = 0;
+ char *end;
+
+ if (str == NULL)
+ return -EINVAL;
+
+ char *val_ch = strtok((char *)str, ",");
+ for (i = 0; i < 8 && NULL != val_ch; i++) {
+
+ val = strtoul(val_ch, &end, 0);
+ if (val > UINT8_MAX || val_ch == end) {
+ printf("ERROR: Invalid value %" PRIu64 "\n", val);
+ return -ERANGE;
+ }
+ array[i] = (uint8_t) val;
+
+ val_ch = strtok(NULL, ",");
+ }
+ return 1;
+}
+
+static void
+set_default_config(struct fpga_5gnr_fec_conf *fpga_conf)
+{
+ int i;
+
+ /* Set pf mode to true */
+ fpga_conf->pf_mode_en = DEFAULT_PF_MODE_EN;
+
+ /* Set ratio between UL and DL to 1:1 (unit of weight is 3 CBs) */
+ fpga_conf->ul_bandwidth = DEFAULT_UL_BANDWIDTH;
+ fpga_conf->dl_bandwidth = DEFAULT_DL_BANDWIDTH;
+
+ /* Set Load Balance Factor to 64 */
+ fpga_conf->ul_load_balance = DEFAULT_UL_LOAD_BALANCE;
+ fpga_conf->dl_load_balance = DEFAULT_DL_LOAD_BALANCE;
+
+ for (i = 0; i < FPGA_5GNR_FEC_NUM_VFS; i++) {
+ fpga_conf->vf_ul_queues_number[i] = DEFAULT_NUM_VF_QUEUE / 2;
+ fpga_conf->vf_dl_queues_number[i] = DEFAULT_NUM_VF_QUEUE / 2;
+ }
+}
+
+static int
+fpga_handler(void *user, const char *section,
+ const char *name, const char *value)
+{
+ struct fpga_5gnr_fec_conf *fpga_conf =
+ (struct fpga_5gnr_fec_conf *) user;
+ int ret = 1;
+
+ if (!strcmp(section, MODE) && !strcmp(name, PFMODE)) {
+ if (!strcmp(value, ZERO))
+ fpga_conf->pf_mode_en = false;
+ else if (!strcmp(value, ONE))
+ fpga_conf->pf_mode_en = true;
+ else
+ ret = 0;
+ } else if (!strcmp(section, UL) && !strcmp(name, BANDWIDTH)) {
+ ret = parse_number8(value, &fpga_conf->ul_bandwidth);
+ } else if (!strcmp(section, DL) && !strcmp(name, BANDWIDTH)) {
+ ret = parse_number8(value, &fpga_conf->dl_bandwidth);
+ } else if (!strcmp(section, UL) && !strcmp(name, LOAD_BALANCE)) {
+ ret = parse_number8(value, &fpga_conf->ul_load_balance);
+ } else if (!strcmp(section, DL) && !strcmp(name, LOAD_BALANCE)) {
+ ret = parse_number8(value, &fpga_conf->dl_load_balance);
+ } else if (!strcmp(section, UL) && !strcmp(name, QUEUE_MAP)) {
+ ret = parse_array8(value, fpga_conf->vf_ul_queues_number);
+ } else if (!strcmp(section, DL) && !strcmp(name, QUEUE_MAP)) {
+ ret = parse_array8(value, fpga_conf->vf_dl_queues_number);
+ } else if (!strcmp(section, FLR) && !strcmp(name, FLR_TIME_OUT)) {
+ ret = parse_number16(value, &fpga_conf->flr_time_out);
+ } else {
+ printf("ERROR: Section (%s) or name (%s) is not valid.\n",
+ section, name);
+ return 0;
+ }
+ if (ret != 1)
+ printf("Error: Conversion of value (%s) failed.\n", value);
+
+ return ret;
+}
+
+int
+fpga_5gnr_parse_conf_file(const char *file_name,
+ struct fpga_5gnr_fec_conf *fpga_conf)
+{
+ int ret;
+
+ set_default_config(fpga_conf);
+
+ ret = ini_parse(file_name, fpga_handler, fpga_conf);
+
+ if (ret == -1) {
+ printf("ERROR: Error loading configuration file %s\n",
+ file_name);
+ set_default_config(fpga_conf);
+ return -ENOENT;
+ } else if (ret == -2) {
+ printf("ERROR: Memory allocation error\n");
+ set_default_config(fpga_conf);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,18 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2020 Intel Corporation
+
+[MODE]
+pf_mode_en = 1
+
+[UL]
+bandwidth = 3
+load_balance = 128
+vfqmap = 4,4,4,4,4,4,4,4
+
+[DL]
+bandwidth = 3
+load_balance = 128
+vfqmap = 4,4,4,4,4,4,4,4
+
+[FLR]
+flr_time_out = 610