ctu_can_fd_userspace.cpp 15.1 KB
Newer Older
Ille, Ondrej, Ing.'s avatar
Ille, Ondrej, Ing. committed
1
/*******************************************************************************
Martin Jeřábek's avatar
Martin Jeřábek committed
2
 *
Ille, Ondrej, Ing.'s avatar
Ille, Ondrej, Ing. committed
3
 * CTU CAN FD IP Core
4
 * Copyright (C) 2015-2018
Martin Jeřábek's avatar
Martin Jeřábek committed
5
 *
6 7
 * Authors:
 *     Ondrej Ille <ondrej.ille@gmail.com>
Ille, Ondrej, Ing.'s avatar
Ille, Ondrej, Ing. committed
8
 *     Martin Jerabek <martin.jerabek01@gmail.com>
Martin Jeřábek's avatar
Martin Jeřábek committed
9 10
 *
 * Project advisors:
Ille, Ondrej, Ing.'s avatar
Ille, Ondrej, Ing. committed
11 12
 * 	Jiri Novak <jnovak@fel.cvut.cz>
 * 	Pavel Pisa <pisa@cmp.felk.cvut.cz>
Martin Jeřábek's avatar
Martin Jeřábek committed
13
 *
Ille, Ondrej, Ing.'s avatar
Ille, Ondrej, Ing. committed
14 15 16
 * Department of Measurement         (http://meas.fel.cvut.cz/)
 * Faculty of Electrical Engineering (http://www.fel.cvut.cz)
 * Czech Technical University        (http://www.cvut.cz/)
Martin Jeřábek's avatar
Martin Jeřábek committed
17
 *
Ille, Ondrej, Ing.'s avatar
Ille, Ondrej, Ing. committed
18 19 20 21
 * 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 2
 * of the License, or (at your option) any later version.
Martin Jeřábek's avatar
Martin Jeřábek committed
22
 *
Ille, Ondrej, Ing.'s avatar
Ille, Ondrej, Ing. committed
23 24 25 26
 * 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.
Martin Jeřábek's avatar
Martin Jeřábek committed
27
 *
Ille, Ondrej, Ing.'s avatar
Ille, Ondrej, Ing. committed
28 29 30
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Martin Jeřábek's avatar
Martin Jeřábek committed
31
 *
32
 ******************************************************************************/
Ille, Ondrej, Ing.'s avatar
Ille, Ondrej, Ing. committed
33

34
#include "userspace_utils.h"
35

36
#include <iostream>
37 38 39 40 41
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
42
#include <time.h>
Martin Jeřábek's avatar
Martin Jeřábek committed
43

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
/*
/sys/devices/pci0000:00/0000:00:1c.4/0000:05:00.0
*/

typedef struct find_dir_chain {
    char *dir_name;
    DIR *dir;
    struct find_dir_chain *parent;
} find_dir_chain_t;

int pci_find_dev(char **dev_dir, const char *dir_name, unsigned vid, unsigned pid, int inst)
{
    unsigned vid_act, pid_act;
    find_dir_chain_t *dir_stack = NULL;
    find_dir_chain_t *dir_act = NULL;
    char *entry_name = NULL;
    FILE *f;
    struct dirent *entry;
    int level = 0;
    int sz1, sz2 = 0;
    char level0_prefix[] = "pci";
    int clean_rest = 0;

    *dev_dir = NULL;

    while(1) {
        if (dir_act != NULL) {
            while (((entry = readdir(dir_act->dir)) == NULL) || (clean_rest)) {
                closedir(dir_act->dir);
                free(dir_act->dir_name);
                dir_stack = dir_act->parent;
                free(dir_act);
                dir_act = dir_stack;
                level--;
                if (dir_stack == NULL)
                    break;
            }
            if (dir_act == NULL)
                break;
            if (entry->d_type != DT_DIR)
                continue;
            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
                continue;
            dir_name = dir_stack->dir_name;
            entry_name = entry->d_name;
            if ((level <= 1) && strncmp(entry_name, level0_prefix, strlen(level0_prefix)))
                continue;
        }
        dir_act = (find_dir_chain_t *)malloc(sizeof(find_dir_chain_t));
        if (dir_act == NULL)
            err(1, "malloc failed");
        dir_act->parent = dir_stack;
        sz1 = strlen(dir_name);
        if (entry_name != NULL)
            sz2 = strlen(entry_name);
        dir_act->dir_name = (char *)malloc(sz1 + sz2 + 2);
        if (dir_act->dir_name == NULL)
            err(1, "malloc failed");
        memcpy(dir_act->dir_name, dir_name, sz1);
        if (entry_name != NULL) {
          dir_act->dir_name[sz1] = '/';
          memcpy(dir_act->dir_name + sz1 + 1, entry_name, sz2);
          dir_act->dir_name[sz1 + 1 + sz2] = 0;
        } else {
          dir_act->dir_name[sz1] = 0;
        }
        /*printf("level %d, name = %s\n", level, dir_act->dir_name);*/
        dir_stack = dir_act;
        dir_act->dir = opendir(dir_act->dir_name);
        if (dir_act->dir == NULL)
            err(1, "opendir failed");
        level++;
        if (level <= 2)
            continue;
        {
            int sz1 = strlen(dir_act->dir_name);
            char vid_fname[sz1 + strlen("/vendor") + 1];
            char pid_fname[sz1 + strlen("/device") + 1];
            memcpy(vid_fname, dir_act->dir_name, sz1);
            memcpy(pid_fname, dir_act->dir_name, sz1);
            strcpy(vid_fname + sz1, "/vendor");
            strcpy(pid_fname + sz1, "/device");
            f = fopen(vid_fname, "r");
            if (f == NULL)
                continue;
            vid_act = -1;
            fscanf(f, "%i", &vid_act);
            fclose(f);
            f = fopen(pid_fname, "r");
            if (f == NULL)
                continue;
            pid_act = -1;
            fscanf(f, "%i", &pid_act);
            fclose(f);
        }
        if (vid != vid_act)
            continue;
        if (pid != pid_act)
            continue;
        if (inst-- > 0)
            continue;
        *dev_dir = strdup(dir_act->dir_name);
        clean_rest = 1;
    }

    return clean_rest;
}

uintptr_t pci_find_bar(unsigned vid, unsigned pid, int inst, int barnr)
{
    FILE *f;
    char *dev_dir;
    int sz1;
    unsigned long long bar1_base;
    if (!pci_find_dev(&dev_dir, "/sys/devices", vid, pid, inst)) {
        return 0;
    }
    printf("found %s\n", dev_dir);
    sz1 = strlen(dev_dir);

    {
        char buff[200];
        char resource_fname[sz1 + strlen("/resource") + 1];
        memcpy(resource_fname, dev_dir, sz1);
        strcpy(resource_fname + sz1, "/resource");
        f = fopen(resource_fname, "r");
        if (f == NULL)
            return 0;
        while (barnr-- > 0)
            fgets(buff, sizeof(buff) - 1, f);
        fscanf(f, "%lli", &bar1_base);
        fclose(f);
    }
    return bar1_base;
}
Martin Jeřábek's avatar
Martin Jeřábek committed
179

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
static inline void
timespec_sub (struct timespec *diff, const struct timespec *left,
              const struct timespec *right)
{
  diff->tv_sec = left->tv_sec - right->tv_sec;
  diff->tv_nsec = left->tv_nsec - right->tv_nsec;

  if (diff->tv_nsec < 0)
    {
      --diff->tv_sec;
      diff->tv_nsec += 1000000000;
    }
}


195 196
int main(int argc, char *argv[])
{
197
    uintptr_t addr_base = 0;
198 199
    unsigned ifc = 0;
    bool do_transmit = false;
200 201 202
    int loop_cycle = 0;
    int gap = 1000;
    int bitrate = 1000000;
203
    int dbitrate = 0;
204
    bool do_periodic_transmit = false;
205
    bool transmit_fdf = false;
206
    bool loopback_mode = false;
207
    bool test_read_speed = false;
208
    //bool do_showhelp = false;
209
    static uintptr_t addrs[] = {0x43C30000, 0x43C70000};
210 211 212 213

    int c;
    char *e;
    const char *progname = argv[0];
214
    while ((c = getopt(argc, argv, "i:a:g:b:B:fltThpr")) != -1) {
215 216 217 218 219 220
        switch (c) {
            case 'i':
                ifc = strtoul(optarg, &e, 0);
                if (*e != '\0')
                    err(1, "-i expects a number");
            break;
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239

            case 'a':
                addr_base = strtoul(optarg, &e, 0);
                if (*e != '\0')
                    err(1, "-a expects a number");
            break;

            case 'g':
                gap = strtoul(optarg, &e, 0);
                if (*e != '\0')
                    err(1, "-g expects a number");
            break;

            case 'b':
                bitrate = strtoul(optarg, &e, 0);
                if (*e != '\0')
                    err(1, "-b expects a number");
            break;

240 241 242 243 244 245
            case 'B':
                dbitrate = strtoul(optarg, &e, 0);
                if (*e != '\0')
                    err(1, "-B expects a number");
            break;

246
            case 'l': loopback_mode = true; break;
247
            case 't': do_transmit = true; break;
248
            case 'T': do_periodic_transmit = true; break;
249
            case 'f': transmit_fdf = true; break;
250
            case 'r': test_read_speed  = true; break;
251 252 253
            case 'p':
                addrs[0] = pci_find_bar(0x1172, 0xcafd, 0, 1);
                if (!addrs[0])
254 255 256
                    addrs[0] = pci_find_bar(0x1760, 0xff00, 0, 1);
                    if (!addrs[0])
                        err(1, "-p PCI device not found");
257 258
                addrs[1] = addrs[0] + 0x4000;
            break;
259
            case 'h':
260
                printf("Usage: %s [-i ifc] [-a address] [-l] [-t] [-T]\n\n"
261 262 263 264 265 266
                       "  -t: Transmit\n",
                       progname
                );
                return 0;
        }
    }
267

268 269 270 271
    if (ifc >= 2) {
        std::cerr << "Err: ifc number must be 0 or 1.\n";
        exit(1);
    }
272 273
    if (addr_base == 0)
        addr_base = addrs[ifc];
274

275
    struct ctucanfd_priv *priv = ctucanfd_init(addr_base);
276 277 278
    int res;

    union ctu_can_fd_device_id_version reg;
279
    reg.u32 = priv->read_reg(priv, CTU_CAN_FD_DEVICE_ID);
280 281 282 283 284 285 286 287 288 289 290 291

    printf("DevID: 0x%08x, should be 0x%08x\n", reg.s.device_id, CTU_CAN_FD_ID);

    if (!ctu_can_fd_check_access(priv))
        errx(1, "error: ctu_can_fd_check_access");

    u32 version = ctu_can_fd_get_version(priv);
    printf("Core version: %u\n", version);

    //struct can_ctrlmode ctrlmode = {CAN_CTRLMODE_FD, CAN_CTRLMODE_FD};
    //ctu_can_fd_set_mode(priv, &ctrlmode);

292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
    if (test_read_speed) {
        struct timespec tic, tac, diff;
	int i;
	u32 dummy;
	(void)dummy;
        clock_gettime(CLOCK_MONOTONIC, &tic);
	for (i = 0; i < 1000 * 1000; i++) {
		dummy = ctu_can_fd_read32(priv, CTU_CAN_FD_RX_DATA);
	}
        clock_gettime(CLOCK_MONOTONIC, &tac);

	timespec_sub(&diff, &tac, &tic);
	printf("%d reads takes %ld.%09ld s\n",
	       i, (long)diff.tv_sec, diff.tv_nsec);

307 308 309 310 311 312 313 314 315 316 317
	dummy = 0;
        clock_gettime(CLOCK_MONOTONIC, &tic);
	for (i = 0; i < 1000 * 1000; i++) {
		ctu_can_fd_write32(priv, CTU_CAN_FD_FILTER_C_VAL, dummy);
	}
        clock_gettime(CLOCK_MONOTONIC, &tac);

	timespec_sub(&diff, &tac, &tic);
	printf("%d writes takes %ld.%09ld s\n",
	       i, (long)diff.tv_sec, diff.tv_nsec);

318 319
        return 0;
    }
320 321


322 323
    //printf("NOT RESETTING!\n");
    ctu_can_fd_reset(priv);
Martin Jeřábek's avatar
Martin Jeřábek committed
324 325 326 327

    {
        union ctu_can_fd_mode_command_status_settings mode;
        mode.u32 = priv->read_reg(priv, CTU_CAN_FD_MODE);
328

Martin Jeřábek's avatar
Martin Jeřábek committed
329 330 331 332
        if (mode.s.ena) {
            printf("Core is enabled but should be disabled!\n");
        }
    }
333 334 335 336 337

    struct net_device nd;
    nd.can.clock.freq = 100000000;

    struct can_bittiming nom_timing = {
338
        .bitrate = bitrate,
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
    };
    res = can_get_bittiming(&nd, &nom_timing,
                      &ctu_can_fd_bit_timing_max,
                      NULL,
                      0);
    if (res)
        err(res, "can_get_bittiming");
    printf("sample_point .%03d, tq %d, prop %d, seg1 %d, seg2 %d, sjw %d, brp %d, bitrate %d\n",
           nom_timing.sample_point,
           nom_timing.tq,
           nom_timing.prop_seg,
           nom_timing.phase_seg1,
           nom_timing.phase_seg2,
           nom_timing.sjw,
           nom_timing.brp,
           nom_timing.bitrate
    );

357 358 359
    if (!dbitrate)
        dbitrate = 10 * bitrate;

360
    struct can_bittiming data_timing = {
361
        .bitrate = dbitrate,
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
    };
    res = can_get_bittiming(&nd, &data_timing,
                      &ctu_can_fd_bit_timing_data_max,
                      NULL,
                      0);
    if (res)
        err(res, "can_get_bittiming data");
    printf("data sample_point .%03d, tq %d, prop %d, seg1 %d, seg2 %d, sjw %d, brp %d, bitrate %d\n",
           data_timing.sample_point,
           data_timing.tq,
           data_timing.prop_seg,
           data_timing.phase_seg1,
           data_timing.phase_seg2,
           data_timing.sjw,
           data_timing.brp,
           data_timing.bitrate
    );

380 381
    //priv->write_reg(priv, CTU_CAN_FD_INT_MASK_CLR, 0xffff);
    //priv->write_reg(priv, CTU_CAN_FD_INT_ENA_SET, 0xffff);
382
    ctu_can_fd_set_nom_bittiming(priv, &nom_timing);
383
    ctu_can_fd_set_data_bittiming(priv, &data_timing);
384
    //ctu_can_fd_rel_rx_buf(priv);
Martin Jeřábek's avatar
Martin Jeřábek committed
385
    //ctu_can_fd_set_ret_limit(priv, true, 1);
386 387 388 389
    //ctu_can_fd_set_ret_limit(priv, false, 0);
    //ctu_can_fd_abort_tx(priv);
    //ctu_can_fd_txt_set_abort(priv, CTU_CAN_FD_TXT_BUFFER_1);
    //ctu_can_fd_txt_set_empty(priv, CTU_CAN_FD_TXT_BUFFER_1);
390 391 392 393 394 395 396 397

    if (loopback_mode) {
        struct can_ctrlmode mode = {0, 0};
	mode.mask  = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_PRESUME_ACK;
	mode.flags = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_PRESUME_ACK;
        ctu_can_fd_set_mode(priv, &mode);
    }

398 399 400
    ctu_can_fd_enable(priv, true);
    usleep(10000);

401
    printf("MODE=0x%02x\n", priv->read_reg(priv, CTU_CAN_FD_MODE));
402 403 404

    if (do_transmit) {
        struct canfd_frame txf;
405
        txf.can_id = 0x1FF;
406
        txf.flags = 0;
407 408
        //u8 d[] = {0xde, 0xad, 0xbe, 0xef};
        u8 d[] = {0xDE, 0xAD, 0xBE, 0xEF};
409 410 411
        memcpy(txf.data, d, sizeof(d));
        txf.len = sizeof(d);

Martin Jeřábek's avatar
Martin Jeřábek committed
412
        res = ctu_can_fd_insert_frame(priv, &txf, 0, CTU_CAN_FD_TXT_BUFFER_1, false);
413 414 415
        if (!res)
            printf("TX failed\n");
        ctu_can_fd_txt_set_rdy(priv, CTU_CAN_FD_TXT_BUFFER_1);
416
        return 0;
417 418 419 420 421 422 423 424 425 426 427
    }

    while (1) {
        u32 nrxf = ctu_can_fd_get_rx_frame_count(priv);//ctu_can_fd_get_rx_frame_ctr(priv);
        union ctu_can_fd_rx_mem_info reg;
        reg.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_RX_MEM_INFO);
        u32 rxsz = reg.s.rx_buff_size - reg.s.rx_mem_free;

        printf("%u RX frames, %u words", nrxf, rxsz);
        printf(", status 0x%02hhx", ctu_can_fd_read8(priv, CTU_CAN_FD_STATUS));
        printf(", settings 0x%02hhx", ctu_can_fd_read8(priv, CTU_CAN_FD_SETTINGS));
428
        printf(", INT_STAT 0x%04hhx", ctu_can_fd_read16(priv, CTU_CAN_FD_INT_STAT));
429 430 431
        printf(", INT_ENA_SET 0x%04hx", priv->read_reg(priv, CTU_CAN_FD_INT_ENA_SET));
        printf(", INT_MASK_SET 0x%04hx", priv->read_reg(priv, CTU_CAN_FD_INT_MASK_SET));
        printf(", TX_STATUS 0x%04hx", priv->read_reg(priv, CTU_CAN_FD_TX_STATUS));
432
        //printf(", CTU_CAN_FD_ERR_CAPT 0x%02hhx", ctu_can_fd_read8(priv, CTU_CAN_FD_ERR_CAPT));
433
        printf(", TRV_DELAY 0x%0hx", priv->read_reg(priv, CTU_CAN_FD_TRV_DELAY));
434 435

        printf("\n");
436 437 438 439 440 441
        /*
        while (rxsz--) {
            u32 data = priv->read_reg(priv, CTU_CAN_FD_RX_DATA);
            printf("  0x%08x\n", data);
        }
        */
442
        while (nrxf || rxsz) {
443 444 445
            struct canfd_frame cf;
            u64 ts;
            ctu_can_fd_read_rx_frame(priv, &cf, &ts);
446
            printf("%llu: #%x [%u]", ts, cf.can_id, cf.len);
447 448 449
            for (int i=0; i<cf.len; ++i)
                printf(" %02x", cf.data[i]);
            printf("\n");
450 451 452 453 454 455
            rxsz = 0;
            nrxf = ctu_can_fd_get_rx_frame_count(priv);
        }

        if (do_periodic_transmit && (loop_cycle & 1)) {
	    struct canfd_frame txf;
456
	    memset(&txf, 0, sizeof(txf));
457 458 459
            txf.can_id = 0x1FF;
            txf.flags = 0;

460 461 462 463 464 465 466 467 468 469 470 471 472
            if (transmit_fdf) {
                txf.flags |= CANFD_BRS;
                u8 dfd[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee};
                txf.can_id = 0x123;
                memcpy(txf.data, dfd, sizeof(dfd));
                txf.len = sizeof(dfd);
	    } else {
                u8 d[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xEE};
                memcpy(txf.data, d, sizeof(d));
                txf.len = sizeof(d);
	    }

            res = ctu_can_fd_insert_frame(priv, &txf, 0, CTU_CAN_FD_TXT_BUFFER_1, transmit_fdf);
473 474 475 476
            if (!res)
                printf("TX failed\n");
            ctu_can_fd_txt_set_rdy(priv, CTU_CAN_FD_TXT_BUFFER_1);

477
        }
478

479 480
        usleep(1000 * gap);
        loop_cycle++;
481 482 483 484
    }

    return 0;
}