#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <time.h>

#include "nfp.h"
#include "nfp_cpp.h"
#include "nfp_nffw.h"
#include "nfp-common/nfp_resid.h"

#define RTSYM_NAME "cntrs"
int main()
{

    struct nfp_device *h_nfp;
    struct nfp_cpp *h_cpp;
    const struct nfp_rtsym *rtsym;
    struct nfp_cpp_area *area;
    uint8_t read_data[64];
    struct timespec ts0, ts1;
    volatile uint64_t *cpp_data;
    uint64_t val;
    uint32_t cpp_id;
    int i;
    int ret = 0;

    h_nfp = nfp_device_open(0);

    if (!h_nfp) {
        fprintf(stderr,
                "error: failed to open nfp handle (%s)\n",
                strerror(errno));
        return 1;
    }

    for (i = 0; i < nfp_rtsym_count(h_nfp); i++) {
        rtsym = nfp_rtsym_get(h_nfp, i);
        if (strstr(rtsym->name, RTSYM_NAME))
            break;
        rtsym = NULL;
    }

    if (!rtsym) {
        fprintf(stderr,
                "error: could not find rtsym %s\n", RTSYM_NAME);
        ret = -1;
        goto exit;
    }

#define ITERATIONS 1024
    clock_gettime(CLOCK_MONOTONIC, &ts0);
    for (i = 0; i < ITERATIONS; i++) {
        if (nfp_rtsym_read(h_nfp, rtsym, read_data, 8, 0) < 0) {
            fprintf(stderr,
                    "error: rtsym read failed %s\n", strerror(errno));
            ret = -1;
            goto exit;
        }
    }
    clock_gettime(CLOCK_MONOTONIC, &ts1);

    printf("rtsym read took %ld ns\n",
            ((ts1.tv_sec * 1000000000 + ts1.tv_nsec) -
            (ts0.tv_sec * 1000000000 + ts0.tv_nsec)) / ITERATIONS);

    h_cpp = nfp_device_cpp(h_nfp);
    if (!h_cpp) {
        fprintf(stderr,
                "error: failed to get cpp handle (%s)\n",
                strerror(errno));
        goto exit;
    }

#define CPP_TGT_MEM 7
#define CPP_ACTION_READ 0
#define ISL_EMEM0 24
#define OFF_EMEM0 0
    cpp_id = NFP_CPP_ISLAND_ID(CPP_TGT_MEM, CPP_ACTION_READ, 0, ISL_EMEM0);
    area = nfp_cpp_area_alloc(h_cpp, cpp_id, OFF_EMEM0 /* address 0 */, 4096);
    if (!area) {
        fprintf(stderr,
                "error: failed to get cpp area (%s)\n",
                strerror(errno));
        goto exit;
    }

    if (nfp_cpp_area_acquire(area)) {
        fprintf(stderr,
                "error: failed to aquire cpp area (%s)\n",
                strerror(errno));
        goto exit;
    }

    cpp_data = nfp_cpp_area_mapped(area);
    if (cpp_data == NULL) {
        fprintf(stderr,
                "error: failed to get mapped area (%s)\n",
                strerror(errno));
        goto exit;
    }

    clock_gettime(CLOCK_MONOTONIC, &ts0);
    for (i = 0; i < ITERATIONS; i++) {
        val = *cpp_data;
    }
    clock_gettime(CLOCK_MONOTONIC, &ts1);
    printf("read value %lx\n", val);

    printf("cpp area read took %ld ns\n",
            ((ts1.tv_sec * 1000000000 + ts1.tv_nsec) -
            (ts0.tv_sec * 1000000000 + ts0.tv_nsec)) / ITERATIONS);

exit:
    nfp_device_close(h_nfp);
    return ret;
}
