From d4e9f2ef938b40f067cac9c1ae3b82d422c9266a Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 5 Jan 2020 15:23:40 +0100 Subject: e1-prbs-test: Add initial code for checking ICE40-E1 traces The idea of this code is to check captures made by ICE40 against the known transmitted per-timeslots PRBS sequences. Change-Id: I084a9737c9e051dcadf971e39e32f10f89aecfbd --- contrib/e1-prbs-test/Makefile | 7 +- contrib/e1-prbs-test/ice40-rx.c | 233 ++++++++++++++++++++++++++++++++++++++++ contrib/e1-prbs-test/internal.h | 2 +- 3 files changed, 239 insertions(+), 3 deletions(-) create mode 100644 contrib/e1-prbs-test/ice40-rx.c diff --git a/contrib/e1-prbs-test/Makefile b/contrib/e1-prbs-test/Makefile index 726befc..172847f 100644 --- a/contrib/e1-prbs-test/Makefile +++ b/contrib/e1-prbs-test/Makefile @@ -4,13 +4,16 @@ LIBOSMO_LIBS:=$(shell pkg-config --libs libosmocore) CFLAGS=-O2 -g -Wall -Werror $(LIBOSMO_CFLAGS) LIBS=$(LIBOSMO_LIBS) -all: e1-prbs-test +all: e1-prbs-test ice40-e1-prbs-check e1-prbs-test: main.o rx.o tx.o prbs.o utils.o $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) +ice40-e1-prbs-check: ice40-rx.o rx.o prbs.o utils.o + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + %.o: %.c $(CC) $(CFLAGS) -o $@ -c $^ clean: - @rm -f e1-prbs-test e1-prbs-test *.o + @rm -f e1-prbs-test ice40-e1-prbs-check *.o diff --git a/contrib/e1-prbs-test/ice40-rx.c b/contrib/e1-prbs-test/ice40-rx.c new file mode 100644 index 0000000..de0e4f1 --- /dev/null +++ b/contrib/e1-prbs-test/ice40-rx.c @@ -0,0 +1,233 @@ +/* (C) 2019 by Harald Welte + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * 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. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "internal.h" + +static struct test_state g_tst; +static int g_prbs_offs_rx; +static uint8_t g_usb_endpoint = 0x81; + +#define E1_CHUNK_HDR_MAGIC 0xe115600d /* E1 is good */ +struct e1_chunk_hdr { + uint32_t magic; + struct { + uint64_t sec; + uint64_t usec; + } time; + uint16_t len; /* length of following payload */ + uint8_t ep; /* USB endpoint */ +} __attribute__((packed)); + +struct ts_buf { + uint8_t bytes[1024]; +}; +struct line_ts_buf { + struct ts_buf ts_buf[32]; + unsigned int next_offset; +}; +static struct line_ts_buf g_line_ts_buf; + +static int demux_in(struct test_state *tst, const uint8_t *data, size_t len) +{ + int i; + + if (len % 32) + fprintf(stderr, "Length %zu is not multiple of 32\n", len); + + for (i = 0; i < len; i++) { + uint32_t ts_nr = i % 32; + g_line_ts_buf.ts_buf[ts_nr].bytes[g_line_ts_buf.next_offset] = data[i]; + + /* go to next offset in all per-timeslot buffers */ + if (ts_nr == 31) + g_line_ts_buf.next_offset++; + + /* if per-ts buffers are full, hand them to decoder */ + if (g_line_ts_buf.next_offset >= sizeof(g_line_ts_buf.ts_buf[0].bytes)) { + uint8_t j; + for (j = 0; j < 32; j++) { + struct timeslot_state *ts = &tst->ts[j]; + //printf("process_rx(%u, %s)\n", j, osmo_hexdump(g_line_ts_buf.ts_buf[j].bytes, g_line_ts_buf.next_offset)); + process_rx(&ts->rx, j, g_line_ts_buf.ts_buf[j].bytes, g_line_ts_buf.next_offset); + } + memset(&g_line_ts_buf, 0, sizeof(g_line_ts_buf)); + g_line_ts_buf.next_offset = 0; + } + } + return 0; +} + + +static int process_file(struct test_state *tst, int fd) +{ + struct e1_chunk_hdr hdr; + unsigned long offset = 0; + uint8_t buf[65535]; + int rc; + + while (1) { + /* first read header */ + rc = read(fd, &hdr, sizeof(hdr)); + if (rc < 0) + return rc; + if (rc != sizeof(hdr)) { + fprintf(stderr, "%d is less than header size (%zd)\n", rc, sizeof(hdr)); + return -1; + } + offset += rc; + if (hdr.magic != E1_CHUNK_HDR_MAGIC) { + fprintf(stderr, "offset %lu: Wrong magic 0x%08x\n", offset, hdr.magic); + return -1; + } + + /* then read payload */ + rc = read(fd, buf, hdr.len); + if (rc < 0) + return rc; + offset += rc; + if (rc != hdr.len) { + fprintf(stderr, "%d is less than payload size (%d)\n", rc, hdr.len); + return -1; + } + + /* filter on the endpoint (direction) specified by the user */ + if (hdr.ep != g_usb_endpoint) + continue; + + if (hdr.len <= 4) + continue; + + //printf("> %s\n", osmo_hexdump(buf, hdr.len)); + demux_in(tst, buf+4, hdr.len-4); + } +} + +static int open_file(struct test_state *tst, const char *fname) +{ + int i; + for (i = 0; i < 32; i++) { + struct timeslot_state *ts = &tst->ts[i]; + ts->ofd.priv_nr = i; + ts_init_prbs_rx(ts, g_prbs_offs_rx); + } + + return open(fname, O_RDONLY); +} + +static void print_report(void) +{ + struct timespec ts_now; + int i; + + clock_gettime(CLOCK_MONOTONIC, &ts_now); + + for (i = 0; i < ARRAY_SIZE(g_tst.ts); i++) { + const struct timeslot_state *ts = &g_tst.ts[i]; + printf("E1TS(%02u) STATS: sync_losses=%u, bit_errs=%u in %lu seconds\n", + ts->ofd.priv_nr, ts->rx.sync_state.num_sync_loss, ts->rx.sync_state.num_bit_err, + ts_now.tv_sec - ts->rx.sync_state.ts_sync.tv_sec); + } +} + +static void sig_handler(int signal) +{ + switch (signal) { + case SIGINT: + print_report(); + exit(0); + break; + case SIGHUP: + print_report(); + break; + } +} + +static void handle_options(int argc, char **argv) +{ + while (1) { + int c; + static const struct option long_opts[] = { + { "rx-prbs-offset", 1, 0, 'r' }, + { "endpoint", 1, 0, 'e' }, + { 0, 0, 0, 0 } + }; + c = getopt_long(argc, argv, "r:e:", long_opts, NULL); + if (c == -1) + break; + + switch (c) { + case 'r': + g_prbs_offs_rx = atoi(optarg); + break; + case 'e': + g_usb_endpoint = strtoul(optarg, NULL, 16); + break; + default: + exit(1); + } + } +} + +int main(int argc, char **argv) +{ + char *fname; + int rc; + + handle_options(argc, argv); + + if (argc <= optind) { + fprintf(stderr, "You must specify the file name of the ICE40-E1 capture\n"); + exit(1); + } + fname = argv[optind]; + + signal(SIGINT, sig_handler); + signal(SIGHUP, sig_handler); + + rc = open_file(&g_tst, fname); + if (rc < 0) { + fprintf(stderr, "Error opening %s: %s\n", fname, strerror(errno)); + exit(1); + } + process_file(&g_tst, rc); + print_report(); +} diff --git a/contrib/e1-prbs-test/internal.h b/contrib/e1-prbs-test/internal.h index 681648a..a2c4ef5 100644 --- a/contrib/e1-prbs-test/internal.h +++ b/contrib/e1-prbs-test/internal.h @@ -4,7 +4,7 @@ #include #include -#define MAX_NR_TS 31 +#define MAX_NR_TS 32 #define PRBS_LEN 2048 /* prbs.c */ -- cgit v1.2.3