Commit e4e825e3 authored by Joanne Hugé's avatar Joanne Hugé Committed by Joanne Hugé

Reorganize files

parent fbb32c56
test-eNB/client
test-eNB/server
private
*.swp
*.swo
*.so
......
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2010-2014 Intel Corporation
CC=gcc -m64 -msse4.1
CXX=g++ -m64 -msse4.1
CFLAGS=-O2 -fno-strict-aliasing -Wall -pedantic
CC=gcc -m64 -mavx2
CXX=g++ -m64 -mavx2
CFLAGS=-O3 -fno-strict-aliasing -Wall -pedantic -g
CFLAGS+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
CFLAGS +=-DALLOW_EXPERIMENTAL_API
CFLAGS+=-MMD -g
......
USER = af_xdp
LIBBPF = ${HOME}/ecpri/xdp/libbpf-build/usr
SRCDIR = "."
SRCS = $(USER).c
OBJS = $(SRCS:%.c=%.o)
ifeq ($(DEBUG),)
CFLAGS = -O2
else
CFLAGS = -Og -g -Wall -Wextra
endif
CFLAGS += -MD -MP
CFLAGS += -I $(SRCDIR)
CFLAGS += -std=gnu99
LDFLAGS += -pthread -L$(LIBBPF)/lib64 -l:libbpf.a -lelf -lz
CFLAGS += -I $(LIBBPF)/include
vpath %.c $(SRCDIR)
$(USER): $(OBJS)
$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $@
-include $(subst .c,.d,$(SRCS))
clean:
$(RM) -rf bin
$(RM) $(OBJS) $(USER) $(subst .c,.d,$(SRCS))
.PHONY: clean
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <getopt.h>
#include <ifaddrs.h>
#include <inttypes.h>
#include <limits.h>
#include <linux/errqueue.h>
#include <linux/ethtool.h>
#include <linux/net_tstamp.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <poll.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <bpf/xsk.h>
#include <linux/if_ether.h>
#include <linux/if_link.h>
#include <linux/if_xdp.h>
#include <linux/ip.h>
#include <linux/udp.h>
#define err(...) \
do { \
fprintf(stderr, __VA_ARGS__); \
exit(EXIT_FAILURE); \
} while (0)
#define err_errno(...) error(EXIT_FAILURE, errno, __VA_ARGS__);
#define ETH_FCS_SIZE 4
#define FRAME_SIZE XSK_UMEM__DEFAULT_FRAME_SIZE
#define PACKET_SIZE 262
#define BUFFER_SIZE 4096
#define NB_PACKETS 2000000
#define BATCH_SIZE 256
//#define PRINT_PROGRESS
#define PRINT_PROGRESS_INTERVAL 1000
#define ACTIVE_POLLING
//#define DEBUG
static void log_info(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " INFO [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
puts(line);
}
#ifdef DEBUG
static void log_debug(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " DEBUG [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
puts(line);
}
#else
#define log_debug(...)
#endif
struct xsk_umem_info {
struct xsk_ring_prod fq;
struct xsk_ring_cons cq;
struct xsk_umem *umem;
void *buffer;
};
struct xdpsock {
struct xsk_ring_cons rx;
struct xsk_ring_prod tx;
struct xsk_umem_info umem;
struct xsk_socket *xsk;
int fd;
};
// Timestamps utils
#define NSEC_PER_SEC INT64_C(1000000000)
static struct timespec int_to_ts(int64_t t) {
struct timespec ts;
ts.tv_sec = t / NSEC_PER_SEC;
ts.tv_nsec = t - (ts.tv_sec * NSEC_PER_SEC);
return ts;
}
static int64_t ts_to_int(struct timespec ts) {
return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
}
static void add_ns(struct timespec *t, int64_t ns) {
t->tv_nsec += ns;
while (t->tv_nsec >= ((int64_t)NSEC_PER_SEC)) {
t->tv_sec += 1;
t->tv_nsec -= NSEC_PER_SEC;
}
}
static int64_t calcdiff_ns(struct timespec t1, struct timespec t2) {
int64_t diff;
diff = NSEC_PER_SEC * ((int)t1.tv_sec - (int)t2.tv_sec);
diff += ((int)t1.tv_nsec - (int)t2.tv_nsec);
return diff;
}
static void init_xdp_recv(char * network_if);
static void init_xdp_send(char * network_if);
static int recv_xdp_packet(void);
static void init_recv(void);
static void send_xdp_packet(void);
static void recv_xdp_cleanup(void);
static void close_xdp_socket(void);
static uint8_t pkt_data[FRAME_SIZE];
static struct xdpsock send_xdp_socket;
static struct xdpsock recv_xdp_socket;
static int received;
static int64_t sent;
static uint32_t idx_rx = 0, idx_recv;
static int batch_size = BATCH_SIZE;
int trace_fd;
int main() {
char * network_if = "ens5f0np0";
log_info("", "Starting");
init_xdp_send(network_if);
//init_xdp_recv(network_if);
return 0;
}
void trace_write(const char *fmt, ...)
{
va_list ap;
char buf[256];
int n;
if (trace_fd < 0)
return;
va_start(ap, fmt);
n = vsnprintf(buf, 256, fmt, ap);
va_end(ap);
write(trace_fd, buf, n);
}
static int latency_target_fd = -1;
static int32_t latency_target_value = 0;
void set_latency_target(void) {
struct stat s;
int err;
errno = 0;
err = stat("/dev/cpu_dma_latency", &s);
if (err == -1) {
error(EXIT_FAILURE, errno, "WARN: stat /dev/cpu_dma_latency failed");
return;
}
errno = 0;
latency_target_fd = open("/dev/cpu_dma_latency", O_RDWR);
if (latency_target_fd == -1) {
error(EXIT_FAILURE, errno, "WARN: open /dev/cpu_dma_latency");
return;
}
errno = 0;
err = write(latency_target_fd, &latency_target_value, 4);
if (err < 1) {
error(EXIT_FAILURE, errno, "# error setting cpu_dma_latency to %d!",
latency_target_value);
close(latency_target_fd);
return;
}
printf("# /dev/cpu_dma_latency set to %dus\n", latency_target_value);
}
static void init_xdp_send(char * network_if) {
uint32_t idx;
int ret, i, xsks_map = 0;
struct xsk_socket_config xsk_cfg;
struct xsk_umem_config cfg = {
.fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
.comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS,
.frame_size = FRAME_SIZE,
.frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM,
.flags = 0,
};
void *buffer = NULL;
struct ethhdr *eth_hdr = (struct ethhdr *)pkt_data;
//set_latency_target();
if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
fprintf(stderr, "mlockall failed");
exit(EXIT_FAILURE);
}
for(int j = 0; j < PACKET_SIZE; j++)
pkt_data[j] = 0x17;
memcpy(eth_hdr->h_dest, "\xb8\x59\x9f\x07\x7d\xdb", ETH_ALEN);
memcpy(eth_hdr->h_source, "\x04\x09\xa5\x0f\x9f\x4c", ETH_ALEN);
eth_hdr->h_proto = htons(ETH_P_IP);
//for(int j = 0; j < PACKET_SIZE; j++)
// printf("%x", pkt_data[j]);
//printf("\n");
//exit(EXIT_SUCCESS);
log_debug("", "posix_memalign");
/* Allocate user space memory for xdp frames */
ret = posix_memalign(&buffer, sysconf(_SC_PAGE_SIZE), BUFFER_SIZE * FRAME_SIZE);
if (ret) err_errno("posix_memalign() failed");
log_debug("", "xsk_umem__create");
ret = xsk_umem__create(&send_xdp_socket.umem.umem, buffer, BUFFER_SIZE * FRAME_SIZE,
&send_xdp_socket.umem.fq, &send_xdp_socket.umem.cq, &cfg);
if (ret) err("xsk_umem__create() failed");
send_xdp_socket.umem.buffer = buffer;
log_debug("", "open_xdp_socket");
/* Create XDP socket */
xsk_cfg.rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
xsk_cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
xsk_cfg.libbpf_flags = 0;
xsk_cfg.xdp_flags = XDP_FLAGS_DRV_MODE;
xsk_cfg.bind_flags = XDP_USE_NEED_WAKEUP;
log_debug("", "xsk_socket__create");
ret = xsk_socket__create(&send_xdp_socket.xsk, network_if, 0, send_xdp_socket.umem.umem,
&send_xdp_socket.rx, &send_xdp_socket.tx, &xsk_cfg);
log_debug("", "send_xdp_packet");
send_xdp_packet();
}
static void init_xdp_recv(char * network_if) {
uint32_t idx;
int ret, i, xsks_map = 0;
struct xsk_socket_config xsk_cfg;
struct xsk_umem_config cfg = {
.fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
.comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS,
.frame_size = FRAME_SIZE,
.frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM,
.flags = 0,
};
void *buffer = NULL;
sent = 0;
log_debug("", "posix_memalign");
/* Allocate user space memory for xdp frames */
ret = posix_memalign(&buffer, sysconf(_SC_PAGE_SIZE), BUFFER_SIZE * FRAME_SIZE);
if (ret) err_errno("posix_memalign() failed");
log_debug("", "xsk_umem__create");
ret = xsk_umem__create(&recv_xdp_socket.umem.umem, buffer, BUFFER_SIZE * FRAME_SIZE,
&recv_xdp_socket.umem.fq, &recv_xdp_socket.umem.cq, &cfg);
if (ret) err("xsk_umem__create() failed");
recv_xdp_socket.umem.buffer = buffer;
log_debug("", "open_xdp_socket");
/* Create XDP socket */
xsk_cfg.rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
xsk_cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
xsk_cfg.libbpf_flags = 0;
xsk_cfg.xdp_flags = XDP_FLAGS_DRV_MODE;
xsk_cfg.bind_flags = 0;
log_debug("", "xsk_socket__create");
ret = xsk_socket__create(&recv_xdp_socket.xsk, network_if, 0, recv_xdp_socket.umem.umem,
&recv_xdp_socket.rx, &recv_xdp_socket.tx, &xsk_cfg);
log_debug("", "recv_xdp_packet");
recv_xdp_packet();
}
static void complete_tx_only(void) {
int rcvd;
uint32_t idx;
rcvd = xsk_ring_cons__peek(&send_xdp_socket.umem.cq, batch_size, &idx);
if (rcvd > 0) {
xsk_ring_cons__release(&send_xdp_socket.umem.cq, rcvd);
sent += rcvd;
}
}
static void send_xdp_packet(void) {
struct pollfd fds[1] = {};
int i, ret;
struct timespec start, end;
int64_t duration_ns;
#ifdef PRINT_PROGRESS
int64_t progress = PRINT_PROGRESS_INTERVAL;
#endif
fds[0].fd = xsk_socket__fd(send_xdp_socket.xsk);
fds[0].events = POLLOUT;
for (int i = 0; i < BUFFER_SIZE; i++)
memcpy(xsk_umem__get_data(send_xdp_socket.umem.buffer, i * FRAME_SIZE), pkt_data, PACKET_SIZE - ETH_FCS_SIZE);
clock_gettime(CLOCK_MONOTONIC, &start);
for(int l = 0; (sent < NB_PACKETS); l++) {
uint32_t idx;
#ifdef PRINT_PROGRESS
if(sent > progress) {
printf("%" PRIi64 "\n", sent);
progress += PRINT_PROGRESS_INTERVAL;
}
#endif
#ifdef ACTIVE_POLLING
ret = poll(fds, 1, 0);
if ((ret <= 0) || !(fds[0].revents & POLLOUT)) {
complete_tx_only();
continue;
}
#else
ret = poll(fds, 1, 100);
if ((ret <= 0) || !(fds[0].revents & POLLOUT)) {
complete_tx_only();
continue;
}
#endif
while(xsk_ring_prod__reserve(&send_xdp_socket.tx, batch_size, &idx) < batch_size)
complete_tx_only();
for (int k = 0; k < batch_size; k++) {
struct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&send_xdp_socket.tx, idx + k);
tx_desc->addr = k * (FRAME_SIZE);
tx_desc->len = PACKET_SIZE - ETH_FCS_SIZE;
}
xsk_ring_prod__submit(&send_xdp_socket.tx, batch_size);
if (xsk_ring_prod__needs_wakeup(&send_xdp_socket.tx))
sendto(xsk_socket__fd(send_xdp_socket.xsk), NULL, 0, MSG_DONTWAIT, NULL, 0);
}
clock_gettime(CLOCK_MONOTONIC, &end);
duration_ns = calcdiff_ns(end, start);
log_info("STATS", "Duration: %" PRIi64 " us", duration_ns / 1000);
log_info("STATS", "Packets sent: %" PRIi64 " / %" PRIi64, sent, NB_PACKETS);
log_info("STATS", "pps: %" PRIi64, (sent * NSEC_PER_SEC) / duration_ns);
}
static int recv_xdp_packet(void) {
int ret;
static struct pollfd fds_recv[1] = {0};
fds_recv[0].fd = xsk_socket__fd(recv_xdp_socket.xsk);
fds_recv[0].events = POLLIN;
/* Add some buffers in fill ring */
log_debug("", "xsk_ring_prod__reserve");
ret = xsk_ring_prod__reserve(&recv_xdp_socket.umem.fq,
XSK_RING_PROD__DEFAULT_NUM_DESCS, &idx_recv);
if (ret != XSK_RING_PROD__DEFAULT_NUM_DESCS)
err("xsk_ring_prod__reserve() failed");
log_debug("", "xsk_ring_prod__fill_addr");
for (int i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; i++)
*xsk_ring_prod__fill_addr(&recv_xdp_socket.umem.fq, idx_recv++) = i * FRAME_SIZE;
log_debug("", "xsk_ring_prod__submit");
xsk_ring_prod__submit(&recv_xdp_socket.umem.fq, XSK_RING_PROD__DEFAULT_NUM_DESCS);
ret = poll(fds_recv, 1, -1);
if (ret != 1)
error(EXIT_FAILURE, errno, "poll failed");
log_info("", "xsk_ring_cons__peek");
received = xsk_ring_cons__peek(&recv_xdp_socket.rx, 1, &idx_rx);
log_info("", "Received packet ! (%d)", received);
if (received != 1)
error(EXIT_FAILURE, errno, "Received %d packets", received);
return 0;
}
static void recv_xdp_cleanup(void) {
uint64_t addr;
int ret;
/* Cleanup */
xsk_ring_cons__release(&recv_xdp_socket.rx, received);
/* Add that particular buffer back to the fill queue */
if (xsk_prod_nb_free(&recv_xdp_socket.umem.fq, received)) {
ret = xsk_ring_prod__reserve(&recv_xdp_socket.umem.fq, received, &idx_recv);
if (ret != received) err("xsk_ring_prod__reserve() failed");
*xsk_ring_prod__fill_addr(&recv_xdp_socket.umem.fq, idx_recv) =
xsk_umem__extract_addr(addr);
xsk_ring_prod__submit(&recv_xdp_socket.umem.fq, received);
}
}
static void close_xdp_socket(void) {
xsk_socket__delete(send_xdp_socket.xsk);
xsk_socket__delete(recv_xdp_socket.xsk);
xsk_umem__delete(send_xdp_socket.umem.umem);
xsk_umem__delete(recv_xdp_socket.umem.umem);
}
#!/bin/bash
set -e
xdp_off() {
ip link set dev ens9f1np1 xdp off
}
tx_packets() {
ethtool -S ens9f1np1|grep -v ": 0"|grep "tx_packets:"|awk '{print $2;}';
}
xdp_off;
make clean && make;
A=$(tx_packets)
nice -n -20 chrt 99 ./af_xdp;
B=$(tx_packets)
echo "Ethtool tx packets sent: $((B -A))";
xdp_off;
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2010-2014 Intel Corporation
CC=gcc -m64 -msse4.1
CXX=g++ -m64 -msse4.1
CFLAGS=-O2 -fno-strict-aliasing
CFLAGS+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
CFLAGS+=-MMD -g
CXXFLAGS=$(CFLAGS)
# binary name
APP = dpdk-recv-send
# all source are stored in SRCS-y
SRCS-y := $(APP).c
PKGCONF ?= pkg-config
# Build using pkg-config variables if possible
ifneq ($(shell $(PKGCONF) --exists libdpdk && echo 0),0)
$(error "no installation of DPDK found")
endif
all: shared
.PHONY: shared
shared: build/$(APP)-shared
ln -sf $(APP)-shared build/$(APP)
dpdk-recv-send.so: dpdk-recv-send.o
$(CC) -shared $(LDFLAGS) $(LDFLAGS_SHARED) -o $@ $<
PC_FILE := $(shell $(PKGCONF) --path libdpdk 2>/dev/null)
CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk)
LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk) -lpthread -lm
CFLAGS += -DALLOW_EXPERIMENTAL_API
build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build
$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)
%.o: %.c
$(CC) $(CFLAGS) $(SRCS-y) -fpic -c -o $@
build:
@mkdir -p $@
.PHONY: clean
clean:
rm -rf build
rm -f *.o *.d *.so
//#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <getopt.h>
#include <immintrin.h>
#include <inttypes.h>
#include <limits.h>
#include <linux/if_packet.h>
#include <math.h>
#include <netdb.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <net/if.h>
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_ether.h>
#include <rte_cycles.h>
#include <rte_lcore.h>
#include <rte_mbuf.h>
#include <rte_ether.h>
#include <rte_ip.h>
#include <rte_udp.h>
static void log_error(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " ERROR [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
puts(line);
exit(EXIT_FAILURE);
}
static void log_info(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " INFO [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
puts(line);
}
// Timestamps utils
#define NSEC_PER_SEC INT64_C(1000000000)
static struct timespec int_to_ts(int64_t t) {
struct timespec ts;
ts.tv_sec = t / NSEC_PER_SEC;
ts.tv_nsec = t - (ts.tv_sec * NSEC_PER_SEC);
return ts;
}
static int64_t ts_to_int(struct timespec ts) {
return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
}
static void add_ns(struct timespec *t, int64_t ns) {
t->tv_nsec += ns;
while (t->tv_nsec >= ((int64_t)NSEC_PER_SEC)) {
t->tv_sec += 1;
t->tv_nsec -= NSEC_PER_SEC;
}
}
static int64_t calcdiff_ns(struct timespec t1, struct timespec t2) {
int64_t diff;
diff = NSEC_PER_SEC * ((int)t1.tv_sec - (int)t2.tv_sec);
diff += ((int)t1.tv_nsec - (int)t2.tv_nsec);
return diff;
}
#define DATA_SIZE 244
#define BURST_SIZE 16
#define MEMPOOL_CACHE_SIZE 256
#define RTE_TEST_RX_DESC_DEFAULT 1024
#define RTE_TEST_TX_DESC_DEFAULT 1024
static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
struct rte_mempool *mbuf_pool;
struct rte_ether_addr s_addr = {{0xb8,0xce,0xf6,0x4b,0x00,0x22}};
struct rte_ether_addr d_addr = {{0xb8,0xce,0xf6,0x4b,0x00,0x23}};
int8_t data[BURST_SIZE][DATA_SIZE];
static const struct rte_eth_conf port_conf_default = {
.rxmode = { .max_rx_pkt_len = RTE_ETHER_MAX_LEN }
};
static inline int port_init(int portid, struct rte_mempool *mbuf_pool) {
struct rte_eth_conf port_conf = port_conf_default;
const uint16_t rx_rings = 1, tx_rings = 1;
int retval;
uint16_t q;
int ret;
retval = rte_eth_dev_configure(portid, rx_rings, tx_rings, &port_conf);
if (retval != 0)
return retval;
/* Allocate and set up 1 RX queue per Ethernet port. */
for (q = 0; q < rx_rings; q++) {
retval = rte_eth_rx_queue_setup(portid, q, nb_rxd,
rte_eth_dev_socket_id(portid), NULL, mbuf_pool);
if (retval < 0)
return retval;
}
/* Allocate and set up 1 TX queue per Ethernet port. */
for (q = 0; q < tx_rings; q++) {
retval = rte_eth_tx_queue_setup(portid, q, nb_txd,
rte_eth_dev_socket_id(portid), NULL);
if (retval < 0)
return retval;
}
/* Start the Ethernet port. */
retval = rte_eth_dev_start(portid);
if (retval < 0)
return retval;
return 0;
}
static void init_dpdk(int argc, char ** argv) {
unsigned int nb_mbufs;
int ret;
ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_exit(EXIT_FAILURE, "initlize fail!");
argc -= ret;
argv += ret;
nb_mbufs = RTE_MAX((nb_rxd + nb_txd + BURST_SIZE + MEMPOOL_CACHE_SIZE), 8192U);
mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", nb_mbufs,
MEMPOOL_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
if (mbuf_pool == NULL)
rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
if (port_init(0, mbuf_pool) != 0)
rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n", 0);
if (port_init(1, mbuf_pool) != 0)
rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n", 1);
}
static void send_packets(int port) {
struct rte_mbuf * pkt[BURST_SIZE];
struct rte_ether_hdr *eth_hdr;
for(int i = 0; i < BURST_SIZE; i++) {
int pkt_size;
pkt[i] = rte_pktmbuf_alloc(mbuf_pool);
eth_hdr = rte_pktmbuf_mtod(pkt[i], struct rte_ether_hdr*);
if(port) {
eth_hdr->d_addr = s_addr;
eth_hdr->s_addr = d_addr;
} else {
eth_hdr->d_addr = d_addr;
eth_hdr->s_addr = s_addr;
}
eth_hdr->ether_type = 0x0a00;
memcpy(rte_pktmbuf_mtod_offset(pkt[i], uint8_t *, sizeof(struct rte_ether_hdr)), data[i], DATA_SIZE);
pkt_size = DATA_SIZE + sizeof(struct rte_ether_hdr);
pkt[i]->data_len = pkt_size;
pkt[i]->pkt_len = pkt_size;
}
const uint16_t nb_tx = rte_eth_tx_burst(port, 0, pkt, BURST_SIZE);
/* Free any unsent packets. */
if (unlikely(nb_tx < BURST_SIZE)) {
uint16_t buf;
for (buf = nb_tx; buf < BURST_SIZE; buf++)
rte_pktmbuf_free(pkt[buf]);
fprintf(stderr, "Sent %d packets instead of %d\n", nb_tx, BURST_SIZE);
exit(EXIT_FAILURE);
}
}
static int recv_packets(int port) {
struct rte_mbuf * pkt[BURST_SIZE];
//if(port == 0) log_info("PORT0", "Init");
while(1) {
//if(port == 0) log_info("PORT0", "Burst begin");
const int nb_rx = rte_eth_rx_burst(port, 0, pkt, BURST_SIZE);
//if(port == 0) log_info("PORT0", "Burst end");
for(int i = 0; i < nb_rx; i++)
rte_pktmbuf_free(pkt[i]);
if(nb_rx)
return nb_rx;
}
}
#define FRAME_FREQ INT64_C(3840000)
#define BURST_N ((FRAME_FREQ / 1000) / BURST_SIZE)
#define PROGRESS_RATE 1000000
void * send_thread(void * p) {
int * port = (int *) p;
int64_t seq_id = 0;
struct timespec next, initial, current;
cpu_set_t mask;
// Set thread CPU affinity
CPU_ZERO(&mask);
CPU_SET(37 + *port, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask))
error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU %d\n", 38);
log_info("SEND_THREAD", "Init %d", *port);
for(int i = 0; i < BURST_SIZE; i++) {
for(int j = 0; j < DATA_SIZE; j++) {
data[i][j] = 0xfe;
}
}
int64_t progress = PROGRESS_RATE;
clock_gettime(CLOCK_MONOTONIC, &initial);
next = initial;
for(int64_t sent = 0;; sent += (BURST_SIZE * BURST_N)) {
// Multiply by i everytime to prevent any frequence drift
add_ns(&next, 1000000);
if(sent > progress) {
clock_gettime(CLOCK_MONOTONIC, &current);
int64_t duration_ns = calcdiff_ns(current, initial);
progress += PROGRESS_RATE;
printf("SEND%d %" PRIi64 " (pps: %" PRIi64 ")\n", *port, sent, (sent * NSEC_PER_SEC) / duration_ns);
}
for(int64_t k = 0; k < BURST_N ; k++) {
for(int j = 0; j < BURST_SIZE; j++)
*(((int64_t *) data[j]) + 2) = seq_id++;
send_packets(*port);
}
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
}
pthread_exit(EXIT_SUCCESS);
}
void * recv_thread(void * p) {
int * port = (int *) p;
int64_t progress = PROGRESS_RATE;
cpu_set_t mask;
// Set thread CPU affinity
CPU_ZERO(&mask);
CPU_SET(35 + *port, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask))
error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU %d\n", 37);
log_info("RECV_THREAD", "Init %d", *port);
for(int64_t received = 0;;) {
if(received > progress) {
progress += PROGRESS_RATE;
printf("RECV%d %" PRIi64 "\n", *port, received);
}
received += recv_packets(*port);
}
pthread_exit(EXIT_SUCCESS);
}
int port0 = 0;
int port1 = 1;
int main(int argc, char **argv)
{
pthread_t recv_pthread;
pthread_t send_pthread;
struct sched_param recv_param;
struct sched_param send_param;
pthread_attr_t recv_attr;
pthread_attr_t send_attr;
log_info("", "Starting threads");
// Initialize pthread attributes (default values)
if (pthread_attr_init(&recv_attr))
log_error("TRX_ECPRI", "init pthread attributes failed\n");
// Set a specific stack size
if (pthread_attr_setstacksize(&recv_attr, PTHREAD_STACK_MIN))
log_error("TRX_ECPRI", "pthread setstacksize failed\n");
// Set scheduler policy and priority of pthread
if (pthread_attr_setschedpolicy(&recv_attr, SCHED_FIFO))
log_error("TRX_ECPRI", "pthread setschedpolicy failed\n");
recv_param.sched_priority = 97;
if (pthread_attr_setschedparam(&recv_attr, &recv_param))
log_error("TRX_ECPRI", "pthread setschedparam failed\n");
/* Use scheduling parameters of attr */
if (pthread_attr_setinheritsched(&recv_attr, PTHREAD_EXPLICIT_SCHED))
log_error("TRX_ECPRI", "pthread setinheritsched failed\n");
if (pthread_attr_init(&send_attr))
log_error("TRX_ECPRI", "init pthread attributes failed\n");
if (pthread_attr_setstacksize(&send_attr, PTHREAD_STACK_MIN))
log_error("TRX_ECPRI", "pthread setstacksize failed\n");
if (pthread_attr_setschedpolicy(&send_attr, SCHED_FIFO))
log_error("TRX_ECPRI", "pthread setschedpolicy failed\n");
send_param.sched_priority = 97;
if (pthread_attr_setschedparam(&send_attr, &send_param))
log_error("TRX_ECPRI", "pthread setschedparam failed\n");
if (pthread_attr_setinheritsched(&send_attr, PTHREAD_EXPLICIT_SCHED))
log_error("TRX_ECPRI", "pthread setinheritsched failed\n");
init_dpdk(argc, argv);
if (pthread_create(&send_pthread, NULL, send_thread, &port0))
error(EXIT_FAILURE, errno, "Couldn't create send thread");
//if (pthread_create(&send_pthread, NULL, send_thread, &port1))
//error(EXIT_FAILURE, errno, "Couldn't create send thread");
if (pthread_create(&recv_pthread, NULL, recv_thread, &port0))
error(EXIT_FAILURE, errno, "Couldn't create recv thread");
if (pthread_create(&recv_pthread, NULL, recv_thread, &port1))
error(EXIT_FAILURE, errno, "Couldn't create recv thread");
for(;;) {
sleep(1);
}
return 0;
}
#!/bin/bash
set -e
tx_packets() {
ethtool -S ens9f1np1|grep -v ": 0"|grep "tx_packets:"|awk '{print $2;}';
}
make;
#A=$(tx_packets)
nice -n -20 chrt 99 build/dpdk-recv-send -l 28 -b 0000:04:00.0 -b 0000:5e:00.0 -b 0000:5e:00.1;
#B=$(tx_packets)
#echo "Ethtool tx packets sent: $((B -A))";
CC=gcc
CFLAGS=-O2
CFLAGS+=-g
PROG=raw_socket
OBJ=$(PROG).o
all: $(PROG)
clean:
rm -f $(PROG) *.o *~
$(PROG): $(OBJ)
$(CC) $(LDFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $^
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <getopt.h>
#include <immintrin.h>
#include <inttypes.h>
#include <limits.h>
#include <linux/if_packet.h>
#include <math.h>
#include <netdb.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <net/if.h>
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
static void log_error(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " ERROR [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
puts(line);
exit(EXIT_FAILURE);
}
static void log_info(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " INFO [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
puts(line);
}
static int latency_target_fd = -1;
static int32_t latency_target_value = 0;
void set_latency_target(void) {
struct stat s;
int err;
errno = 0;
err = stat("/dev/cpu_dma_latency", &s);
if (err == -1) {
error(EXIT_FAILURE, errno, "WARN: stat /dev/cpu_dma_latency failed");
return;
}
errno = 0;
latency_target_fd = open("/dev/cpu_dma_latency", O_RDWR);
if (latency_target_fd == -1) {
error(EXIT_FAILURE, errno, "WARN: open /dev/cpu_dma_latency");
return;
}
errno = 0;
err = write(latency_target_fd, &latency_target_value, 4);
if (err < 1) {
error(EXIT_FAILURE, errno, "# error setting cpu_dma_latency to %d!",
latency_target_value);
close(latency_target_fd);
return;
}
printf("# /dev/cpu_dma_latency set to %dus\n", latency_target_value);
}
// Counters
static volatile int64_t sent_frame_count;
// Network
static int send_sockfd;
static struct sockaddr_ll connect_sk_addr;
// Timestamps utils
#define NSEC_PER_SEC INT64_C(1000000000)
static struct timespec int_to_ts(int64_t t) {
struct timespec ts;
ts.tv_sec = t / NSEC_PER_SEC;
ts.tv_nsec = t - (ts.tv_sec * NSEC_PER_SEC);
return ts;
}
static int64_t ts_to_int(struct timespec ts) {
return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
}
static void add_ns(struct timespec *t, int64_t ns) {
t->tv_nsec += ns;
while (t->tv_nsec >= ((int64_t)NSEC_PER_SEC)) {
t->tv_sec += 1;
t->tv_nsec -= NSEC_PER_SEC;
}
}
static int64_t calcdiff_ns(struct timespec t1, struct timespec t2) {
int64_t diff;
diff = NSEC_PER_SEC * ((int)t1.tv_sec - (int)t2.tv_sec);
diff += ((int)t1.tv_nsec - (int)t2.tv_nsec);
return diff;
}
#define PACKET_SIZE 262
#define NB_PACKETS 2000000
int main() {
uint8_t dst_mac[6] = "\xb8\x59\x9f\x07\x7d\xdb";
uint8_t src_mac[6] = "\x04\x09\xa5\x0f\x9f\x4c";
char * network_if = "ens9f1np1";
uint8_t packet[PACKET_SIZE];
uint8_t buffer[PACKET_SIZE * 1024];
struct ether_header *eh = (struct ether_header *) packet;
int if_index;
struct timespec initial, end;
int64_t sent;
int64_t duration_ns;
int k = 0;
struct mmsghdr msgh[1024];
struct iovec msgv[1024];
//set_latency_target();
memset((uint8_t *) packet, 0, PACKET_SIZE);
if (!(if_index = if_nametoindex(network_if))) {
perror("if_nametoindex");
return 1;
}
if ((send_sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) {
perror("Socket Error");
return 1;
}
connect_sk_addr.sll_ifindex = if_index;
connect_sk_addr.sll_halen = ETH_ALEN;
for(int i = 0; i < 6; i++)
connect_sk_addr.sll_addr[i] = dst_mac[i];
for(int i = 0; i < 6; i++)
eh->ether_shost[i] = src_mac[i];
for(int i = 0; i < 6; i++)
eh->ether_dhost[i] = dst_mac[i];
/* Ethertype field */
eh->ether_type = htons(0xaefe);
for(int i = 0; i < 1024; i++)
memcpy(buffer + (i * PACKET_SIZE), packet, PACKET_SIZE);
memset(msgv, 0, sizeof(msgv));
memset(msgh, 0, sizeof(msgh));
for(int j = 0; j < 1024; j++) {
msgh[j].msg_hdr.msg_name = &connect_sk_addr;
msgh[j].msg_hdr.msg_namelen = sizeof(connect_sk_addr);
msgh[j].msg_hdr.msg_iov = &msgv[j];
msgh[j].msg_hdr.msg_iovlen = 1;
}
for(int j = 0; j < 1024; j++) {
msgv[j].iov_base = buffer + (j * PACKET_SIZE);
msgv[j].iov_len = PACKET_SIZE;
}
clock_gettime(CLOCK_TAI, &initial);
for(int i = 0; sent < NB_PACKETS; i++) {
int ret = sendmmsg(send_sockfd, msgh, 1024, 0);
if(ret < 1024)
error(EXIT_FAILURE, errno, "sendmmsg error (returned %d)", ret);
sent += ret;
}
clock_gettime(CLOCK_TAI, &end);
duration_ns = calcdiff_ns(end, initial);
log_info("STATS", "Duration: %" PRIi64 " us", duration_ns / 1000);
log_info("STATS", "Packets sent: %" PRIi64 " / %" PRIi64, sent, NB_PACKETS);
log_info("STATS", "pps: %" PRIi64, (sent * NSEC_PER_SEC) / duration_ns);
return 0;
}
#!/bin/bash
set -e
tx_packets() {
ethtool -S ens9f1np1|grep -v ": 0"|grep "tx_packets:"|awk '{print $2;}';
}
make clean && make;
A=$(tx_packets)
nice -n -20 chrt 99 ./raw_socket;
B=$(tx_packets)
echo "Ethtool tx packets sent: $((B -A))";
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <getopt.h>
#include <immintrin.h>
#include <inttypes.h>
#include <limits.h>
#include <linux/if_packet.h>
#include <math.h>
#include <netdb.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <net/if.h>
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "trx_driver.h"
#define DEBUG
#define SSE4 /* define if CPU supports SSE4.1 */
#include "private.c"
/* eCPRI Send and Recv */
#define PACKET_SIZE 262
#define FRAME_FREQ INT64_C(3840000)
#define SEND_LIMIT 1250
#define TRX_WB_MAX_PARTS 1000
#define TRX_BUF_MAX_SIZE 1000
static void log_error(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " ERROR [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
puts(line);
exit(EXIT_FAILURE);
}
static void log_info(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " INFO [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
puts(line);
}
#ifdef DEBUG
static void log_debug(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " DEBUG [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
puts(line);
}
#else
#define log_debug(...)
#endif
static int latency_target_fd = -1;
static int32_t latency_target_value = 0;
/* Latency trick
* if the file /dev/cpu_dma_latency exists,
* open it and write a zero into it. This will tell
* the power management system not to transition to
* a high cstate (in fact, the system acts like idle=poll)
* When the fd to /dev/cpu_dma_latency is closed, the behavior
* goes back to the system default.
*
* Documentation/power/pm_qos_interface.txt
*/
void set_latency_target(void) {
struct stat s;
int err;
errno = 0;
err = stat("/dev/cpu_dma_latency", &s);
if (err == -1) {
error(EXIT_FAILURE, errno, "WARN: stat /dev/cpu_dma_latency failed");
return;
}
errno = 0;
latency_target_fd = open("/dev/cpu_dma_latency", O_RDWR);
if (latency_target_fd == -1) {
error(EXIT_FAILURE, errno, "WARN: open /dev/cpu_dma_latency");
return;
}
errno = 0;
err = write(latency_target_fd, &latency_target_value, 4);
if (err < 1) {
error(EXIT_FAILURE, errno, "# error setting cpu_dma_latency to %d!",
latency_target_value);
close(latency_target_fd);
return;
}
printf("# /dev/cpu_dma_latency set to %dus\n", latency_target_value);
}
typedef struct {
volatile void * buffer;
char name[64];
size_t buf_len;
size_t len;
volatile int write_index;
volatile int read_index;
} ring_buffer_t;
typedef struct {
const char * re_mac;
const char * rec_mac;
const char * rec_if;
int recv_affinity;
int send_affinity;
int prepare_affinity;
int decompress_affinity;
int ecpri_period;
int flow_id;
int sample_rate;
} TRXEcpriState;
// Buffers
static ring_buffer_t rx_rbuf;
static ring_buffer_t trx_read_rbuf;
static ring_buffer_t tx_rbuf;
static ring_buffer_t trx_write_rbuf;
static volatile int trx_wb_part[TRX_WB_MAX_PARTS]; // TODO write next index instead of current
static volatile int64_t trx_wb_ts[TRX_WB_MAX_PARTS];
static int trx_wb_part_read_index;
static int trx_wb_part_write_index;
// Locks
pthread_mutex_t tx_mutex;
pthread_cond_t tx_cond;
pthread_mutex_t rx_mutex;
pthread_cond_t rx_cond;
pthread_mutex_t tx_ready_mutex;
pthread_cond_t tx_ready_cond;
sem_t trx_read_sem;
// Counters
static volatile int64_t prepared_frame_count;
static volatile int64_t read_frame_count;
static volatile int64_t sent_frame_count;
// Computed values
static int rxtx_buf_size;
static int ecpri_period_mult;
// Network
static volatile int seq_id;
static int send_sockfd;
static int recv_sockfd;
static struct sockaddr_ll connect_sk_addr;
// Timestamps utils
#define NSEC_PER_SEC INT64_C(1000000000)
static struct timespec int_to_ts(int64_t t) {
struct timespec ts;
ts.tv_sec = t / NSEC_PER_SEC;
ts.tv_nsec = t - (ts.tv_sec * NSEC_PER_SEC);
return ts;
}
static int64_t ts_to_int(struct timespec ts) {
return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
}
static void add_ns(struct timespec *t, int64_t ns) {
t->tv_nsec += ns;
while (t->tv_nsec >= ((int64_t)NSEC_PER_SEC)) {
t->tv_sec += 1;
t->tv_nsec -= NSEC_PER_SEC;
}
}
static int64_t calcdiff_ns(struct timespec t1, struct timespec t2) {
int64_t diff;
diff = NSEC_PER_SEC * ((int)t1.tv_sec - (int)t2.tv_sec);
diff += ((int)t1.tv_nsec - (int)t2.tv_nsec);
return diff;
}
static void rbuf_update_write_index(ring_buffer_t * rbuf) {
rbuf->write_index = (rbuf->write_index + 1) % rbuf->buf_len;
}
static void rbuf_update_read_index(ring_buffer_t * rbuf) {
rbuf->read_index = (rbuf->read_index + 1) % rbuf->buf_len;
}
static int rbuf_read_amount(const ring_buffer_t * rbuf) {
return (rbuf->read_index + rbuf->buf_len - rbuf->write_index) % rbuf->buf_len;
}
static int rbuf_write_amount(const ring_buffer_t * rbuf) {
return (rbuf->write_index + rbuf->buf_len - rbuf->read_index) % rbuf->buf_len;
}
#define RBUF_READ(rbuf, type) (((type *) rbuf.buffer) + (rbuf.read_index * rbuf.len))
#define RBUF_WRITE(rbuf, type) (((type *) rbuf.buffer) + (rbuf.write_index * rbuf.len))
#define RBUF_INIT(rbuf, _name, _buf_len, _len, type) do\
{\
log_debug("TRX_ECPRI", "Allocating %s with %d bytes\n", _name, (_buf_len * _len));\
rbuf.buffer = (type *) malloc(_buf_len * _len);\
strcpy(rbuf.name, _name);\
rbuf.buf_len = _buf_len;\
rbuf.len = _len;\
rbuf.write_index = 0;\
rbuf.read_index = 0;\
} while(0)
static void *recv_thread(void *p) {
cpu_set_t mask;
TRXEcpriState * s = (TRXEcpriState *) p;
int ret;
log_info("RECV_THREAD", "Thread init");
// Set thread CPU affinity
CPU_ZERO(&mask);
CPU_SET(s->recv_affinity, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask))
error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU %d\n", s->recv_affinity);
for(;;) {
struct mmsghdr msgh[4000];
struct iovec msgv[4000];
memset(msgv, 0, sizeof(msgv));
memset(msgh, 0, sizeof(msgh));
for(int j = 0; j < ecpri_period_mult; j++) {
msgv[j].iov_base = RBUF_WRITE(rx_rbuf, uint8_t);
msgv[j].iov_len = rx_rbuf.len;
msgh[j].msg_hdr.msg_iov = &msgv[j];
msgh[j].msg_hdr.msg_iovlen = 1;
rbuf_update_write_index(&rx_rbuf);
}
ret = recvmmsg(recv_sockfd, msgh, ecpri_period_mult, 0, NULL);
if(ret == -1)
error(EXIT_FAILURE, errno, "recvmmsg error");
if(ret != ecpri_period_mult)
log_error("RECV_THREAD", "recvmmsg received %d messages instead of %d\n", ret, ecpri_period_mult);
pthread_mutex_lock(&rx_mutex);
pthread_cond_signal(&rx_cond);
pthread_mutex_unlock(&rx_mutex);
}
pthread_exit(EXIT_SUCCESS);
}
static void *send_thread(void *p) {
cpu_set_t mask;
struct timespec initial, next;
struct timespec t1[4000];
struct timespec t2[4000];
int k = 0;
TRXEcpriState * s = (TRXEcpriState *) p;
struct mmsghdr msgh[4000];
struct iovec msgv[4000];
log_info("SEND_THREAD", "Thread init");
// Set thread CPU affinity
CPU_ZERO(&mask);
CPU_SET(s->send_affinity, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask))
error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU %d\n", s->send_affinity);
memset(msgv, 0, sizeof(msgv));
memset(msgh, 0, sizeof(msgh));
for(int j = 0; j < ecpri_period_mult; j++) {
msgh[j].msg_hdr.msg_name = &connect_sk_addr;
msgh[j].msg_hdr.msg_namelen = sizeof(connect_sk_addr);
msgh[j].msg_hdr.msg_iov = &msgv[j];
msgh[j].msg_hdr.msg_iovlen = 1;
}
pthread_mutex_lock(&tx_ready_mutex);
pthread_cond_wait(&tx_ready_cond, &tx_ready_mutex);
pthread_mutex_unlock(&tx_ready_mutex);
clock_gettime(CLOCK_TAI, &initial);
for(int64_t i = 1;; i++) {
int ret, msg_sent;
#ifdef DEBUG
if(i > SEND_LIMIT) {
int64_t d, dt;
clock_gettime(CLOCK_TAI, &next);
d = calcdiff_ns(next, initial);
for(int j = 0; j < k; j++) {
dt = calcdiff_ns(t2[j], t1[j]);
log_debug("SEND_THREAD", "%" PRIi64, dt);
}
log_debug("SEND_THREAD", "Packets sent: %" PRIi64, sent_frame_count);
log_debug("SEND_THREAD", "Duration: %" PRIi64, d);
log_debug("SEND_THREAD", "ecpri_period_mult: %" PRIi64, ecpri_period_mult);
log_debug("SEND_THREAD", "FRAME_FREQ: %" PRIi64, FRAME_FREQ);
exit(EXIT_SUCCESS);
}
#endif
next = initial;
// Multiply by i everytime to prevent any frequence drift
add_ns(&next, (ecpri_period_mult * NSEC_PER_SEC * i) / FRAME_FREQ);
for(int j = 0; j < ecpri_period_mult; j++) {
msgv[j].iov_base = RBUF_READ(tx_rbuf, uint8_t);
msgv[j].iov_len = tx_rbuf.len;
rbuf_update_read_index(&tx_rbuf);
}
for(msg_sent = 0; msg_sent < ecpri_period_mult;) {
#ifdef DEBUG
clock_gettime(CLOCK_TAI, &t1[k]);
#endif
ret = sendmmsg(send_sockfd, msgh + msg_sent, (ecpri_period_mult - msg_sent), 0);
#ifdef DEBUG
clock_gettime(CLOCK_TAI, &t2[k++]);
#endif
if(ret <= 0)
error(EXIT_FAILURE, errno, "sendmmsg error (returned %d)", ret);
msg_sent += ret;
sent_frame_count += ret;
}
pthread_mutex_lock(&tx_mutex);
pthread_cond_signal(&tx_cond);
pthread_mutex_unlock(&tx_mutex);
clock_nanosleep(CLOCK_TAI, TIMER_ABSTIME, &next, NULL);
}
pthread_exit(EXIT_SUCCESS);
}
static void *prepare_thread(void *p) {
cpu_set_t mask;
TRXEcpriState * s = (TRXEcpriState *) p;
int tx_ready_buffer_full = 0;
log_info("PREPARE_THREAD", "Thread init");
// Set thread CPU affinity
CPU_ZERO(&mask);
CPU_SET(s->prepare_affinity, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask))
error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU %d\n", s->prepare_affinity);
for(int64_t i = 0;; i++) {
int16_t samples_int[256];
// If we have frames to prepare
int n = rbuf_write_amount(&tx_rbuf);
if((i == 0) || n) {
// If there are frames from trx_write callback to prepare
if(rbuf_read_amount(&trx_write_rbuf)) {
int64_t ts = trx_wb_ts[trx_wb_part_read_index];
int empty_frames_ahead = ts - prepared_frame_count;
empty_frames_ahead = empty_frames_ahead < n ? empty_frames_ahead : n;
if(empty_frames_ahead > 0) {
for(int j = 0; j < empty_frames_ahead; j++) {
*((uint16_t *) (RBUF_WRITE(tx_rbuf, uint8_t) + 20)) = htons(seq_id++);
rbuf_update_write_index(&tx_rbuf);
prepared_frame_count++;
}
}
else if (empty_frames_ahead == 0) {
int m = trx_wb_part[(trx_wb_part_read_index + 1) % TRX_WB_MAX_PARTS] - trx_write_rbuf.read_index;
m = m < n ? m : n;
for(int j = 0; j < m; j++) {
float * const trx_samples = RBUF_READ(trx_write_rbuf, float);
uint8_t * const tx_frame = RBUF_WRITE(tx_rbuf, uint8_t);
memset(samples_int, 0, 512);
float_to_int16(samples_int, trx_samples, 256, 32767);
encode_bf1(tx_frame + 22 , samples_int);
encode_bf1(tx_frame + 22 + 60 , samples_int + 64);
encode_bf1(tx_frame + 22 + 120, samples_int + 128);
encode_bf1(tx_frame + 22 + 180, samples_int + 192);
*((uint16_t *)(tx_frame + 20)) = htons(seq_id++);
rbuf_update_write_index(&tx_rbuf);
rbuf_update_read_index(&trx_write_rbuf);
prepared_frame_count++;
}
if(m == 0)
trx_wb_part_read_index = (trx_wb_part_read_index + 1) % TRX_WB_MAX_PARTS;
}
else {
log_error("PREPARE_THREAD", "missed trx_write timestamp");
}
}
else {
*((uint16_t *) (RBUF_WRITE(tx_rbuf, uint8_t) + 20)) = htons(seq_id++);
rbuf_update_write_index(&tx_rbuf);
prepared_frame_count++;
}
}
else {
if (!tx_ready_buffer_full) {
tx_ready_buffer_full = 1;
pthread_mutex_lock(&tx_ready_mutex);
pthread_cond_signal(&tx_ready_cond);
pthread_mutex_unlock(&tx_ready_mutex);
}
pthread_mutex_lock(&tx_mutex);
pthread_cond_wait(&tx_cond, &tx_mutex);
pthread_mutex_unlock(&tx_mutex);
}
}
pthread_exit(EXIT_SUCCESS);
}
static void *decompress_thread(void *p) {
cpu_set_t mask;
TRXEcpriState * s = (TRXEcpriState *) p;
int rx_ready = 0;
const float mult = 1. / 32767.;
log_info("DECOMPRESS_THREAD", "Thread init");
// Set thread CPU affinity
CPU_ZERO(&mask);
CPU_SET(s->decompress_affinity, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask))
error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU %d\n", s->decompress_affinity);
for(;;) {
int n = rbuf_read_amount(&rx_rbuf);
if(n) {
for(int j = 0; j < n; j++) {
int16_t samples_int[256];
const uint8_t * rx_samples = RBUF_READ(rx_rbuf, uint8_t) + 22;
// TODO : analyze seq_id, ecpri packet type etc... ?
// TODO : set rx_ready at some point (when ?)
if(rx_ready) {
memset(samples_int, 0, 512);
decode_bf1(samples_int , rx_samples , 16);
decode_bf1(samples_int + 64 , rx_samples + 60, 16);
decode_bf1(samples_int + 128, rx_samples + 120, 16);
decode_bf1(samples_int + 192, rx_samples + 180, 16);
int16_to_float(RBUF_WRITE(trx_read_rbuf, float), samples_int, 256, mult);
rbuf_update_read_index(&rx_rbuf);
rbuf_update_write_index(&trx_read_rbuf);
sem_post(&trx_read_sem);
}
}
}
else {
pthread_mutex_lock(&rx_mutex);
pthread_cond_wait(&rx_cond, &rx_mutex);
pthread_mutex_unlock(&rx_mutex);
}
}
pthread_exit(EXIT_SUCCESS);
}
static int start_threads(TRXEcpriState * s) {
pthread_t recv_pthread;
pthread_t send_pthread;
pthread_t prepare_pthread;
pthread_t decompress_pthread;
struct sched_param recv_param;
struct sched_param send_param;
struct sched_param prepare_param;
struct sched_param decompress_param;
pthread_attr_t recv_attr;
pthread_attr_t send_attr;
pthread_attr_t prepare_attr;
pthread_attr_t decompress_attr;
log_info("TRX_ECPRI", "Starting threads");
// Initialize pthread attributes (default values)
if (pthread_attr_init(&recv_attr))
log_error("TRX_ECPRI", "init pthread attributes failed\n");
// Set a specific stack size
if (pthread_attr_setstacksize(&recv_attr, PTHREAD_STACK_MIN))
log_error("TRX_ECPRI", "pthread setstacksize failed\n");
// Set scheduler policy and priority of pthread
if (pthread_attr_setschedpolicy(&recv_attr, SCHED_FIFO))
log_error("TRX_ECPRI", "pthread setschedpolicy failed\n");
recv_param.sched_priority = 97;
if (pthread_attr_setschedparam(&recv_attr, &recv_param))
log_error("TRX_ECPRI", "pthread setschedparam failed\n");
/* Use scheduling parameters of attr */
if (pthread_attr_setinheritsched(&recv_attr, PTHREAD_EXPLICIT_SCHED))
log_error("TRX_ECPRI", "pthread setinheritsched failed\n");
if (pthread_attr_init(&send_attr))
log_error("TRX_ECPRI", "init pthread attributes failed\n");
if (pthread_attr_setstacksize(&send_attr, PTHREAD_STACK_MIN))
log_error("TRX_ECPRI", "pthread setstacksize failed\n");
if (pthread_attr_setschedpolicy(&send_attr, SCHED_FIFO))
log_error("TRX_ECPRI", "pthread setschedpolicy failed\n");
send_param.sched_priority = 97;
if (pthread_attr_setschedparam(&send_attr, &send_param))
log_error("TRX_ECPRI", "pthread setschedparam failed\n");
if (pthread_attr_setinheritsched(&send_attr, PTHREAD_EXPLICIT_SCHED))
log_error("TRX_ECPRI", "pthread setinheritsched failed\n");
if (pthread_attr_init(&prepare_attr))
log_error("TRX_ECPRI", "init pthread attributes failed\n");
if (pthread_attr_setstacksize(&prepare_attr, PTHREAD_STACK_MIN))
log_error("TRX_ECPRI", "pthread setstacksize failed\n");
if (pthread_attr_setschedpolicy(&prepare_attr, SCHED_FIFO))
log_error("TRX_ECPRI", "pthread setschedpolicy failed\n");
prepare_param.sched_priority = 97;
if (pthread_attr_setschedparam(&prepare_attr, &prepare_param))
log_error("TRX_ECPRI", "pthread setschedparam failed\n");
if (pthread_attr_setinheritsched(&prepare_attr, PTHREAD_EXPLICIT_SCHED))
log_error("TRX_ECPRI", "pthread setinheritsched failed\n");
if (pthread_attr_init(&decompress_attr))
log_error("TRX_ECPRI", "init pthread attributes failed\n");
if (pthread_attr_setstacksize(&decompress_attr, PTHREAD_STACK_MIN))
log_error("TRX_ECPRI", "pthread setstacksize failed\n");
if (pthread_attr_setschedpolicy(&decompress_attr, SCHED_FIFO))
log_error("TRX_ECPRI", "pthread setschedpolicy failed\n");
decompress_param.sched_priority = 97;
if (pthread_attr_setschedparam(&decompress_attr, &decompress_param))
log_error("TRX_ECPRI", "pthread setschedparam failed\n");
if (pthread_attr_setinheritsched(&decompress_attr, PTHREAD_EXPLICIT_SCHED))
log_error("TRX_ECPRI", "pthread setinheritsched failed\n");
if (pthread_create(&recv_pthread, NULL, recv_thread, s))
error(EXIT_FAILURE, errno, "Couldn't create recv thread");
if (pthread_create(&send_pthread, NULL, send_thread, s))
error(EXIT_FAILURE, errno, "Couldn't create send thread");
if (pthread_create(&prepare_pthread, NULL, prepare_thread, s))
error(EXIT_FAILURE, errno, "Couldn't create prepare thread");
if (pthread_create(&decompress_pthread, NULL, decompress_thread, s))
error(EXIT_FAILURE, errno, "Couldn't create decompress thread");
return 0;
}
int start(TRXEcpriState * s) {
uint8_t dst_mac[6];
uint8_t src_mac[6];
uint8_t ecpri_packet[PACKET_SIZE];
struct ether_header *eh = (struct ether_header *) ecpri_packet;
int if_index;
log_debug("TRX_ECPRI", "raw socket setup");
//set_latency_target();
seq_id = 0;
read_frame_count = 0;
sent_frame_count = 0;
prepared_frame_count = 0;
ecpri_period_mult = (s->ecpri_period * FRAME_FREQ) / 1000000;
rxtx_buf_size = (3 * ecpri_period_mult);
RBUF_INIT(rx_rbuf, "RX ring buffer", rxtx_buf_size, PACKET_SIZE, uint8_t);
RBUF_INIT(tx_rbuf, "TX ring buffer", rxtx_buf_size, PACKET_SIZE, uint8_t);
RBUF_INIT(trx_read_rbuf, "TRXRead ring buffer", TRX_BUF_MAX_SIZE, 256, float);
RBUF_INIT(trx_write_rbuf, "TRXWrite ring buffer", TRX_BUF_MAX_SIZE, 256, float);
trx_wb_part_read_index = 0;
trx_wb_part_write_index = 0;
pthread_mutex_init(&tx_mutex, NULL);
pthread_mutex_init(&rx_mutex, NULL);
pthread_mutex_init(&tx_ready_mutex, NULL);
pthread_cond_init(&tx_cond, NULL);
pthread_cond_init(&rx_cond, NULL);
pthread_cond_init(&tx_ready_cond, NULL);
sem_init(&trx_read_sem, 0, 0);
memset((uint8_t *) ecpri_packet, 0, PACKET_SIZE);
if (!(if_index = if_nametoindex(s->rec_if))) {
perror("if_nametoindex");
return 1;
}
if(sscanf(s->re_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%*c", &dst_mac[0], &dst_mac[1], &dst_mac[2], &dst_mac[3], &dst_mac[4], &dst_mac[5]) != 6)
fprintf(stderr, "Invalid eRE MAC address\n");
if(sscanf(s->rec_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%*c", &src_mac[0], &src_mac[1], &src_mac[2], &src_mac[3], &src_mac[4], &src_mac[5]) != 6)
fprintf(stderr, "Invalid eREC MAC address\n");
if ((send_sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) {
perror("Socket Error");
return 1;
}
if ((recv_sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) {
perror("Socket Error");
return 1;
}
connect_sk_addr.sll_ifindex = if_index;
connect_sk_addr.sll_halen = ETH_ALEN;
for(int i = 0; i < 6; i++)
connect_sk_addr.sll_addr[i] = dst_mac[i];
log_debug("TRX_ECPRI", "bind");
for(int i = 0; i < 6; i++)
eh->ether_shost[i] = src_mac[i];
for(int i = 0; i < 6; i++)
eh->ether_dhost[i] = dst_mac[i];
/* Ethertype field */
eh->ether_type = htons(0xaefe);
/* Standard Header */
ecpri_packet[14] = 0x10; // Protocol data revision 0x1, C = 0
// Message type = 0x00, IQ data
// Payload size
*((uint16_t *) (ecpri_packet + 16)) = htons(244);
*((uint16_t *) (ecpri_packet + 18)) = htons(s->flow_id);
for(int i = 0; i < rxtx_buf_size; i++) {
//log_debug("TRX_ECPRI", "%d / %d - %d\n", i, rxtx_buf_size, tx_rbuf.len);
memcpy(((uint8_t *) tx_rbuf.buffer) + (i * tx_rbuf.len), ecpri_packet, tx_rbuf.len);
}
start_threads(s);
return 0;
}
static void trx_ecpri_end(TRXState *s1)
{
log_info("TRX_ECPRI", "End");
TRXEcpriState *s = s1->opaque;
free(s);
}
static void trx_ecpri_write(TRXState *s1, trx_timestamp_t timestamp, const void **__samples, int count, int tx_port_index, TRXWriteMetadata *md)
{
(void) s1;
float ** _samples = (float **) __samples;
int write_count = count >> 5;
int64_t ts = timestamp >> 5;
trx_wb_part[trx_wb_part_write_index] = trx_write_rbuf.write_index;
trx_wb_ts[trx_wb_part_write_index] = ts;
for(int k = 0; k < write_count; k++) {
for(int i = 0; i < 4; i++)
for(int j = 0; j < 64; j++)
RBUF_WRITE(trx_write_rbuf, float)[i * 64 + j] = _samples[i][j + (k << 6)];
rbuf_update_write_index(&trx_write_rbuf);
}
trx_wb_part_write_index = (trx_wb_part_write_index + 1) % TRX_WB_MAX_PARTS;
trx_wb_part[trx_wb_part_write_index] = trx_write_rbuf.write_index + write_count;
}
static int trx_ecpri_read(TRXState *s1, trx_timestamp_t *ptimestamp, void **__samples, int count, int rx_port_index, TRXReadMetadata *md)
{
(void) s1;
float ** _samples = (float **) __samples;
int read_count = count >> 5;
for(int k = 0; k < read_count; k++) {
float * trx_samples;
sem_wait(&trx_read_sem);
trx_samples = RBUF_READ(trx_read_rbuf, float);
for(int i = 0; i < 4; i++)
for(int j = 0; j < 64; j++)
_samples[i][j + (k << 6)] = trx_samples[i * 64 + j];
rbuf_update_read_index(&trx_read_rbuf);
}
*ptimestamp = read_frame_count << 5;
read_frame_count += read_count;
return count;
}
/* This function can be used to automatically set the sample
rate. Here we don't implement it, so the user has to force a given
sample rate with the "sample_rate" configuration option */
static int trx_ecpri_get_sample_rate(TRXState *s1, TRXFraction *psample_rate,
int *psample_rate_num, int sample_rate_min)
{
return -1;
}
static int trx_ecpri_start(TRXState *s1, const TRXDriverParams *params)
{
TRXEcpriState *s = s1->opaque;
s->sample_rate = params->sample_rate[0].num / params->sample_rate[0].den;
start(s);
return 0;
}
int trx_driver_init(TRXState *s1)
{
TRXEcpriState *s;
double val;
// Lock all current and future pages from preventing of being paged to
// swap
if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
log_error("TRX_ECPRI", "mlockall failed");
}
log_info("TRX_ECPRI", "Init");
if (s1->trx_api_version != TRX_API_VERSION) {
fprintf(stderr, "ABI compatibility mismatch between LTEENB and TRX driver (LTEENB ABI version=%d, TRX driver ABI version=%d)\n",
s1->trx_api_version, TRX_API_VERSION);
return -1;
}
s = malloc(sizeof(TRXEcpriState));
memset(s, 0, sizeof(*s));
trx_get_param_double(s1, &val, "recv_affinity");
s->recv_affinity = (int) val;
trx_get_param_double(s1, &val, "send_affinity");
s->send_affinity = (int) val;
trx_get_param_double(s1, &val, "prepare_affinity");
s->send_affinity = (int) val;
trx_get_param_double(s1, &val, "decompress_affinity");
s->send_affinity = (int) val;
trx_get_param_double(s1, &val, "flow_id");
s->flow_id = (int) val;
trx_get_param_double(s1, &val, "ecpri_period");
s->ecpri_period = (int) val;
s->re_mac = trx_get_param_string(s1, "re_mac");
s->rec_mac = trx_get_param_string(s1, "rec_mac");
s->rec_if = trx_get_param_string(s1, "rec_if");
s1->opaque = s;
s1->trx_end_func = trx_ecpri_end;
s1->trx_write_func2 = trx_ecpri_write;
s1->trx_read_func2 = trx_ecpri_read;
s1->trx_start_func = trx_ecpri_start;
s1->trx_get_sample_rate_func = trx_ecpri_get_sample_rate;
return 0;
}
int start_udp(TRXEcpriState * s) {
struct addrinfo server_hints, client_hints, *server_info, *client_info, *p;
int rv;
char ip_string[INET6_ADDRSTRLEN];
if(init_buffers()) {
fprintf(stderr, "Init buffer failed\n");
exit(EXIT_FAILURE);
}
memset((uint8_t *) ecpri_packet, 0, MAX_PACKET_SIZE);
memset((uint8_t *) empty_ecpri_packet, 0, MAX_PACKET_SIZE);
memset(&server_hints, 0, sizeof server_hints);
memset(&client_hints, 0, sizeof client_hints);
server_hints.ai_family = AF_UNSPEC;
server_hints.ai_socktype = SOCK_DGRAM;
client_hints.ai_family = AF_INET;
client_hints.ai_socktype = SOCK_DGRAM;
client_hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, s->rec_port, &client_hints, &client_info)) != 0) {
fprintf(stderr, "client getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
if ((recv_sockfd = socket(client_info->ai_family, client_info->ai_socktype,
client_info->ai_protocol)) == -1) {
perror("client: socket");
return 1;
}
if ((send_sockfd = socket(client_info->ai_family, client_info->ai_socktype,
client_info->ai_protocol)) == -1) {
perror("client: socket");
return 1;
}
if (bind(recv_sockfd, client_info->ai_addr, client_info->ai_addrlen) == -1) {
close(recv_sockfd);
perror("client: connect");
return 1;
}
freeaddrinfo(client_info);
if ((rv = getaddrinfo(s->re_ip, s->re_port, &server_hints, &server_info)) != 0) {
fprintf(stderr, "server getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
for(p = server_info; p != NULL; p = p->ai_next) {
if (connect(send_sockfd, p->ai_addr, p->ai_addrlen) == -1) {
perror("client: connect");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "client: failed to connect\n");
return 1;
}
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), ip_string, sizeof ip_string);
log_info("TRX_ECPRI", "Connected to %s\n", ip_string);
freeaddrinfo(server_info);
_start();
return 0;
}
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <getopt.h>
#include <immintrin.h>
#include <inttypes.h>
#include <limits.h>
#include <linux/if_packet.h>
#include <math.h>
#include <netdb.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <net/if.h>
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
/* Note: src and dst must be 16 byte aligned */
static void float_to_int16(int16_t *dst, const float *src, int n, float mult)
{
const __m128 *p;
__m128i *q, a0, a1;
__m128 mult1;
mult1 = _mm_set1_ps(mult);
p = (const void *)src;
q = (void *)dst;
while (n >= 16) {
a0 = _mm_cvtps_epi32(p[0] * mult1);
a1 = _mm_cvtps_epi32(p[1] * mult1);
q[0] = _mm_packs_epi32(a0, a1);
a0 = _mm_cvtps_epi32(p[2] * mult1);
a1 = _mm_cvtps_epi32(p[3] * mult1);
q[1] = _mm_packs_epi32(a0, a1);
p += 4;
q += 2;
n -= 16;
}
if (n >= 8) {
a0 = _mm_cvtps_epi32(p[0] * mult1);
a1 = _mm_cvtps_epi32(p[1] * mult1);
q[0] = _mm_packs_epi32(a0, a1);
p += 2;
q += 1;
n -= 8;
}
if (n != 0) {
/* remaining samples (n <= 7) */
do {
a0 = _mm_cvtps_epi32(_mm_load_ss((float *)p) * mult);
*(int16_t *)q = _mm_cvtsi128_si32 (_mm_packs_epi32(a0, a0));
p = (__m128 *)((float *)p + 1);
q = (__m128i *)((int16_t *)q + 1);
n--;
} while (n != 0);
}
}
/* Note: src and dst must be 16 byte aligned */
static void int16_to_float(float *dst, const int16_t *src, int len, float mult)
{
__m128i a0, a1, a, b, sign;
__m128 mult1;
mult1 = _mm_set1_ps(mult);
while (len >= 8) {
a = *(__m128i *)&src[0];
#ifdef SSE4
a0 = _mm_cvtepi16_epi32(a);
#else
// Fix for CPU without SSE4.1
a0 = _mm_unpacklo_epi16(a, a);
a0 = _mm_srai_epi32(a0, 16);
#endif
b = _mm_srli_si128(a, 8);
#ifdef SSE4
a1 = _mm_cvtepi16_epi32(b);
#else
a1 = _mm_unpacklo_epi16(b, b);
a1 = _mm_srai_epi32(a1, 16);
#endif
*(__m128 *)&dst[0] = _mm_cvtepi32_ps(a0) * mult1;
*(__m128 *)&dst[4] = _mm_cvtepi32_ps(a1) * mult1;
dst += 8;
src += 8;
len -= 8;
}
/* remaining data */
while (len != 0) {
_mm_store_ss(&dst[0], _mm_cvtsi32_ss(_mm_setzero_ps(), src[0]) * mult1);
dst++;
src++;
len--;
}
}
/* Compr_bf1 */
static inline int max_int(int a, int b)
{
if (a > b)
return a;
else
return b;
}
static inline int min_int(int a, int b)
{
if (a < b)
return a;
else
return b;
}
static inline int clamp_int(int val, int min_val, int max_val)
{
if (val < min_val)
return min_val;
else if (val > max_val)
return max_val;
else
return val;
}
/* 1 <= n_bits <= 7 */
static void encode_put_bits(uint8_t *buf, int bit_offset, int n_bits, unsigned int val)
{
int shift, n;
shift = bit_offset & 7;
buf[bit_offset >> 3] |= val << shift;
n = 8 - shift;
if (n > 0)
buf[(bit_offset >> 3) + 1] |= val >> n;
}
/* 1 <= n_bits <= 7 */
static int decode_get_bits(const uint8_t *buf, int bit_offset, int n_bits)
{
int shift, n, v;
shift = bit_offset & 7;
n = 8 - shift;
v = buf[bit_offset >> 3];
if (n_bits > n)
v |= buf[(bit_offset >> 3) + 1] << 8;
return (v >> shift) & ((1 << n_bits) - 1);
}
#define BLOCK_LEN 8
#define CMULT_BITS 7
#define CMULT 181
/* CPRI frame format for 122.88 MHz with 4 channels using bit rate
option 7 (9.8304 Gb/s):
data byte offset
0 Channel 0. One BF1 block containing I0, Q0, I1, Q1, ..., I31, Q31.
60 Channel 1
120 Channel 2
180 Channel 3
*/
/* take 64 * 16 bit I or Q values as input and return 60 bytes into obuf.
When using a smaller sample bit width, the samples should be
appropriately scaled (e.g. multiplied by 2 for a bit width =
15). */
static void encode_bf1(uint8_t *obuf, const int16_t *samples)
{
int e, vmax, v, block_num, i, e1, m;
memset(obuf, 0, 4 + 7 * BLOCK_LEN);
/* the compressed data contains 8 blocks of 8 I or Q components. Each
block is compressed independently but the corresponding values
and output bits are interleaved. */
for(block_num = 0; block_num < 8; block_num++) {
vmax = 0;
for(i = 0; i < BLOCK_LEN; i++) {
vmax = max_int(vmax, abs(samples[i * 8 + block_num]));
}
vmax = min_int(vmax, (1 << 15) - 1);
/* 0 <= vmax <= 2^15-1 */
e = 8;
while (vmax < (1 << 14) && e > 0) {
vmax <<= 1;
e--;
}
if (e <= 1) {
e1 = e;
m = 1;
} else {
m = (vmax >= (CMULT << (14 - CMULT_BITS)));
e1 = e * 2 + m - 2;
}
encode_put_bits(obuf, block_num * 32, 4, e1); /* store the exponent */
for(i = 0; i < BLOCK_LEN; i++) {
v = samples[i * 8 + block_num];
v = v << (8 - e);
if (!m)
v = v * CMULT;
else
v = v << CMULT_BITS;
/* quantize on 7 bits using two's complement notation */
v = (v + (1 << (8 + CMULT_BITS))) >> (9 + CMULT_BITS);
v = clamp_int(v, -64, 63);
v = v & 0x7f;
if (i < 4)
encode_put_bits(obuf, block_num * 32 + 4 + i * 7, 7, v);
else
encode_put_bits(obuf, 8 * 32 + block_num * 28 + (i - 4) * 7, 7, v);
}
}
}
/* Take 60 bytes as input and return 64 values of sample_bit_width bits
(14 <= sample_bit_width <= 16) */
static void decode_bf1(int16_t *samples, const uint8_t *ibuf, int sample_bit_width)
{
int e, v, e1, m, shift, bias;
int block_num, i;
shift = (15 - sample_bit_width) + CMULT_BITS + 1;
bias = (1 << shift) >> 1;
for(block_num = 0; block_num < 8; block_num++) {
e1 = decode_get_bits(ibuf, block_num * 32, 4);
if (e1 <= 1) {
e = e1;
m = 1;
} else {
e1 += 2;
m = e1 & 1;
e = e1 >> 1;
}
for(i = 0; i < BLOCK_LEN; i++) {
if (i < 4)
v = decode_get_bits(ibuf, block_num * 32 + 4 + i * 7, 7);
else
v = decode_get_bits(ibuf, 8 * 32 + block_num * 28 + (i - 4) * 7, 7);
/* sign extend two's complement 7 bit value to 32 bits */
v = (int32_t)((uint32_t)v << (32 - 7)) >> (32 - 7);
/* multiply by the scale */
v = v << e;
if (!m)
v = v * CMULT;
else
v = v << (CMULT_BITS + 1);
/* shift and round */
v = (v + bias) >> shift;
samples[i * 8 + block_num] = v;
}
}
}
char in[256];
char out[256];
int main(int argc, char ** argv) {
int compress, tx;
int16_t int16_samples[64];
const float mult = 1. / 32767.;
if(argc != 2)
fprintf(stderr, "Wrong argument number");
compress = (argv[1][0] == 'c');
freopen(NULL, "rb", stdin);
fread(in, compress ? (64 * 4) : 60 , 1, stdin);
fclose(stdin);
if(compress) {
float_to_int16(int16_samples, (float *) in, 64, 32767);
encode_bf1(out, int16_samples);
} else {
decode_bf1(int16_samples, in, 16);
int16_to_float((float *) out, int16_samples, 64, mult);
}
freopen(NULL, "wb", stdout);
fwrite(out, compress ? 60 : (64 * 4), 1, stdout);
fclose(stdout);
return 0;
}
import subprocess
import struct
import random
def float_to_byte(float_list):
iq_samples_bytes = bytearray(64 * 4)
for i in range(64):
b = struct.pack('f', float_list[i])
for j in range(4):
iq_samples_bytes[i * 4 + j] = b[j]
return bytes(iq_samples_bytes)
def byte_to_float(b):
float_list = []
for i in range(64):
x = struct.unpack('f', b[4 * i:4 * (i+1)])[0]
float_list.append(x)
return float_list
def compress(b):
s = subprocess.run(
["./bf1", "c"],
input=b,
capture_output=True,
)
return s.stdout
def decompress(b):
s = subprocess.run(["./bf1", "d"],
input=b,
capture_output=True,
)
return s.stdout
iq_samples = [0.2 * (random.randint(1,1000) / 1000.0) for i in range(64)]
iq_samples2 = byte_to_float(decompress(compress(float_to_byte(iq_samples))))
d = []
for i in range(64):
d.append(abs(iq_samples[i]-iq_samples2[i]))
print("{}% accuracy".format(100 - 100 * max(d) / max(map(abs, iq_samples))))
/* DPDK */
#define BURST_SIZE 16
#define MEMPOOL_CACHE_SIZE 256
#define RTE_TEST_RX_DESC_DEFAULT 1024
#define RTE_TEST_TX_DESC_DEFAULT 1024
......@@ -8,7 +7,6 @@ static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
struct rte_mempool *mbuf_pool;
struct rte_ether_addr s_addr;
struct rte_ether_addr d_addr;
int8_t tx_data[BURST_SIZE][TX_PACKET_SIZE];
static const struct rte_eth_conf port_conf_default = {
.rxmode = { .max_lro_pkt_size = RTE_ETHER_MAX_LEN }
};
......@@ -57,6 +55,7 @@ static void init_dpdk(int argc, char ** argv) {
argv += ret;
nb_mbufs = RTE_MAX((nb_rxd + nb_txd + BURST_SIZE + MEMPOOL_CACHE_SIZE), 8192U);
nb_mbufs = 1024U * 16;
mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", nb_mbufs,
MEMPOOL_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
......
/* DRB configuration for each 5QI value.
5QI characteristics in TS 23.501 table 5.7.4-1.
The qci parameter is used for the 5QI value */
#ifndef EPS_FALLBACK
#define EPS_FALLBACK 0
#endif
[
/**************************************** GBR */
{
qci: 1, /* UM - real time (RTP for VOIP) */
use_for_en_dc: false,
#if EPS_FALLBACK > 0
trigger_eps_fallback: true,
#endif
ims_dedicated_bearer: true,
pdcp_config: {
discardTimer: 100, /* in ms, 0 means infinity */
pdcp_SN_SizeUL: 12,
pdcp_SN_SizeDL: 12,
statusReportRequired: false,
outOfOrderDelivery: false,
t_Reordering: 0,
/* ROHC header compression */
/*
headerCompression: {
maxCID: 15,
profile0x0001: true, // RTP profile
profile0x0002: true, // UDP profile
profile0x0004: false, // IP profile
},
*/
},
rlc_config: {
ul_um: {
sn_FieldLength: 6,
},
dl_um: {
sn_FieldLength: 6,
t_Reassembly: 50,
},
},
logical_channel_config: {
priority: 7,
prioritisedBitRate: 0, /* in kb/s, -1 means infinity */
bucketSizeDuration: 100, /* in ms */
logicalChannelGroup: 1,
},
},
{
qci: 2, /* UM - real time (video) */
use_for_en_dc: false,
#if EPS_FALLBACK > 0
trigger_eps_fallback: true,
#endif
ims_dedicated_bearer: true,
pdcp_config: {
discardTimer: 150, /* in ms, 0 means infinity */
pdcp_SN_SizeUL: 18,
pdcp_SN_SizeDL: 18,
statusReportRequired: false,
outOfOrderDelivery: false,
t_Reordering: 0,
},
rlc_config: {
ul_um: {
sn_FieldLength: 12,
},
dl_um: {
sn_FieldLength: 12,
t_Reassembly: 50,
},
},
logical_channel_config: {
priority: 8,
prioritisedBitRate: 0, /* in kb/s, -1 means infinity */
bucketSizeDuration: 100, /* in ms */
logicalChannelGroup: 1,
},
},
{
qci: 3, /* UM - real time (gaming) */
pdcp_config: {
discardTimer: 100, /* in ms, 0 means infinity */
pdcp_SN_SizeUL: 18,
pdcp_SN_SizeDL: 18,
statusReportRequired: false,
outOfOrderDelivery: false,
t_Reordering: 0,
},
rlc_config: {
ul_um: {
sn_FieldLength: 12,
},
dl_um: {
sn_FieldLength: 12,
t_Reassembly: 50,
},
},
logical_channel_config: {
priority: 7,
prioritisedBitRate: 0, /* in kb/s, -1 means infinity */
bucketSizeDuration: 100, /* in ms */
logicalChannelGroup: 2,
},
},
{
qci: 4, /* AM - Non-Conversational Video (Buffered Streaming) */
pdcp_config: {
discardTimer: 0, /* in ms, 0 means infinity */
pdcp_SN_SizeUL: 18,
pdcp_SN_SizeDL: 18,
statusReportRequired: true,
outOfOrderDelivery: false,
},
rlc_config: {
ul_am: {
sn_FieldLength: 18,
t_PollRetransmit: 80, /* in ms */
pollPDU: 64,
pollByte: 125, /* in kBytes, 0 means infinity */
maxRetxThreshold: 4,
},
dl_am: {
sn_FieldLength: 18,
t_Reassembly: 80, /* in ms */
t_StatusProhibit: 10, /* in ms */
},
},
logical_channel_config: {
priority: 9,
prioritisedBitRate: 8, /* in kb/s, -1 means infinity */
bucketSizeDuration: 100, /* in ms */
logicalChannelGroup: 3,
},
},
{
qci: 65, /* UM - real time (MC-PTT voice) */
use_for_en_dc: false,
#if EPS_FALLBACK > 0
trigger_eps_fallback: true,
#endif
ims_dedicated_bearer: true,
pdcp_config: {
discardTimer: 100, /* in ms, 0 means infinity */
pdcp_SN_SizeUL: 12,
pdcp_SN_SizeDL: 12,
statusReportRequired: false,
outOfOrderDelivery: false,
t_Reordering: 0,
/* ROHC header compression */
/*
headerCompression: {
maxCID: 15,
profile0x0001: true, // RTP profile
profile0x0002: true, // UDP profile
profile0x0004: false, // IP profile
},
*/
},
rlc_config: {
ul_um: {
sn_FieldLength: 6,
},
dl_um: {
sn_FieldLength: 6,
t_Reassembly: 50,
},
},
logical_channel_config: {
priority: 5,
prioritisedBitRate: 0, /* in kb/s, -1 means infinity */
bucketSizeDuration: 100, /* in ms */
logicalChannelGroup: 4,
},
},
{
qci: 66, /* UM - real time (non MC-PTT voice) */
use_for_en_dc: false,
#if EPS_FALLBACK > 0
trigger_eps_fallback: true,
#endif
ims_dedicated_bearer: true,
pdcp_config: {
discardTimer: 150, /* in ms, 0 means infinity */
pdcp_SN_SizeUL: 18,
pdcp_SN_SizeDL: 18,
statusReportRequired: false,
outOfOrderDelivery: false,
t_Reordering: 0,
},
rlc_config: {
ul_um: {
sn_FieldLength: 12,
},
dl_um: {
sn_FieldLength: 12,
t_Reassembly: 50,
},
},
logical_channel_config: {
priority: 7,
prioritisedBitRate: 0, /* in kb/s, -1 means infinity */
bucketSizeDuration: 100, /* in ms */
logicalChannelGroup: 4,
},
},
{
qci: 67, /* UM - Mission Critical Video user plane */
use_for_en_dc: false,
#if EPS_FALLBACK > 0
trigger_eps_fallback: true,
#endif
ims_dedicated_bearer: true,
pdcp_config: {
discardTimer: 100, /* in ms, 0 means infinity */
pdcp_SN_SizeUL: 18,
pdcp_SN_SizeDL: 18,
statusReportRequired: false,
outOfOrderDelivery: false,
t_Reordering: 0,
},
rlc_config: {
ul_um: {
sn_FieldLength: 12,
},
dl_um: {
sn_FieldLength: 12,
t_Reassembly: 50,
},
},
logical_channel_config: {
priority: 6,
prioritisedBitRate: 0, /* in kb/s, -1 means infinity */
bucketSizeDuration: 100, /* in ms */
logicalChannelGroup: 5,
},
},
/**************************************** non GBR */
{
qci: 5, /* AM - high priority (SIP) */
use_for_en_dc: false,
pdcp_config: {
discardTimer: 0, /* in ms, 0 means infinity */
pdcp_SN_SizeUL: 18,
pdcp_SN_SizeDL: 18,
statusReportRequired: true,
outOfOrderDelivery: false,
},
rlc_config: {
ul_am: {
sn_FieldLength: 18,
t_PollRetransmit: 80, /* in ms */
pollPDU: 64,
pollByte: 125, /* in kBytes, 0 means infinity */
maxRetxThreshold: 4,
},
dl_am: {
sn_FieldLength: 18,
t_Reassembly: 80, /* in ms */
t_StatusProhibit: 10, /* in ms */
},
},
logical_channel_config: {
priority: 6,
prioritisedBitRate: 8, /* in kb/s, -1 means infinity */
bucketSizeDuration: 100, /* in ms */
logicalChannelGroup: 4,
},
},
{
qci: 6, /* AM - Video (buffered streaming) */
pdcp_config: {
discardTimer: 0, /* in ms, 0 means infinity */
pdcp_SN_SizeUL: 18,
pdcp_SN_SizeDL: 18,
statusReportRequired: true,
outOfOrderDelivery: false,
},
rlc_config: {
ul_am: {
sn_FieldLength: 18,
t_PollRetransmit: 80, /* in ms */
pollPDU: 64,
pollByte: 125, /* in kBytes, 0 means infinity */
maxRetxThreshold: 4,
},
dl_am: {
sn_FieldLength: 18,
t_Reassembly: 80, /* in ms */
t_StatusProhibit: 10, /* in ms */
},
},
logical_channel_config: {
priority: 10,
prioritisedBitRate: 8, /* in kb/s, -1 means infinity */
bucketSizeDuration: 100, /* in ms */
logicalChannelGroup: 5,
},
},
{
qci: 7, /* UM - voice, video (live streaming), interactive gaming */
pdcp_config: {
discardTimer: 100, /* in ms, 0 means infinity */
pdcp_SN_SizeUL: 18,
pdcp_SN_SizeDL: 18,
statusReportRequired: false,
outOfOrderDelivery: false,
t_Reordering: 0,
},
rlc_config: {
ul_um: {
sn_FieldLength: 12,
},
dl_um: {
sn_FieldLength: 12,
t_Reassembly: 50,
},
},
logical_channel_config: {
priority: 11,
prioritisedBitRate: 0, /* in kb/s, -1 means infinity */
bucketSizeDuration: 100, /* in ms */
logicalChannelGroup: 6,
},
},
{
qci: 8, /* AM - best effort (Internet traffic) */
pdcp_config: {
discardTimer: 0, /* in ms, 0 means infinity */
pdcp_SN_SizeUL: 18,
pdcp_SN_SizeDL: 18,
statusReportRequired: true,
outOfOrderDelivery: false,
},
rlc_config: {
ul_am: {
sn_FieldLength: 18,
t_PollRetransmit: 80, /* in ms */
pollPDU: 64,
pollByte: 125, /* in kBytes, 0 means infinity */
maxRetxThreshold: 4,
},
dl_am: {
sn_FieldLength: 18,
t_Reassembly: 80, /* in ms */
t_StatusProhibit: 10, /* in ms */
},
},
logical_channel_config: {
priority: 12,
prioritisedBitRate: 8, /* in kb/s, -1 means infinity */
bucketSizeDuration: 100, /* in ms */
logicalChannelGroup: 7,
},
},
{
qci: 9, /* AM - best effort (Internet traffic) */
pdcp_config: {
discardTimer: 0, /* in ms, 0 means infinity */
pdcp_SN_SizeUL: 18,
pdcp_SN_SizeDL: 18,
statusReportRequired: true,
outOfOrderDelivery: false,
},
rlc_config: {
ul_am: {
sn_FieldLength: 18,
t_PollRetransmit: 80, /* in ms */
pollPDU: 64,
pollByte: 125, /* in kBytes, 0 means infinity */
maxRetxThreshold: 4,
},
dl_am: {
sn_FieldLength: 18,
t_Reassembly: 80, /* in ms */
t_StatusProhibit: 10, /* in ms */
},
},
logical_channel_config: {
priority: 13,
prioritisedBitRate: 8, /* in kb/s, -1 means infinity */
bucketSizeDuration: 100, /* in ms */
logicalChannelGroup: 7,
},
},
{
qci: 69, /* AM - high priority (MC-PTT signalling) */
use_for_en_dc: false,
pdcp_config: {
discardTimer: 0, /* in ms, 0 means infinity */
pdcp_SN_SizeUL: 18,
pdcp_SN_SizeDL: 18,
statusReportRequired: true,
outOfOrderDelivery: false,
},
rlc_config: {
ul_am: {
sn_FieldLength: 18,
t_PollRetransmit: 80, /* in ms */
pollPDU: 64,
pollByte: 125, /* in kBytes, 0 means infinity */
maxRetxThreshold: 4,
},
dl_am: {
sn_FieldLength: 18,
t_Reassembly: 80, /* in ms */
t_StatusProhibit: 10, /* in ms */
},
},
logical_channel_config: {
priority: 4,
prioritisedBitRate: 8, /* in kb/s, -1 means infinity */
bucketSizeDuration: 100, /* in ms */
logicalChannelGroup: 4,
},
},
{
qci: 70, /* AM - MC data */
pdcp_config: {
discardTimer: 0, /* in ms, 0 means infinity */
pdcp_SN_SizeUL: 18,
pdcp_SN_SizeDL: 18,
statusReportRequired: true,
outOfOrderDelivery: false,
},
rlc_config: {
ul_am: {
sn_FieldLength: 18,
t_PollRetransmit: 80, /* in ms */
pollPDU: 64,
pollByte: 125, /* in kBytes, 0 means infinity */
maxRetxThreshold: 4,
},
dl_am: {
sn_FieldLength: 18,
t_Reassembly: 80, /* in ms */
t_StatusProhibit: 10, /* in ms */
},
},
logical_channel_config: {
priority: 11,
prioritisedBitRate: 8, /* in kb/s, -1 means infinity */
bucketSizeDuration: 100, /* in ms */
logicalChannelGroup: 5,
},
},
]
/* lteenb configuration file version ##VERSION##
* Copyright (C) 2019-2021 Amarisoft
* NR SA FDD or TDD cell */
#define TDD 1 // Values: 0 (NR FDD), 1(NR TDD)
#define TDD_CONFIG 2 // Values: 1, 2 or 3
#define N_ANTENNA_DL 4 // Values: 1 (SISO), 2 (MIMO 2x2), 4 (MIMO 4x4)
#define N_ANTENNA_UL 1 // Values: 1, 2, 4
#define BANDWIDTH 100 // NR cell bandwidth
#define CPRI 1
#define NR_TEST_MODE -1
/* define to 1 to enable periodic SRS with N_ANTENNA_UL ports. Uplink
SU-MIMO is also enabled if N_ANTENNA_UL >= 2. Not all UEs support
uplink SU-MIMO. */
#define USE_SRS 0
{
//log_options: "all.level=debug,all.max_size=1",
log_options: "all.level=error,all.max_size=0,nas.level=debug,nas.max_size=1,ngap.level=debug,ngap.max_size=1,xnap.level=debug,xnap.max_size=1,rrc.level=debug,rrc.max_size=1",
log_filename: "/tmp/gnb0.log",
/* Enable remote API and Web interface */
com_addr: "0.0.0.0:9001",
rf_driver: {
name: "ecpri",
//rec_mac: "b8:59:9f:07:86:42",
//re_mac: "04:09:a5:0f:9f:4a", /* HFR Switch */
//rec_if: "ens9f1",
//dpdk_options: "-l 10,20 -b 0000:04:00.0 -b 0000:3b:00.0 -b 0000:3b:00.1 -b 0000:5e:00.1 ",
rec_mac: "b8:59:9f:07:86:43",
re_mac: "b8:59:9f:07:7e:2b", /* Tiogapass003 */
rec_if: "ens9f1",
dpdk_options: "-l 10,20 -b 0000:04:00.0 -b 0000:3b:00.0 -b 0000:3b:00.1 -b 0000:5e:00.0 ",
recv_affinity: 39,
send_affinity: 38,
encode_affinity: 37,
decode_affinity: 36,
statistic_affinity: 35,
ecpri_period: 800,
flow_id: 0,
trace_period: 10000000,
log_directory: "/root/ecpri-logs",
},
tx_gain: 90.0, /* TX gain (in dB) */
rx_gain: 60.0, /* RX gain (in dB) */
sample_rate: 122.88, /* MHz */
amf_list: [
{
/* address of AMF for NGAP connection. Must be modified if the AMF runs on a different host. */
amf_addr: "127.0.1.100",
},
],
/* GTP bind address (=address of the ethernet interface connected to
the AMF). Must be modified if the AMF runs on a different host. */
gtp_addr: "127.0.1.1",
gnb_id_bits: 28,
gnb_id: 0x12345,
nr_support: true,
/* list of cells */
cell_list: [],
nr_cell_list: [
{
rf_port: 0,
cell_id: 0x01,
#if TDD == 1
band: 78,
//dl_nr_arfcn: 632628, /* 3489.42 MHz */
dl_nr_arfcn: 640000, /* For Sunwave CBRS RRH: 3600 MHz */
#else
band: 7,
dl_nr_arfcn: 536020, /* 2680 MHz */
ssb_subcarrier_spacing: 15,
#endif
},
], /* nr_cell_list */
nr_cell_default: {
subcarrier_spacing: 30, /* kHz */
bandwidth: BANDWIDTH, /* MHz */
n_antenna_dl: N_ANTENNA_DL,
n_antenna_ul: N_ANTENNA_UL,
/* force the timing TA offset (optional) */
// n_timing_advance_offset: 39936,
#if TDD == 1
tdd_ul_dl_config: {
pattern1: {
#if TDD_CONFIG == 1
period: 5, /* in ms */
dl_slots: 7,
dl_symbols: /* 6 */ 2,
ul_slots: 2,
ul_symbols: 2,
#elif TDD_CONFIG == 2 // Sunwave conf: Case C DDDDDDDSUU DDDDDDDSUU 6:4:4
period: 5, /* in ms */
dl_slots: 7,
dl_symbols: 6,
ul_slots: 2,
ul_symbols: 4,
#elif TDD_CONFIG == 3
period: 5, /* in ms */
dl_slots: 6,
dl_symbols: 2,
ul_slots: 3,
ul_symbols: 2,
#endif
},
},
ssb_pos_bitmap: "10000000",
#else
ssb_pos_bitmap: "1000",
#endif
ssb_period: 20, /* in ms */
n_id_cell: 500,
plmn_list: [ {
tac: 100,
plmn: "00101",
reserved: false,
nssai: [
{
sst: 1,
},
/*{
sst: 2,
},
{
sst: 3,
sd: 50,
},*/
],
},
],
/*sib_sched_list: [
{
filename: "sib2_nr.asn",
si_periodicity: 16,
},
{
filename: "sib3_nr.asn",
si_periodicity: 16,
},
{
filename: "sib4_nr.asn",
si_periodicity: 32,
},
],
sib9: {
si_periodicity: 32
},*/
si_window_length: 40,
cell_barred: false,
intra_freq_reselection: true,
q_rx_lev_min: -70,
q_qual_min: -20,
p_max: 10, /* dBm */
root_sequence_index: 1, /* PRACH root sequence index */
/* Scheduling request period (slots). */
sr_period: 40,
dmrs_type_a_pos: 2,
/* to limit the number of HARQ feedback in UL, use pdsch_harq_ack_max;
allows to workaround issues with SM-G977N for example */
//pdsch_harq_ack_max: 2,
prach: {
#if TDD == 1
prach_config_index: 160, /* format B4, subframe 9 */
msg1_subcarrier_spacing: 30, /* kHz */
#else
prach_config_index: 16, /* subframe 1 every frame */
#endif
msg1_fdm: 1,
msg1_frequency_start: 0,
zero_correlation_zone_config: 15,
preamble_received_target_power: -110, /* in dBm */
preamble_trans_max: 7,
power_ramping_step: 4, /* in dB */
ra_response_window: 20, /* in slots */
restricted_set_config: "unrestricted_set",
ra_contention_resolution_timer: 64, /* in ms */
ssb_per_prach_occasion: 1,
cb_preambles_per_ssb: 8,
},
pdcch: {
n_rb_coreset0: 48,
n_symb_coreset0: 1,
search_space0_index: 0,
dedicated_coreset: {
rb_start: -1, /* -1 to have the maximum bandwidth */
l_crb: -1, /* -1 means all the bandwidth */
duration: 1,
precoder_granularity: "sameAsREG_bundle",
},
css: {
n_candidates: [ 0, 0, 1, 0, 0 ],
},
rar_al_index: 2,
si_al_index: 2,
uss: {
n_candidates: [ 0, 2, 1, 0, 0 ],
dci_0_1_and_1_1: true,
},
al_index: 1,
},
pdsch: {
mapping_type: "typeA",
dmrs_add_pos: 1,
dmrs_type: 1,
dmrs_max_len: 1,
k0: 0, /* delay in slots from DCI to PDSCH */
/* delay in slots from PDSCH to PUCCH/PUSCH ACK/NACK */
#if TDD == 1
#if TDD_CONFIG == 1
k1: [ 8, 7, 7, 6, 5, 4, 12 /* , 11 */ ],
#elif TDD_CONFIG == 2
k1: [ 8, 7, 7, 6, 5, 4, 12, 11 ],
#elif TDD_CONFIG == 3
k1: [ 7, 6, 6, 5, 5, 4 ],
#endif
#else
k1: 4,
#endif
mcs_table: "qam256",
rar_mcs: 2,
si_mcs: 6,
/* If defined, force the PDSCH MCS for all UEs. Otherwise it is computed
* based on DL channel quality estimation */
/* mcs: 24, */
#if NR_TEST_MODE != -1
/* hardcoded scheduling parameters */
n_layer: N_ANTENNA_DL,
#if N_ANTENNA_DL >= 4
n_dmrs_cdm_groups: 2,
#else
n_dmrs_cdm_groups: 1,
#endif
/* If defined, force the PDSCH MCS for all UEs. Otherwise it is computed
* based on DL channel quality estimation */
mcs: 28,
fer: 0,
#endif
},
csi_rs: {
nzp_csi_rs_resource: [
{
csi_rs_id: 0,
#if N_ANTENNA_DL == 1
n_ports: 1,
frequency_domain_allocation: "row2",
bitmap: "100000000000",
cdm_type: "no_cdm",
#elif N_ANTENNA_DL == 2
n_ports: 2,
frequency_domain_allocation: "other",
bitmap: "100000",
cdm_type: "fd_cdm2",
#elif N_ANTENNA_DL == 4
n_ports: 4,
frequency_domain_allocation: "row4",
bitmap: "100",
cdm_type: "fd_cdm2",
#elif N_ANTENNA_DL == 8
n_ports: 8,
frequency_domain_allocation: "other",
bitmap: "110011",
cdm_type: "fd_cdm2",
#else
#error unsupported number of DL antennas
#endif
density: 1,
first_symb: 4,
rb_start: 0,
l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */
power_control_offset: 0, /* dB */
power_control_offset_ss: 0, /* dB */
period: 80,
offset: 1, /* != 0 to avoid collision with SSB */
qcl_info_periodic_csi_rs: 0,
},
#define USE_TRS
#ifdef USE_TRS
/* TRS : period of 40 ms, slots 1 & 2, symbols 4 and 8 */
{
csi_rs_id: 1,
n_ports: 1,
frequency_domain_allocation: "row1",
bitmap: "0001",
cdm_type: "no_cdm",
density: 3,
first_symb: 4,
rb_start: 0,
l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */
power_control_offset: 0, /* dB */
power_control_offset_ss: 0, /* dB */
period: 40,
offset: 11,
qcl_info_periodic_csi_rs: 0,
},
{
csi_rs_id: 2,
n_ports: 1,
frequency_domain_allocation: "row1",
bitmap: "0001",
cdm_type: "no_cdm",
density: 3,
first_symb: 8,
rb_start: 0,
l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */
power_control_offset: 0, /* dB */
power_control_offset_ss: 0, /* dB */
period: 40,
offset: 11,
qcl_info_periodic_csi_rs: 0,
},
{
csi_rs_id: 3,
n_ports: 1,
frequency_domain_allocation: "row1",
bitmap: "0001",
cdm_type: "no_cdm",
density: 3,
first_symb: 4,
rb_start: 0,
l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */
power_control_offset: 0, /* dB */
power_control_offset_ss: 0, /* dB */
period: 40,
offset: 12,
qcl_info_periodic_csi_rs: 0,
},
{
csi_rs_id: 4,
n_ports: 1,
frequency_domain_allocation: "row1",
bitmap: "0001",
cdm_type: "no_cdm",
density: 3,
first_symb: 8,
rb_start: 0,
l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */
power_control_offset: 0, /* dB */
power_control_offset_ss: 0, /* dB */
period: 40,
offset: 12,
qcl_info_periodic_csi_rs: 0,
},
#endif
],
nzp_csi_rs_resource_set: [
{
csi_rs_set_id: 0,
nzp_csi_rs_resources: [ 0 ],
repetition: false,
},
#ifdef USE_TRS
{
csi_rs_set_id: 1,
nzp_csi_rs_resources: [ 1, 2, 3, 4 ],
repetition: false,
trs_info: true,
},
#endif
],
csi_im_resource: [
{
csi_im_id: 0,
pattern: 1,
subcarrier_location: 8,
symbol_location: 8,
rb_start: 0,
l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */
period: 80,
offset: 1, /* != 0 to avoid collision with SSB */
},
],
csi_im_resource_set: [
{
csi_im_set_id: 0,
csi_im_resources: [ 0 ],
}
],
/* ZP CSI-RS to set the CSI-IM REs to zero */
zp_csi_rs_resource: [
{
csi_rs_id: 0,
frequency_domain_allocation: "row4",
bitmap: "100",
n_ports: 4,
cdm_type: "fd_cdm2",
first_symb: 8,
density: 1,
rb_start: 0,
l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */
period: 80,
offset: 1,
},
],
p_zp_csi_rs_resource_set: [
{
zp_csi_rs_resources: [ 0 ],
},
],
csi_resource_config: [
{
csi_rsc_config_id: 0,
nzp_csi_rs_resource_set_list: [ 0 ],
resource_type: "periodic",
},
{
csi_rsc_config_id: 1,
csi_im_resource_set_list: [ 0 ],
resource_type: "periodic",
},
#ifdef USE_TRS
{
csi_rsc_config_id: 2,
nzp_csi_rs_resource_set_list: [ 1 ],
resource_type: "periodic",
},
#endif
],
csi_report_config: [
{
resources_for_channel_measurement: 0,
csi_im_resources_for_interference: 1,
report_config_type: "periodic",
period: 80,
report_quantity: "CRI_RI_PMI_CQI",
#if N_ANTENNA_DL > 1
codebook_config: {
codebook_type: "type1",
sub_type: "typeI_SinglePanel",
#if N_ANTENNA_DL == 2
#elif N_ANTENNA_DL == 4
n1: 2,
n2: 1,
codebook_mode: 1,
#elif N_ANTENNA_DL == 8
n1: 4,
n2: 1,
codebook_mode: 1,
#endif
},
#endif
cqi_table: 2,
subband_size: "value1",
},
],
},
pucch: {
pucch_group_hopping: "neither",
hopping_id: -1, /* -1 = n_cell_id */
p0_nominal: -90,
#if 0
pucch0: {
initial_cyclic_shift: 1,
n_symb: 1,
},
#else
pucch1: {
n_cs: 3,
n_occ: 3,
freq_hopping: true,
},
#endif
#if 1
pucch2: {
n_symb: 2,
n_prb: 1,
freq_hopping: true,
simultaneous_harq_ack_csi: false,
max_code_rate: 0.25,
},
#endif
#if 0
pucch3: {
bpsk: false,
additional_dmrs: false,
freq_hopping: true,
n_prb: 1,
simultaneous_harq_ack_csi: false,
max_code_rate: 0.25,
},
#endif
#if 0
pucch4: {
occ_len: 4,
bpsk: false,
additional_dmrs: false,
freq_hopping: true,
simultaneous_harq_ack_csi: false,
max_code_rate: 0.25,
},
#endif
},
#if USE_SRS
srs: {
#if TDD_CONFIG == 1 || TDD_CONFIG == 2
srs_symbols: [ 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 ],
#elif TDD_CONFIG == 3
srs_symbols: [ 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ],
#endif
srs_resource: [
{
srs_resource_id: 0,
n_ports: N_ANTENNA_UL,
resource_type: "periodic",
period: 80, /* in slots */
}
],
srs_resource_set: [
{
srs_resource_id_list: [ 0 ],
},
],
},
#endif
pusch: {
mapping_type: "typeA",
n_symb: 14,
dmrs_add_pos: 1,
dmrs_type: 1,
dmrs_max_len: 1,
tf_precoding: false,
mcs_table: "qam256", /* without transform precoding */
mcs_table_tp: "qam256", /* with transform precoding */
ldpc_max_its: 5,
k2: 4, /* delay in slots from DCI to PUSCH */
p0_nominal_with_grant: -76,
msg3_k2: 7,
msg3_mcs: 4,
msg3_delta_power: 0, /* in dB */
beta_offset_ack_index: 9,
/* if defined, force the PUSCH MCS for all UEs. Otherwise it is
computed from the last received PUSCH. */
/* mcs: 16, */
},
/* MAC configuration */
mac_config: {
msg3_max_harq_tx: 5,
ul_max_harq_tx: 5, /* max number of HARQ transmissions for uplink */
dl_max_harq_tx: 5, /* max number of HARQ transmissions for downlink */
ul_max_consecutive_retx: 30, /* disconnect UE if reached */
dl_max_consecutive_retx: 30, /* disconnect UE if reached */
periodic_bsr_timer: 20,
retx_bsr_timer: 320,
periodic_phr_timer: 500,
prohibit_phr_timer: 200,
phr_tx_power_factor_change: "dB3",
sr_prohibit_timer: 0, /* in ms, 0 to disable the timer */
sr_trans_max: 64,
},
cipher_algo_pref: [],
integ_algo_pref: [2, 1],
inactivity_timer: 10000,
drb_config: "drb_nr.cfg",
#if NR_TEST_MODE != -1
#if NR_TEST_MODE == 0
test_mode: {
type: "pdsch",
rnti: 0x100,
pdsch_retx: 0,
},
#elif NR_TEST_MODE == 1
test_mode: {
type: "pusch",
rnti: 0x100,
pusch_retx: 0,
},
#else
test_mode: {
type: "load",
ue_count: UE_COUNT,
},
#endif
#endif
},
}
/* lteenb configuration file version ##VERSION##
* Copyright (C) 2019-2021 Amarisoft
* NR SA FDD or TDD cell */
#define TDD 1 // Values: 0 (NR FDD), 1(NR TDD)
#define TDD_CONFIG 2 // Values: 1, 2 or 3
#define N_ANTENNA_DL 4 // Values: 1 (SISO), 2 (MIMO 2x2), 4 (MIMO 4x4)
#define N_ANTENNA_UL 1 // Values: 1, 2, 4
#define BANDWIDTH 100 // NR cell bandwidth
#define CPRI 1
#define NR_TEST_MODE -1
/* define to 1 to enable periodic SRS with N_ANTENNA_UL ports. Uplink
SU-MIMO is also enabled if N_ANTENNA_UL >= 2. Not all UEs support
uplink SU-MIMO. */
#define USE_SRS 0
{
//log_options: "all.level=debug,all.max_size=1",
log_options: "all.level=error,all.max_size=0,nas.level=debug,nas.max_size=1,ngap.level=debug,ngap.max_size=1,xnap.level=debug,xnap.max_size=1,rrc.level=debug,rrc.max_size=1",
log_filename: "/tmp/gnb0.log",
/* Enable remote API and Web interface */
com_addr: "0.0.0.0:9001",
rf_driver: {
name: "ecpri",
rec_mac: "b8:59:9f:07:86:42",
re_mac: "04:09:a5:0f:9f:4a", /* HFR Switch */
rec_if: "ens9f0",
dpdk_options: "-l 10,20 -b 0000:04:00.0 -b 0000:3b:00.0 -b 0000:3b:00.1 -b 0000:5e:00.1 ",
//rec_mac: "b8:59:9f:07:86:43",
//re_mac: "b8:59:9f:07:7e:2b", /* Tiogapass003 */
//rec_if: "ens9f1",
//dpdk_options: "-l 10,20 -b 0000:04:00.0 -b 0000:3b:00.0 -b 0000:3b:00.1 -b 0000:5e:00.0 ",
recv_affinity: 39,
send_affinity: 38,
encode_affinity: 37,
decode_affinity: 36,
statistic_affinity: 35,
ecpri_period: 800,
flow_id: 0,
trace_period: 10000000,
log_directory: "/root/ecpri-logs",
},
tx_gain: 90.0, /* TX gain (in dB) */
rx_gain: 60.0, /* RX gain (in dB) */
sample_rate: 122.88, /* MHz */
amf_list: [
{
/* address of AMF for NGAP connection. Must be modified if the AMF runs on a different host. */
amf_addr: "127.0.1.100",
},
],
/* GTP bind address (=address of the ethernet interface connected to
the AMF). Must be modified if the AMF runs on a different host. */
gtp_addr: "127.0.1.1",
gnb_id_bits: 28,
gnb_id: 0x12345,
nr_support: true,
/* list of cells */
cell_list: [],
nr_cell_list: [
{
rf_port: 0,
cell_id: 0x01,
#if TDD == 1
band: 78,
//dl_nr_arfcn: 632628, /* 3489.42 MHz */
dl_nr_arfcn: 640000, /* For Sunwave CBRS RRH: 3600 MHz */
#else
band: 7,
dl_nr_arfcn: 536020, /* 2680 MHz */
ssb_subcarrier_spacing: 15,
#endif
},
], /* nr_cell_list */
nr_cell_default: {
subcarrier_spacing: 30, /* kHz */
bandwidth: BANDWIDTH, /* MHz */
n_antenna_dl: N_ANTENNA_DL,
n_antenna_ul: N_ANTENNA_UL,
/* force the timing TA offset (optional) */
// n_timing_advance_offset: 39936,
#if TDD == 1
tdd_ul_dl_config: {
pattern1: {
#if TDD_CONFIG == 1
period: 5, /* in ms */
dl_slots: 7,
dl_symbols: /* 6 */ 2,
ul_slots: 2,
ul_symbols: 2,
#elif TDD_CONFIG == 2 // Sunwave conf: Case C DDDDDDDSUU DDDDDDDSUU 6:4:4
period: 5, /* in ms */
dl_slots: 7,
dl_symbols: 6,
ul_slots: 2,
ul_symbols: 4,
#elif TDD_CONFIG == 3
period: 5, /* in ms */
dl_slots: 6,
dl_symbols: 2,
ul_slots: 3,
ul_symbols: 2,
#endif
},
},
ssb_pos_bitmap: "10000000",
#else
ssb_pos_bitmap: "1000",
#endif
ssb_period: 20, /* in ms */
n_id_cell: 500,
plmn_list: [ {
tac: 100,
plmn: "00101",
reserved: false,
nssai: [
{
sst: 1,
},
/*{
sst: 2,
},
{
sst: 3,
sd: 50,
},*/
],
},
],
/*sib_sched_list: [
{
filename: "sib2_nr.asn",
si_periodicity: 16,
},
{
filename: "sib3_nr.asn",
si_periodicity: 16,
},
{
filename: "sib4_nr.asn",
si_periodicity: 32,
},
],
sib9: {
si_periodicity: 32
},*/
si_window_length: 40,
cell_barred: false,
intra_freq_reselection: true,
q_rx_lev_min: -70,
q_qual_min: -20,
p_max: 10, /* dBm */
root_sequence_index: 1, /* PRACH root sequence index */
/* Scheduling request period (slots). */
sr_period: 40,
dmrs_type_a_pos: 2,
/* to limit the number of HARQ feedback in UL, use pdsch_harq_ack_max;
allows to workaround issues with SM-G977N for example */
//pdsch_harq_ack_max: 2,
prach: {
#if TDD == 1
prach_config_index: 160, /* format B4, subframe 9 */
msg1_subcarrier_spacing: 30, /* kHz */
#else
prach_config_index: 16, /* subframe 1 every frame */
#endif
msg1_fdm: 1,
msg1_frequency_start: 0,
zero_correlation_zone_config: 15,
preamble_received_target_power: -110, /* in dBm */
preamble_trans_max: 7,
power_ramping_step: 4, /* in dB */
ra_response_window: 20, /* in slots */
restricted_set_config: "unrestricted_set",
ra_contention_resolution_timer: 64, /* in ms */
ssb_per_prach_occasion: 1,
cb_preambles_per_ssb: 8,
},
pdcch: {
n_rb_coreset0: 48,
n_symb_coreset0: 1,
search_space0_index: 0,
dedicated_coreset: {
rb_start: -1, /* -1 to have the maximum bandwidth */
l_crb: -1, /* -1 means all the bandwidth */
duration: 1,
precoder_granularity: "sameAsREG_bundle",
},
css: {
n_candidates: [ 0, 0, 1, 0, 0 ],
},
rar_al_index: 2,
si_al_index: 2,
uss: {
n_candidates: [ 0, 2, 1, 0, 0 ],
dci_0_1_and_1_1: true,
},
al_index: 1,
},
pdsch: {
mapping_type: "typeA",
dmrs_add_pos: 1,
dmrs_type: 1,
dmrs_max_len: 1,
k0: 0, /* delay in slots from DCI to PDSCH */
/* delay in slots from PDSCH to PUCCH/PUSCH ACK/NACK */
#if TDD == 1
#if TDD_CONFIG == 1
k1: [ 8, 7, 7, 6, 5, 4, 12 /* , 11 */ ],
#elif TDD_CONFIG == 2
k1: [ 8, 7, 7, 6, 5, 4, 12, 11 ],
#elif TDD_CONFIG == 3
k1: [ 7, 6, 6, 5, 5, 4 ],
#endif
#else
k1: 4,
#endif
mcs_table: "qam256",
rar_mcs: 2,
si_mcs: 6,
/* If defined, force the PDSCH MCS for all UEs. Otherwise it is computed
* based on DL channel quality estimation */
/* mcs: 24, */
#if NR_TEST_MODE != -1
/* hardcoded scheduling parameters */
n_layer: N_ANTENNA_DL,
#if N_ANTENNA_DL >= 4
n_dmrs_cdm_groups: 2,
#else
n_dmrs_cdm_groups: 1,
#endif
/* If defined, force the PDSCH MCS for all UEs. Otherwise it is computed
* based on DL channel quality estimation */
mcs: 28,
fer: 0,
#endif
},
csi_rs: {
nzp_csi_rs_resource: [
{
csi_rs_id: 0,
#if N_ANTENNA_DL == 1
n_ports: 1,
frequency_domain_allocation: "row2",
bitmap: "100000000000",
cdm_type: "no_cdm",
#elif N_ANTENNA_DL == 2
n_ports: 2,
frequency_domain_allocation: "other",
bitmap: "100000",
cdm_type: "fd_cdm2",
#elif N_ANTENNA_DL == 4
n_ports: 4,
frequency_domain_allocation: "row4",
bitmap: "100",
cdm_type: "fd_cdm2",
#elif N_ANTENNA_DL == 8
n_ports: 8,
frequency_domain_allocation: "other",
bitmap: "110011",
cdm_type: "fd_cdm2",
#else
#error unsupported number of DL antennas
#endif
density: 1,
first_symb: 4,
rb_start: 0,
l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */
power_control_offset: 0, /* dB */
power_control_offset_ss: 0, /* dB */
period: 80,
offset: 1, /* != 0 to avoid collision with SSB */
qcl_info_periodic_csi_rs: 0,
},
#define USE_TRS
#ifdef USE_TRS
/* TRS : period of 40 ms, slots 1 & 2, symbols 4 and 8 */
{
csi_rs_id: 1,
n_ports: 1,
frequency_domain_allocation: "row1",
bitmap: "0001",
cdm_type: "no_cdm",
density: 3,
first_symb: 4,
rb_start: 0,
l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */
power_control_offset: 0, /* dB */
power_control_offset_ss: 0, /* dB */
period: 40,
offset: 11,
qcl_info_periodic_csi_rs: 0,
},
{
csi_rs_id: 2,
n_ports: 1,
frequency_domain_allocation: "row1",
bitmap: "0001",
cdm_type: "no_cdm",
density: 3,
first_symb: 8,
rb_start: 0,
l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */
power_control_offset: 0, /* dB */
power_control_offset_ss: 0, /* dB */
period: 40,
offset: 11,
qcl_info_periodic_csi_rs: 0,
},
{
csi_rs_id: 3,
n_ports: 1,
frequency_domain_allocation: "row1",
bitmap: "0001",
cdm_type: "no_cdm",
density: 3,
first_symb: 4,
rb_start: 0,
l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */
power_control_offset: 0, /* dB */
power_control_offset_ss: 0, /* dB */
period: 40,
offset: 12,
qcl_info_periodic_csi_rs: 0,
},
{
csi_rs_id: 4,
n_ports: 1,
frequency_domain_allocation: "row1",
bitmap: "0001",
cdm_type: "no_cdm",
density: 3,
first_symb: 8,
rb_start: 0,
l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */
power_control_offset: 0, /* dB */
power_control_offset_ss: 0, /* dB */
period: 40,
offset: 12,
qcl_info_periodic_csi_rs: 0,
},
#endif
],
nzp_csi_rs_resource_set: [
{
csi_rs_set_id: 0,
nzp_csi_rs_resources: [ 0 ],
repetition: false,
},
#ifdef USE_TRS
{
csi_rs_set_id: 1,
nzp_csi_rs_resources: [ 1, 2, 3, 4 ],
repetition: false,
trs_info: true,
},
#endif
],
csi_im_resource: [
{
csi_im_id: 0,
pattern: 1,
subcarrier_location: 8,
symbol_location: 8,
rb_start: 0,
l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */
period: 80,
offset: 1, /* != 0 to avoid collision with SSB */
},
],
csi_im_resource_set: [
{
csi_im_set_id: 0,
csi_im_resources: [ 0 ],
}
],
/* ZP CSI-RS to set the CSI-IM REs to zero */
zp_csi_rs_resource: [
{
csi_rs_id: 0,
frequency_domain_allocation: "row4",
bitmap: "100",
n_ports: 4,
cdm_type: "fd_cdm2",
first_symb: 8,
density: 1,
rb_start: 0,
l_crb: -1, /* -1 means from rb_start to the end of the bandwidth */
period: 80,
offset: 1,
},
],
p_zp_csi_rs_resource_set: [
{
zp_csi_rs_resources: [ 0 ],
},
],
csi_resource_config: [
{
csi_rsc_config_id: 0,
nzp_csi_rs_resource_set_list: [ 0 ],
resource_type: "periodic",
},
{
csi_rsc_config_id: 1,
csi_im_resource_set_list: [ 0 ],
resource_type: "periodic",
},
#ifdef USE_TRS
{
csi_rsc_config_id: 2,
nzp_csi_rs_resource_set_list: [ 1 ],
resource_type: "periodic",
},
#endif
],
csi_report_config: [
{
resources_for_channel_measurement: 0,
csi_im_resources_for_interference: 1,
report_config_type: "periodic",
period: 80,
report_quantity: "CRI_RI_PMI_CQI",
#if N_ANTENNA_DL > 1
codebook_config: {
codebook_type: "type1",
sub_type: "typeI_SinglePanel",
#if N_ANTENNA_DL == 2
#elif N_ANTENNA_DL == 4
n1: 2,
n2: 1,
codebook_mode: 1,
#elif N_ANTENNA_DL == 8
n1: 4,
n2: 1,
codebook_mode: 1,
#endif
},
#endif
cqi_table: 2,
subband_size: "value1",
},
],
},
pucch: {
pucch_group_hopping: "neither",
hopping_id: -1, /* -1 = n_cell_id */
p0_nominal: -90,
#if 0
pucch0: {
initial_cyclic_shift: 1,
n_symb: 1,
},
#else
pucch1: {
n_cs: 3,
n_occ: 3,
freq_hopping: true,
},
#endif
#if 1
pucch2: {
n_symb: 2,
n_prb: 1,
freq_hopping: true,
simultaneous_harq_ack_csi: false,
max_code_rate: 0.25,
},
#endif
#if 0
pucch3: {
bpsk: false,
additional_dmrs: false,
freq_hopping: true,
n_prb: 1,
simultaneous_harq_ack_csi: false,
max_code_rate: 0.25,
},
#endif
#if 0
pucch4: {
occ_len: 4,
bpsk: false,
additional_dmrs: false,
freq_hopping: true,
simultaneous_harq_ack_csi: false,
max_code_rate: 0.25,
},
#endif
},
#if USE_SRS
srs: {
#if TDD_CONFIG == 1 || TDD_CONFIG == 2
srs_symbols: [ 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 ],
#elif TDD_CONFIG == 3
srs_symbols: [ 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 ],
#endif
srs_resource: [
{
srs_resource_id: 0,
n_ports: N_ANTENNA_UL,
resource_type: "periodic",
period: 80, /* in slots */
}
],
srs_resource_set: [
{
srs_resource_id_list: [ 0 ],
},
],
},
#endif
pusch: {
mapping_type: "typeA",
n_symb: 14,
dmrs_add_pos: 1,
dmrs_type: 1,
dmrs_max_len: 1,
tf_precoding: false,
mcs_table: "qam256", /* without transform precoding */
mcs_table_tp: "qam256", /* with transform precoding */
ldpc_max_its: 5,
k2: 4, /* delay in slots from DCI to PUSCH */
p0_nominal_with_grant: -76,
msg3_k2: 7,
msg3_mcs: 4,
msg3_delta_power: 0, /* in dB */
beta_offset_ack_index: 9,
/* if defined, force the PUSCH MCS for all UEs. Otherwise it is
computed from the last received PUSCH. */
/* mcs: 16, */
},
/* MAC configuration */
mac_config: {
msg3_max_harq_tx: 5,
ul_max_harq_tx: 5, /* max number of HARQ transmissions for uplink */
dl_max_harq_tx: 5, /* max number of HARQ transmissions for downlink */
ul_max_consecutive_retx: 30, /* disconnect UE if reached */
dl_max_consecutive_retx: 30, /* disconnect UE if reached */
periodic_bsr_timer: 20,
retx_bsr_timer: 320,
periodic_phr_timer: 500,
prohibit_phr_timer: 200,
phr_tx_power_factor_change: "dB3",
sr_prohibit_timer: 0, /* in ms, 0 to disable the timer */
sr_trans_max: 64,
},
cipher_algo_pref: [],
integ_algo_pref: [2, 1],
inactivity_timer: 10000,
drb_config: "drb_nr.cfg",
#if NR_TEST_MODE != -1
#if NR_TEST_MODE == 0
test_mode: {
type: "pdsch",
rnti: 0x100,
pdsch_retx: 0,
},
#elif NR_TEST_MODE == 1
test_mode: {
type: "pusch",
rnti: 0x100,
pusch_retx: 0,
},
#else
test_mode: {
type: "load",
ue_count: UE_COUNT,
},
#endif
#endif
},
}
/*
* Dummy transceiver driver (receives zeros and sync to real time)
*
* Copyright (C) 2012-2021 Amarisoft
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <getopt.h>
#include <math.h>
#include <assert.h>
#include <unistd.h>
#include <sys/time.h>
#include "trx_driver.h"
typedef int BOOL;
typedef struct {
int sample_rate;
int tx_channel_count;
int rx_channel_count;
int64_t rx_timestamp;
int64_t rx_count;
int64_t tx_count;
BOOL dump_max;
float max_sample;
int sat_count;
int64_t last_disp_time;
} TRXDummyState;
static int64_t get_time_us(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
}
static void trx_dummy_end(TRXState *s1)
{
TRXDummyState *s = s1->opaque;
printf("rx_count: %" PRId64 "\n", s->rx_count);
printf("tx_count: %" PRId64 "\n", s->tx_count);
free(s);
}
static inline int64_t ts_to_time(TRXDummyState *s, int64_t ts)
{
int n, r;
n = (ts / s->sample_rate);
r = (ts % s->sample_rate);
return (int64_t)n * 1000000 + (((int64_t)r * 1000000) / s->sample_rate);
}
static void trx_dummy_write(TRXState *s1, trx_timestamp_t timestamp, const void **samples, int count, int flags, int rf_port_index)
{
TRXDummyState *s = s1->opaque;
if (!(flags & TRX_WRITE_FLAG_PADDING) && s->dump_max) {
const float *tab;
int i, j;
float v_max, v;
v_max = s->max_sample;
for(j = 0; j < s->tx_channel_count; j++) {
tab = (const float *)samples[j];
for(i = 0; i < count * 2; i++) {
v = fabsf(tab[i]);
/* Note: 1.0 corresponds to the maximum value */
if (v >= 1.0)
s->sat_count++;
if (v > v_max) {
v_max = v;
}
}
}
s->max_sample = v_max;
if ((get_time_us() - s->last_disp_time) >= 2000000) {
printf("max_sample=%0.3f sat=%d\n", s->max_sample, s->sat_count);
s->max_sample = 0;
s->sat_count = 0;
s->last_disp_time = get_time_us();
}
}
s->tx_count += count;
}
static int trx_dummy_read(TRXState *s1, trx_timestamp_t *ptimestamp, void **psamples, int count, int rf_port)
{
TRXDummyState *s = s1->opaque;
int64_t end_time, d;
TRXComplex *samples;
int j;
*ptimestamp = s->rx_timestamp;
s->rx_timestamp += count;
s->rx_count += count;
end_time = ts_to_time(s, s->rx_timestamp);
/* Since we don't have a real sample source, we just return zero
samples and use the PC real time clock as time source */
for(;;) {
d = end_time - get_time_us();
if (d <= 0)
break;
if (d > 10000)
d = 10000;
usleep(d);
}
for(j = 0; j < s->rx_channel_count; j++) {
samples = psamples[j];
memset(samples, 0, count * sizeof(TRXComplex));
}
return count;
}
/* This function can be used to automatically set the sample
rate. Here we don't implement it, so the user has to force a given
sample rate with the "sample_rate" configuration option */
static int trx_dummy_get_sample_rate(TRXState *s, TRXFraction *psample_rate,
int *psample_rate_num, int sample_rate_min)
{
return -1;
}
static int trx_dummy_start(TRXState *s1, const TRXDriverParams *p)
{
TRXDummyState *s = s1->opaque;
struct timeval tv;
if (p->rf_port_count != 1)
return -1; /* only one TX port is supported */
s->sample_rate = p->sample_rate[0].num / p->sample_rate[0].den;
s->tx_channel_count = p->tx_channel_count;
s->rx_channel_count = p->rx_channel_count;
gettimeofday(&tv, NULL);
/* compute first RX timetamp in sample rate units */
s->rx_timestamp = (int64_t)tv.tv_sec * s->sample_rate +
((int64_t)tv.tv_usec * s->sample_rate / 1000000);
s->last_disp_time = get_time_us();
return 0;
}
int trx_driver_init(TRXState *s1)
{
TRXDummyState *s;
double val;
if (s1->trx_api_version != TRX_API_VERSION) {
fprintf(stderr, "ABI compatibility mismatch between LTEENB and TRX driver (LTEENB ABI version=%d, TRX driver ABI version=%d)\n",
s1->trx_api_version, TRX_API_VERSION);
return -1;
}
s = malloc(sizeof(TRXDummyState));
memset(s, 0, sizeof(*s));
s->dump_max = 0;
/* option to dump the maximum sample value */
if (trx_get_param_double(s1, &val, "dump_max") >= 0)
s->dump_max = (val != 0);
s1->opaque = s;
s1->trx_end_func = trx_dummy_end;
s1->trx_write_func = trx_dummy_write;
s1->trx_read_func = trx_dummy_read;
s1->trx_start_func = trx_dummy_start;
s1->trx_get_sample_rate_func = trx_dummy_get_sample_rate;
return 0;
}
import subprocess
import struct
import random
import os
import hashlib
def _hash(x):
return hashlib.sha256(repr(x).encode()).hexdigest()[:8]
def print_hash(name, x):
print("{:<32}: {}".format(name, _hash(x)))
def parse_binary(b):
return ''.join(map(lambda x:format(int(x), '0>2x'), b))
def parse_frame(buf):
c = [0]
def b(c, n):
ret = buf[c[0]:c[0]+n]
c[0] += n
return ret
dst_mac = b(c, 6)
src_mac = b(c, 6)
ether_type = b(c, 2)
ecpri_common_header = b(c, 4)
pc_id = b(c, 2)
seq_id = b(c, 2)
iq_samples = b(c, 240)
avg = sum(map(lambda x:int(x), iq_samples))
s = '''
dst_mac, src_mac, ether_type: {}, {}, {}
ecpri_common_header: {}
pc_id, seq_id: {}, {}
IQ samples: {}'''.format(
parse_binary(dst_mac),
parse_binary(src_mac),
parse_binary(ether_type),
parse_binary(ecpri_common_header),
parse_binary(pc_id),
parse_binary(seq_id),
parse_binary(iq_samples))[1:]
return (avg == 0, s)
def read_trace(name, n):
log_directory = '/root/ecpri-logs'
file_name = '{}/{}'.format(log_directory, name)
n = min(n * 262, os.path.getsize(file_name))
f = open(file_name, "rb")
data = f.read(n)
f.close()
print(name + ", frame number = " + str(n / 262))
return data
def print_frame(data):
n = len(data)
frame_len = 262
for i in range(int(n / frame_len)):
empty, s = parse_frame(data[i * frame_len:(i+1) * frame_len])
if empty:
continue
print("Frame example:\n\n{}\n".format(s))
break
def analyze_tdd(data):
null_frames = 0
tdd_switch_list = []
prev_empty = False
prev_i = 0
n = len(data)
frame_len = 262
for i in range(int(n / frame_len)):
empty, s = parse_frame(data[i * frame_len:(i+1) * frame_len])
if i > 0 and empty != prev_empty:
tdd_switch_list.append(i-prev_i)
prev_i = i
null_frames += empty
prev_empty = empty
tdd_switch_list.append(i-prev_i)
print('Frame analyzed: ' + str(i))
total_frames = int(n / frame_len)
ratio = total_frames / null_frames if null_frames > 0 else 'inf'
print('TDD ratio: {}\n'.format(ratio))
print('TDD switch list: ' + ', '.join(map(str, tdd_switch_list)) + '\n')
def print_frame_list(data, start, end):
n = len(data)
frame_len = 262
end = int(n / frame_len) if end == -1 else end
end = min(int(n / frame_len), end)
for i in range(start, end):
empty, s = parse_frame(data[i * frame_len:(i+1) * frame_len])
print(s)
print('')
def print_iq_list(data, start, end, tx=False):
n = len(data)
frame_len = 64 * 4
end = int(n / frame_len) if end == -1 else end
end = min(int(n / frame_len), end)
for i in range(start * 4, end * 4, 4):
iq = data[i * frame_len:(i+1) * frame_len]
iq_packed = []
k = 0
h = _hash(iq)
prev_x = 2
for x in map(lambda x: int(x != 0), iq):
if x == prev_x:
iq_packed[-1][0] += 1
else:
iq_packed.append([1, str(x) * 2])
prev_x = x
print(h + " " + " ".join(map(lambda x: "{}*{}".format(*x), iq_packed)))
def read_trx_trace(name, n, channels):
log_directory = '/root/ecpri-logs'
file_name = '{}/{}'.format(log_directory, name)
n = min(n * (4 * channels * 64), os.path.getsize(file_name))
f = open(file_name, "rb")
data = f.read(n)
f.close()
print(name + ", frame number = " + str(n / (4 * channels * 64)))
return data
def analyze_trx_tdd(data, channels):
null_frames = 0
tdd_switch_list = []
prev_empty = False
prev_i = 0
n = len(data)
frame_len = channels * 4 * 64
max_iq_sample = 0
min_iq_sample = 0
avg_iq_sample = 0
for i in range(int(n / frame_len)):
iq_samples = data[i * frame_len:(i+1) * frame_len]
iq_samples = [struct.unpack('f', iq_samples[4*j:4*(j+1)])[0] for j in range(int(len(iq_samples) / 4))]
iq_samples_abs_avg = sum(map(abs, iq_samples))
max_iq_sample = max(max_iq_sample, max(iq_samples))
min_iq_sample = min(min_iq_sample, min(iq_samples))
avg_iq_sample += iq_samples_abs_avg
empty = iq_samples_abs_avg < 0.1
if i == 0:
first_tdd_period = empty
if i > 0 and empty != prev_empty:
tdd_switch_list.append(i-prev_i)
prev_i = i
null_frames += empty
prev_empty = empty
tdd_switch_list.append(i-prev_i)
avg_iq_sample /= (n / frame_len) * (64 * channels)
print('Max IQ sample: {}, Min IQ sample: {}'.format(max_iq_sample, min_iq_sample))
print('Avg IQ sample: {}'.format(avg_iq_sample))
print('Frame analyzed: ' + str(i))
total_frames = int(n / frame_len)
ratio = total_frames / null_frames if null_frames > 0 else 'inf'
print('TDD ratio: {}\n'.format(ratio))
print('TDD switch list: ' + ', '.join(map(str, tdd_switch_list)) + '\n')
print('First TDD period: ' + "Not emitting" if first_tdd_period else "emitting")
BF1_PATH="../bf1/bf1"
def float_to_byte(float_list):
iq_samples_bytes = bytearray(64 * 4)
for i in range(64):
b = struct.pack('f', float_list[i])
for j in range(4):
iq_samples_bytes[i * 4 + j] = b[j]
return bytes(iq_samples_bytes)
def byte_to_float(b):
float_list = []
for i in range(64):
x = struct.unpack('f', b[4 * i:4 * (i+1)])[0]
float_list.append(x)
return float_list
def compress(data):
s = subprocess.run(
[BF1_PATH, "c"],
input=data,
capture_output=True,
)
return s.stdout
def decompress(data):
s = subprocess.run([BF1_PATH, "d"],
input=data,
capture_output=True,
)
return s.stdout
def open_all(n):
return (
read_trace('tx.trace', n),
read_trace('rx.trace', n),
read_trx_trace('trxw.trace', n, 4),
read_trx_trace('trxr.trace', n, 1),
)
def analyze_all_tdd(n=0, data=None):
if data:
tx_data, rx_data, trxw_data, trxr_data = data
print("TX")
analyze_tdd(tx_data)
print("RX")
analyze_tdd(rx_data)
print("TRXW")
analyze_trx_tdd(trxw_data, 4)
print("TRXR")
analyze_trx_tdd(trxr_data, 1)
else:
data = read_trace('rx.trace', n)
analyze_tdd(data)
data = read_trace('tx.trace', n)
analyze_tdd(data)
data = read_trx_trace('trxw.trace', n, 4)
analyze_trx_tdd(data, 4)
data = read_trx_trace('trxr.trace', n, 1)
analyze_trx_tdd(data, 1)
def check_tx_trxw(n=0, data=None):
if data:
tx_data, rx_data, trxw_data, trxr_data = data
else:
tx_data = read_trace('tx.trace', n)
trxw_data = read_trx_trace('trxw.trace', n, 4)
for i in range(int(min(len(tx_data) / 262, len(trxw_data) / (64 * 4 * 4)))):
for j in range(1):
k = i * 4 + j
tx_iq = tx_data[(i * 262 + 22 + j * 60):(i * 262 + 22 + (j+1) * 60)]
trxw_iq = trxw_data[(k * (64 * 4)):((k+1) * (64 * 4))]
tx_iq_d = decompress(tx_iq)
trxw_iq_d = compress(trxw_iq)
if (_hash(tx_iq) != _hash(trxw_iq_d)) or (_hash(trxw_iq) != _hash(tx_iq_d)):
print((i,j))
print_hash("TX", tx_iq)
print_hash("TRX compressed", trxw_iq_d)
print_hash("TRX", trxw_iq)
print_hash("TX decompressed", tx_iq_d)
def print_all(n=0, data=None):
if data:
tx_data, rx_data, trxw_data, trxr_data = data
print('TX_DATA')
print_frame_list(tx_data, 0, 10)
print('RX_DATA')
print_frame_list(rx_data, 0, 10)
print('TRXW_DATA')
print('TRXR_DATA')
print_iq_list(trxr_data, 0, 10)
data = open_all(100000)
#check_tx_trxw(data=data)
#print_all(data=data)
analyze_all_tdd(data=data)
export PTP_INSTALL="/root/linuxptp"
export INTERFACE="ens9f0"
export DPDK_INSTALL="/root/dpdk-21.11"
export LTEENB="/root/enb/lteenb"
export ENB_CONFIG="enb.cfg"
export LOG_DIRECTORY="/root/ecpri-logs"
export LD_LIBRARY_PATH="/root/ecpri-priv:/root/enb:$LD_LIBRARY_PATH"
export AMARISOFT_PATH="/root/.amarisoft"
export PTP_INSTALL="/root/linuxptp"
export INTERFACE="ens9f0"
export DPDK_INSTALL="/root/dpdk-stable-20.11.3"
export LTEENB="/root/enb/lteenb"
export ENB_CONFIG="enb-hfr.cfg"
export LOG_DIRECTORY="/home/hfr/ecpri-logs"
export LD_LIBRARY_PATH="/root/ecpri-priv:/root/enb:$LD_LIBRARY_PATH"
export AMARISOFT_PATH="/root/.amarisoft"
#!/bin/bash
DIR=$(dirname $(realpath $0))
cd $DPDK_INSTALL/usertools;
python3 dpdk-hugepages.py --setup 2G;
cd $DIR/.. && make;
ln -sf $DIR/../libtrx_ecpri_dpdk.so $(dirname $LTEENB)/trx_ecpri.so
chrt -f 97 taskset -c 39 phc2sys -m -c $INTERFACE -s CLOCK_REALTIME -O0 -f $PTP_INSTALL/configs/G.8275.1.cfg
chrt -f 97 taskset -c 38 ptp4l -H -i $INTERFACE -m -f $PTP_INSTALL/configs/G.8275.1.cfg
#!/bin/bash
PRIV="/root/ecpri-priv"
PUBL="/root/trx-ecpri"
rsync -ah --delete --exclude .git $PRIV/ $PUBL/;
rm -rf \
$PUBL/private.c\
$PUBL/trx_driver.h\
$PUBL/push-public.sh\
$PUBL/test-eNB/private.c\
$PUBL/test-eNB/trx_driver.h\
;
#!/bin/bash
DIR=$(dirname $(realpath $0))
$DIR/stop-cpri.sh
systemctl start lte
#!/bin/bash
DIR=$(dirname $(realpath $0))
systemctl stop lte;
$DIR/stop-ecpri.sh;
cd $DIR/..;
#make clean;
make &&
#$DIR/launch-ptp > $LOG_DIRECTORY/ptp.log 2> $LOG_DIRECTORY/ptp.error &
#$DIR/launch-phc2sys > $LOG_DIRECTORY/phc2sys.log 2> $LOG_DIRECTORY/phc2sys.error &
$LTEENB $DIR/../enb-configs/$ENB_CONFIG
#!/bin/bash
systemctl stop lte
#!/bin/bash
ECPRI_PID=$(ps aux | grep test-dpdk-ecpri | grep -v grep | awk '{print $2;}')
if [ -n "$ECPRI_PID" ]; then
kill $ECPRI_PID;
fi
LTE_PID=$(ps -ec -o pid,cmd | grep lteenb | grep -v grep | awk '{print $1;}')
if [ -n "$LTE_PID" ]; then
kill -9 $LTE_PID;
fi
killall ptp4l 2> /dev/null;
killall phc2sys 2> /dev/null;
# Copyright (C) 2014-2021 Amarisoft
# TRX Makefile version 2021-07-12
CC=gcc -m64 -msse4.1
CXX=g++ -m64 -msse4.1
CFLAGS=-O2 -fno-strict-aliasing -Wall -pedantic -std=gnu17
CFLAGS +=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
CFLAGS += -DALLOW_EXPERIMENTAL_API
CXXFLAGS=$(CFLAGS)
LIB=trx_ecpri_dpdk
LIBDIR=/root/ecpri-priv
LDFLAGS=-l$(LIB)
PROG=test-dpdk-ecpri
all: $(PROG)
$(PROG): $(PROG).o
$(CC) -L$(LIBDIR) -o $@ $< $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f $(PROG) *.o *~ *.d
-include $(wildcard *.d)
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <getopt.h>
#include <immintrin.h>
#include <inttypes.h>
#include <limits.h>
#include <linux/if_packet.h>
#include <math.h>
#include <netdb.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <net/if.h>
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "trx_driver.h"
typedef struct {
const char * re_mac;
const char * rec_mac;
const char * rec_if;
const char * dpdk_options;
const char * log_directory;
int recv_affinity;
int send_affinity;
int encode_affinity;
int decode_affinity;
int statistic_affinity;
int ecpri_period;
int flow_id;
int sample_rate;
int trace_period;
} TRXEcpriState;
static void log_error(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " ERROR [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
puts(line);
exit(EXIT_FAILURE);
}
static void log_info(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " INFO [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
puts(line);
}
// Timestamps utils
#define NSEC_PER_SEC INT64_C(1000000000)
static struct timespec int_to_ts(int64_t t) {
struct timespec ts;
ts.tv_sec = t / NSEC_PER_SEC;
ts.tv_nsec = t - (ts.tv_sec * NSEC_PER_SEC);
return ts;
}
static int64_t ts_to_int(struct timespec ts) {
return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
}
static void add_ns(struct timespec *t, int64_t ns) {
t->tv_nsec += ns;
while (t->tv_nsec >= ((int64_t)NSEC_PER_SEC)) {
t->tv_sec += 1;
t->tv_nsec -= NSEC_PER_SEC;
}
}
static int64_t calcdiff_ns(struct timespec t1, struct timespec t2) {
int64_t diff;
diff = NSEC_PER_SEC * ((int)t1.tv_sec - (int)t2.tv_sec);
diff += ((int)t1.tv_nsec - (int)t2.tv_nsec);
return diff;
}
TRXState s1;
float ** tx_samples_zeroes;
float ** tx_samples_ones;
float ** rx_samples;
void dummy_enb_init(TRXState *s1, TRXEcpriState *s);
static void enb(TRXState *s1, TRXEcpriState * s);
int main(int argc, char * argv[]) {
(void) argc;
(void) argv;
TRXEcpriState *s;
// Lock all current and future pages from preventing of being paged to
// swap
if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
log_error("TRX_ECPRI", "mlockall failed");
}
s = malloc(sizeof(TRXEcpriState));
memset(s, 0, sizeof(*s));
#if 0
// tiogapass-003 MT27710
s->rec_mac = "b8:59:9f:07:7e:2a";
//s->re_mac = "04:09:a5:0f:9f:4c"; // Lille M6424 Switch
s->re_mac = "b8:59:9f:07:86:42"; // tiogapass-004 MT27710
//s->re_mac = "b4:96:91:a7:1c:f4"; // tiogapass-004 XXV710DA2T port0
s->rec_if = "ens9f0";
s->dpdk_options = "-l 10,20 -b 0000:04:00.0 -b 0000:5e:00.1 ";
#endif
#if 0
// tiogapass-003 MT27710 port1
s->rec_mac = "b8:59:9f:07:7e:2b";
s->re_mac = "04:09:a5:0f:9f:4c"; // Lille M6424 Switch
//s->re_mac = "b8:59:9f:07:86:42"; // tiogapass-004 MT27710
//s->re_mac = "b4:96:91:a7:1c:f5"; // tiogapass-004 XXV710DA2T port1
s->rec_if = "ens9f1";
s->dpdk_options = "-l 10,20 -b 0000:04:00.0 -b 0000:5e:00.0 ";
#endif
#if 0
// hfr-tiogapass-001 MT27710
s->rec_mac = "b8:59:9f:07:82:ca";
s->re_mac = "04:09:a5:0f:76:1c"; // HFR M6424 switch
s->rec_if = "ens9f0";
s->dpdk_options = "-l 10,20 -b 0000:04:00.0 -b 0000:18:00.0 -b 0000:18:00.1 -b 0000:5e:00.1 ";
#endif
#if 0
// hfr-tiogapass-001 XXV710DA2T
s->rec_mac = "b4:96:91:a7:1b:28";
s->re_mac = "04:09:a5:0f:76:1c"; // HFR M6424 switch
s->rec_if = "ens1f0";
s->dpdk_options = "-l 10,20 -b 0000:04:00.0 -b 0000:18:00.1 -b 0000:5e:00.0 -b 0000:5e:00.1 ";
#endif
#if 0
// tiogapass-004 MT27710
s->rec_mac = "b8:59:9f:07:86:42";
s->re_mac = "04:09:a5:0f:9f:4a"; // Lille M6424 Switch
//s->re_mac = "b8:59:9f:07:7e:2a"; // tiogapass-003 MT27710
s->rec_if = "ens9f0";
s->dpdk_options = "-l 10,20 -b 0000:04:00.0 -b 0000:3b:00.0 -b 0000:3b:00.1 -b 0000:5e:00.1 ";
#endif
#if 0
// tiogapass-004 MT27710 port1
s->rec_mac = "b8:59:9f:07:86:43";
//s->re_mac = "04:09:a5:0f:9f:4c"; // Lille M6424 Switch
s->re_mac = "b8:59:9f:07:0e:2b"; // tiogapass-003 MT27710 port 1
s->rec_if = "ens9f1";
s->dpdk_options = "-l 10,20 -b 0000:04:00.0 -b 0000:3b:00.0 -b 0000:3b:00.1 -b 0000:5e:00.0 ";
#endif
#if 0
// tiogapass-004 XXV710DA2T
s->rec_mac = "b4:96:91:a7:1c:f4";
s->re_mac = "04:09:a5:0f:9f:4c"; // Lille M6424 Switch
s->re_mac = "b8:59:9f:07:7e:2a"; // tiogapass-003 MT27710
s->rec_if = "ens5f0";
s->dpdk_options = "-l 10,20 -b 0000:04:00.0 -b 0000:3b:00.1 -b 0000:5e:00.0 -b 0000:5e:00.1 ";
#endif
//s->recv_affinity = 39;
//s->send_affinity = 38;
//s->prepare_affinity = 37;
//s->decompress_affinity = 36;
//s->statistic_affinity = 35;
//s->ecpri_period = 800;
//s->flow_id = 0;
//s->sample_rate = 122880000;
//s->trace_file = "/root/ecpri-logs/rx.trace";
//s->stats_file = "/root/ecpri-logs/ecpri.stats";
//s->trace_period = 1000000;
// enb.cfg
s->rec_mac = "b8:59:9f:07:86:42";
s->re_mac = "04:09:a5:0f:9f:4a";
s->rec_if = "ens9f0";
s->dpdk_options = "-l 10,20 -b 0000:04:00.0 -b 0000:3b:00.0 -b 0000:3b:00.1 -b 0000:5e:00.1 ";
s->recv_affinity = 39;
s->send_affinity = 38;
s->encode_affinity = 37;
s->decode_affinity = 36;
s->statistic_affinity = 35;
s->ecpri_period = 800;
s->flow_id = 0;
s->trace_period = 10000000;
s->log_directory = "/root/ecpri-logs";
log_info("TEST-DPDK-ECPRI", "Starting test...\n");
log_info("TEST-DPDK-ECPRI", "rec-mac: %s, re-mac: %s, rec-if: %s", s->rec_mac, s->re_mac, s->rec_if);
dummy_enb_init(&s1, s);
enb(&s1, s);
}
static void enb(TRXState * s1, TRXEcpriState * s) {
struct timespec next;
trx_timestamp_t ptimestamp;
int64_t p = 100000 * 10;
int m = 1;
int64_t first_rx_ts;
clock_gettime(CLOCK_TAI, &next);
tx_samples_zeroes = (float**) malloc(sizeof(float*) * 4);
tx_samples_ones = (float**) malloc(sizeof(float*) * 4);
rx_samples = (float**) malloc(sizeof(float*) * 4);
for(int i = 0; i < 4; i++) {
tx_samples_zeroes[i] = (float*) malloc(sizeof(float) * 65536);
tx_samples_ones[i] = (float*) malloc(sizeof(float) * 65536);
rx_samples[i] = (float*) malloc(sizeof(float) * 65536);
for(int j = 0; j < 65536; j++) {
tx_samples_zeroes[i][j] = 0.0f;
tx_samples_ones[i][j] = 1.0f;
}
}
for(int i = 0;; i++) {
int64_t tx_timestamp = 256 * (INT64_C(3840000) * ((int64_t) p * i + p)) / (INT64_C(1000000000));
add_ns(&next, p);
s1->trx_read_func2(s1, &ptimestamp, rx_samples, 32, 0, NULL);
if(i == 0)
first_rx_ts = ptimestamp;
s1->trx_write_func2(s1, first_rx_ts + 2*i*32, tx_samples_zeroes, 32, 0, NULL);
s1->trx_read_func2(s1, &ptimestamp, rx_samples, 32, 0, NULL);
s1->trx_write_func2(s1, first_rx_ts + (2*i + 1)*32, tx_samples_ones, 32, 0, NULL);
clock_nanosleep(CLOCK_TAI, TIMER_ABSTIME, &next, NULL);
}
}
#!/bin/bash
export LD_LIBRARY_PATH="/root/ecpri-priv:$LD_LIBRARY_PATH"
cd .. &&
make &&
cd test-eNB &&
make all &&
./test-dpdk-ecpri
#ifndef TRX_ECPRI_H
#define TRX_ECPRI_H
typedef struct {
const char * re_mac;
const char * rec_mac;
const char * rec_if;
int recv_affinity;
int send_affinity;
int prepare_affinity;
int decompress_affinity;
int ecpri_period;
int flow_id;
int sample_rate;
} TRXEcpriState;
int start(TRXEcpriState * s);
void log_error(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " ERROR [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
puts(line);
exit(EXIT_FAILURE);
}
void log_info(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " INFO [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
puts(line);
}
#ifdef DEBUG
void log_debug(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " DEBUG [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
puts(line);
}
#else
#define log_debug(...)
#endif
#endif
......@@ -31,6 +31,9 @@
#include <time.h>
#include <unistd.h>
#define DPDK
#ifdef DPDK
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_ether.h>
......@@ -40,45 +43,45 @@
#include <rte_ether.h>
#include <rte_ip.h>
#include <rte_udp.h>
#endif
#include "trx_driver.h"
#define DEBUG // Enables / deactivates log_debug
#define SSE4 // define if CPU supports SSE4.1
#define DST_ADDR_SYNTAX // Depends on DPDK version
#include "private/trx_driver.h"
#define SEND_LIMIT (100000)
#define TRACE
//#define DEBUG // Enables / deactivates log_debug
//#define DST_ADDR_SYNTAX // Depends on DPDK version
/* Proprietary code:
- compression / decompression of IQ samples
- fast conversion between int16_t and float
*/
#include "private.c"
//#define SEND_LIMIT (1000)
//#define TRACE
#include "utils.c"
/* eCPRI Send and Recv */
#define TX_N_SAMPLES 256
#define RX_N_SAMPLES 64
#define RX_N_CHANNEL 1
#define TX_N_CHANNEL 4
#define FRAME_FREQ INT64_C(3840000) // Basic frame frequency
#define TX_PACKET_SIZE 262
#define RX_MAX_PACKET_SIZE 262
#define TX_ECPRI_PACKET_SIZE (TX_PACKET_SIZE - 14)
#define RX_N_CHANNEL 1
#define TX_N_CHANNEL 4
#define N_SAMPLES (32)
#define TRX_MAX_GROUP 1500
#define TRX_BUF_MAX_SIZE 500000
#define TXRX_BUF_MAX_SIZE 500000
#define FRAME_FREQ INT64_C(3840000) // Basic frame frequency
#define TRX_MAX_GROUP 1000
#define TRX_BUF_MAX_SIZE 100000
#define TXRX_BUF_MAX_SIZE 100000
#define STATISTIC_REFRESH_RATE INT64_C(500 * 1000 * 1000)
#define STAT_INT_LEN "9"
#define TRACE_BUFFER_SIZE_MB 200
typedef struct {
float re;
float im;
} Complex;
typedef struct {
const uint8_t * re_mac;
const uint8_t * rec_mac;
const uint8_t * rec_if;
const uint8_t * dpdk_options;
const char * dpdk_options;
const uint8_t * log_directory;
int recv_affinity;
int send_affinity;
......@@ -88,7 +91,6 @@ typedef struct {
int ecpri_period;
int flow_id;
int sample_rate;
int trace_period;
} TRXEcpriState;
typedef struct {
......@@ -100,13 +102,12 @@ typedef struct {
typedef struct {
volatile void * buffer;
uint8_t name[64];
char name[64];
int buf_len;
int len;
volatile int write_index;
volatile int read_index;
volatile int write_ahead;
pthread_mutex_t ahead_mutex;
} ring_buffer_t;
#ifdef TRACE
......@@ -120,7 +121,7 @@ static void init_buffer(buffer_t * buffer, int64_t size) {
buffer->size = size;
buffer->buffer = (uint8_t *) malloc(size);
buffer->counter = 0;
memset(buffer->buffer, 0, size);
memset((uint8_t*) buffer->buffer, 0, size);
}
static void write_buffer(buffer_t * buffer, int i, uint8_t * source, int64_t len) {
if(buffer->counter + len >= buffer->size)
......@@ -136,25 +137,19 @@ typedef struct {
uint8_t zeroes;
} sample_group_t;
/* Proprietary code:
- compression / decompression of IQ samples
- fast conversion between int16_t and float
*/
#include "private/bf1_avx2.c"
// Buffers
static ring_buffer_t rx_rbuf; // Received packets
static ring_buffer_t trxr_rbuf; // Decoded IQ samples
static ring_buffer_t trxr_rbuf[RX_N_CHANNEL]; // Decoded IQ samples
static ring_buffer_t tx_rbuf; // Packets to send
static ring_buffer_t trxw_rbuf; // Uncompressed IQ samples
static ring_buffer_t trxw_rbuf[TX_N_CHANNEL]; // Uncompressed IQ samples
static ring_buffer_t trxw_group_rbuf; // Group of IQ samples
// Locks
pthread_mutex_t tx_mutex;
pthread_cond_t tx_cond;
pthread_mutex_t rx_mutex;
pthread_cond_t rx_cond;
pthread_mutex_t encode_mutex;
pthread_cond_t encode_cond;
pthread_mutex_t decode_mutex;
pthread_cond_t decode_cond;
pthread_mutex_t trxw_mutex;
pthread_cond_t trxw_cond;
static volatile int64_t rx_cond_counter = 0;
static volatile int64_t decode_cond_counter = 0;
// Counters
static volatile counter_stat_t recv_counter; // frames received from eRE
static volatile counter_stat_t decode_counter; // decoded frames
......@@ -165,13 +160,16 @@ static volatile counter_stat_t sent_counter; // frames sent to eRE
static volatile counter_stat_t rx_drop_counter; // frames sent to eRE
static volatile counter_stat_t tx_drop_counter; // frames sent to eRE
#define STAT_FRAME_INTERVAL INT64_C(380000)
#define EFREQ 38400
#define STAT_FRAME_INTERVAL INT64_C(EFREQ * 150)
static volatile int sync_complete = 0;
static int first_trx_write = 1;
static volatile uint8_t iq_frame_full[1024];
static volatile uint8_t iq_frame_empty[1024];
static uint8_t iq_frame_full[1024];
static uint8_t iq_frame_empty[1024];
static uint8_t pkt_frame_full[1024];
static uint8_t pkt_frame_empty[1024];
// Network
static volatile int seq_id;
......@@ -197,6 +195,16 @@ static int rbuf_write_amount(ring_buffer_t * rbuf) {
// That we way we don't have to use locks
return ((rbuf->read_index + rbuf->buf_len - rbuf->write_index - 1) % rbuf->buf_len);
}
static int rbuf_contiguous_copy(ring_buffer_t * rbuf1, ring_buffer_t * rbuf2, int n) {
int ret = n;
if(rbuf1) {
n = rbuf1->buf_len - rbuf1->read_index;
ret = n < ret ? n : ret;
}
if(rbuf2)
n = rbuf2->buf_len - rbuf2->write_index;
return n < ret ? n : ret;
}
#define RBUF_READ0(rbuf, type) (((type *) rbuf.buffer) + (rbuf.read_index * rbuf.len))
#define RBUF_WRITE0(rbuf, type) (((type *) rbuf.buffer) + (rbuf.write_index * rbuf.len))
#define RBUF_READ(rbuf, i, type) (((type *) rbuf.buffer) + (((rbuf.read_index + i) % rbuf.buf_len) * rbuf.len))
......@@ -211,27 +219,26 @@ static int rbuf_write_amount(ring_buffer_t * rbuf) {
rbuf.write_index = 0;\
rbuf.read_index = 0;\
rbuf.write_ahead = 0;\
pthread_mutex_init(&rbuf.ahead_mutex, NULL);\
} while(0)
static void log_exit(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " EXIT [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
fprintf(stderr, "%s\n", line);
// Dump useful information
fprintf(stderr,
"%13s %13s %13s %13s %13s %13s %13s %13s %13s %13s %13s %13s %13s %13s \n",
static void print_stats(FILE * f, int print_header) {
if(print_header) {
fprintf(f,
"%" STAT_INT_LEN "s "
"%" STAT_INT_LEN "s "
"%" STAT_INT_LEN "s "
"%" STAT_INT_LEN "s "
"%" STAT_INT_LEN "s "
"%" STAT_INT_LEN "s "
"%" STAT_INT_LEN "s "
"%" STAT_INT_LEN "s "
"%" STAT_INT_LEN "s "
"%" STAT_INT_LEN "s "
"%" STAT_INT_LEN "s "
"%" STAT_INT_LEN "s "
"%" STAT_INT_LEN "s "
"%" STAT_INT_LEN "s "
"\n",
"rx dropped",
"tx dropped",
"received",
......@@ -246,8 +253,23 @@ static void log_exit(const char * section, const char * msg, ...) {
"write pps",
"encode pps",
"sent pps");
fprintf(stderr,
"%13" PRIi64 " %13" PRIi64 " %13" PRIi64 " %13" PRIi64 " %13" PRIi64 " %13" PRIi64 " %13" PRIi64 " %13" PRIi64 "pps %13" PRIi64 "pps %13" PRIi64 "pps %13" PRIi64 "pps %13" PRIi64 "pps %13" PRIi64 "pps\n",
}
fprintf(f,
"%" STAT_INT_LEN "" PRIi64 " "
"%" STAT_INT_LEN "" PRIi64 " "
"%" STAT_INT_LEN "" PRIi64 " "
"%" STAT_INT_LEN "" PRIi64 " "
"%" STAT_INT_LEN "" PRIi64 " "
"%" STAT_INT_LEN "" PRIi64 " "
"%" STAT_INT_LEN "" PRIi64 " "
"%" STAT_INT_LEN "" PRIi64 " "
"%" STAT_INT_LEN "" PRIi64 "pps "
"%" STAT_INT_LEN "" PRIi64 "pps "
"%" STAT_INT_LEN "" PRIi64 "pps "
"%" STAT_INT_LEN "" PRIi64 "pps "
"%" STAT_INT_LEN "" PRIi64 "pps "
"%" STAT_INT_LEN "" PRIi64 "pps "
"\n",
rx_drop_counter.counter,
tx_drop_counter.counter,
recv_counter.counter,
......@@ -262,11 +284,30 @@ static void log_exit(const char * section, const char * msg, ...) {
write_counter.pps,
encode_counter.pps,
sent_counter.pps);
fprintf(stderr, "TX RBUF: ri %li wi %li ra %li wa %li\n", tx_rbuf.read_index, tx_rbuf.write_index, rbuf_read_amount(&tx_rbuf), rbuf_write_amount(&tx_rbuf));
fprintf(stderr, "RX RBUF: ri %li wi %li ra %li wa %li\n", rx_rbuf.read_index, rx_rbuf.write_index, rbuf_read_amount(&rx_rbuf), rbuf_write_amount(&rx_rbuf));
fprintf(stderr, "TRXW RBUF: ri %li wi %li ra %li wa %li\n", trxw_rbuf.read_index, trxw_rbuf.write_index, rbuf_read_amount(&trxw_rbuf), rbuf_write_amount(&trxw_rbuf));
fprintf(stderr, "TRXR RBUF: ri %li wi %li ra %li wa %li\n", trxr_rbuf.read_index, trxr_rbuf.write_index, rbuf_read_amount(&trxr_rbuf), rbuf_write_amount(&trxr_rbuf));
fprintf(stderr, "TRXW GROUP RBUF: ri %li wi %li ra %li wa %li\n", trxw_group_rbuf.read_index, trxw_group_rbuf.write_index, rbuf_read_amount(&trxw_group_rbuf), rbuf_write_amount(&trxw_group_rbuf));
}
static void log_exit(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
char line[256];
va_list arglist;
time(&t);
ts = *localtime(&t);
strftime(line, 80, "%m-%d %H:%M:%S", &ts);
sprintf(line + strlen(line), " EXIT [%s] ", section);
va_start(arglist, msg);
vsprintf(line + strlen(line), msg, arglist);
va_end(arglist);
fprintf(stderr, "%s\n", line);
// Dump useful information
print_stats(stderr, 1);
fprintf(stderr, "TX RBUF: ri %d wi %d ra %d wa %d\n", tx_rbuf.read_index, tx_rbuf.write_index, rbuf_read_amount(&tx_rbuf), rbuf_write_amount(&tx_rbuf));
fprintf(stderr, "RX RBUF: ri %d wi %d ra %d wa %d\n", rx_rbuf.read_index, rx_rbuf.write_index, rbuf_read_amount(&rx_rbuf), rbuf_write_amount(&rx_rbuf));
fprintf(stderr, "TRXW RBUF: ri %d wi %d ra %d wa %d\n", trxw_rbuf[0].read_index, trxw_rbuf[0].write_index, rbuf_read_amount(&trxw_rbuf[0]), rbuf_write_amount(&trxw_rbuf[0]));
fprintf(stderr, "TRXR RBUF: ri %d wi %d ra %d wa %d\n", trxr_rbuf[0].read_index, trxr_rbuf[0].write_index, rbuf_read_amount(&trxr_rbuf[0]), rbuf_write_amount(&trxr_rbuf[0]));
fprintf(stderr, "TRXW GROUP RBUF: ri %d wi %d ra %d wa %d\n", trxw_group_rbuf.read_index, trxw_group_rbuf.write_index, rbuf_read_amount(&trxw_group_rbuf), rbuf_write_amount(&trxw_group_rbuf));
fflush(stdout);
fflush(stderr);
......@@ -274,26 +315,21 @@ static void log_exit(const char * section, const char * msg, ...) {
}
#define BURST_SIZE 16
#define TX_POOL_SIZE 16
int8_t tx_data[BURST_SIZE][TX_PACKET_SIZE];
#ifdef DPDK
#include "dpdk.c"
static void send_packets(int port) {
struct rte_mbuf * pkt[BURST_SIZE];
struct rte_mbuf * pkt[TX_POOL_SIZE];
struct rte_ether_hdr *eth_hdr;
uint16_t nb_tx = 0;
for(int i = 0; i < BURST_SIZE; i++) {
for(int i = 0; i < TX_POOL_SIZE; i++) {
int pkt_size;
pkt[i] = rte_pktmbuf_alloc(mbuf_pool);
eth_hdr = rte_pktmbuf_mtod(pkt[i], struct rte_ether_hdr*);
#ifdef DST_ADDR_SYNTAX
if(port) {
eth_hdr->dst_addr = s_addr;
eth_hdr->src_addr = d_addr;
} else {
eth_hdr->dst_addr = d_addr;
eth_hdr->src_addr = s_addr;
}
#else
if(port) {
eth_hdr->d_addr = s_addr;
eth_hdr->s_addr = d_addr;
......@@ -301,15 +337,15 @@ static void send_packets(int port) {
eth_hdr->d_addr = d_addr;
eth_hdr->s_addr = s_addr;
}
#endif
eth_hdr->ether_type = htons(0xaefe);
memcpy(rte_pktmbuf_mtod_offset(pkt[i], uint8_t *, sizeof(struct rte_ether_hdr)), tx_data[i], TX_ECPRI_PACKET_SIZE);
pkt_size = TX_PACKET_SIZE;
pkt[i]->data_len = pkt_size;
pkt[i]->pkt_len = pkt_size;
}
while(nb_tx < BURST_SIZE) {
nb_tx = rte_eth_tx_burst(port, 0, pkt + nb_tx, BURST_SIZE - nb_tx);
while(nb_tx < TX_POOL_SIZE) {
int64_t x = TX_POOL_SIZE - nb_tx;
nb_tx += rte_eth_tx_burst(port, 0, pkt + nb_tx, x > BURST_SIZE ? BURST_SIZE : x);
}
/* Free any unsent packets. */
......@@ -322,6 +358,19 @@ static void send_packets(int port) {
}
/* DPDK */
#else
struct rte_mbuf {
int buf_addr;
int data_off;
};
void rte_pktmbuf_free(void * pkt) {
(void) pkt;
for(int i = 0; i < 1000; i ++)
asm("NOP");
}
#endif
static void init_counter(volatile counter_stat_t * c) {
c->counter = 0;
......@@ -346,6 +395,7 @@ static void update_counter(volatile counter_stat_t * c, int64_t v) {
#ifdef SEND_LIMIT
static void send_limit_handler(struct timespec initial, TRXEcpriState * s) {
struct timespec next;
#ifdef TRACE
if(((tx_trace_buffer.counter / 262) > SEND_LIMIT) &&
((rx_trace_buffer.counter / 262) > SEND_LIMIT) &&
((trxw_trace_buffer.counter / 1024) > SEND_LIMIT) &&
......@@ -356,9 +406,8 @@ static void send_limit_handler(struct timespec initial, TRXEcpriState * s) {
log_info("SEND_THREAD", "Packets sent: %" PRIi64, sent_counter.counter);
log_info("SEND_THREAD", "Duration: %" PRIi64, d);
log_info("SEND_THREAD", "FRAME_FREQ: %" PRIi64, FRAME_FREQ);
#ifdef TRACE
FILE * f;
uint8_t n[256];
char n[256];
log_info("SEND_THREAD", "tx_trace_buffer counter: %li", tx_trace_buffer.counter);
log_info("SEND_THREAD", "rx_trace_buffer counter: %li", rx_trace_buffer.counter);
log_info("SEND_THREAD", "trxw_trace_buffer counter: %li", trxw_trace_buffer.counter);
......@@ -366,31 +415,84 @@ static void send_limit_handler(struct timespec initial, TRXEcpriState * s) {
memset(n, '\0', 256);
sprintf(n, "%s/tx.trace", s->log_directory);
f = fopen(n, "wb+");
fwrite(tx_trace_buffer.buffer, tx_trace_buffer.counter, 1, f);
fwrite((uint8_t*) tx_trace_buffer.buffer, tx_trace_buffer.counter, 1, f);
fclose(f);
memset(n, '\0', 256);
sprintf(n, "%s/rx.trace", s->log_directory);
f = fopen(n, "wb+");
fwrite(rx_trace_buffer.buffer, rx_trace_buffer.counter, 1, f);
fwrite((uint8_t*) rx_trace_buffer.buffer, rx_trace_buffer.counter, 1, f);
fclose(f);
memset(n, '\0', 256);
sprintf(n, "%s/trxr.trace", s->log_directory);
f = fopen(n, "wb+");
fwrite(trxr_trace_buffer.buffer, trxr_trace_buffer.counter, 1, f);
fwrite((uint8_t*) trxr_trace_buffer.buffer, trxr_trace_buffer.counter, 1, f);
fclose(f);
memset(n, '\0', 256);
sprintf(n, "%s/trxw.trace", s->log_directory);
f = fopen(n, "wb+");
fwrite((uint8_t*) trxw_trace_buffer.buffer, trxw_trace_buffer.counter, 1, f);
fclose(f);
#else
if((read_counter.counter > SEND_LIMIT)) {
int64_t d;
clock_gettime(CLOCK_TAI, &next);
d = calcdiff_ns(next, initial);
log_info("SEND_THREAD", "Packets sent: %" PRIi64, sent_counter.counter);
log_info("SEND_THREAD", "Duration: %" PRIi64, d);
log_info("SEND_THREAD", "FRAME_FREQ: %" PRIi64, FRAME_FREQ);
FILE * f;
char n[256];
int wi;
memset(n, '\0', 256);
sprintf(n, "%s/tx.trace", s->log_directory);
f = fopen(n, "wb+");
wi = (tx_rbuf.write_index - SEND_LIMIT + tx_rbuf.buf_len) % tx_rbuf.buf_len;
for(int i = 0; i < SEND_LIMIT; i++) {
fwrite(pkt_frame_full, 14, 1, f);
fwrite(((uint8_t*) tx_rbuf.buffer) + wi * tx_rbuf.len, tx_rbuf.len, 1, f);
wi = (wi + 1) % tx_rbuf.buf_len;
}
fclose(f);
memset(n, '\0', 256);
sprintf(n, "%s/rx.trace", s->log_directory);
f = fopen(n, "wb+");
wi = (rx_rbuf.write_index - SEND_LIMIT + rx_rbuf.buf_len) % rx_rbuf.buf_len;
for(int i = 0; i < SEND_LIMIT; i++) {
fwrite(((uint8_t*) rx_rbuf.buffer) + wi * rx_rbuf.len, rx_rbuf.len, 1, f);
wi = (wi + 1) % rx_rbuf.buf_len;
}
fclose(f);
memset(n, '\0', 256);
sprintf(n, "%s/trxw.trace", s->log_directory);
f = fopen(n, "wb+");
fwrite(trxw_trace_buffer.buffer, trxw_trace_buffer.counter, 1, f);
wi = (trxw_rbuf[0].write_index - SEND_LIMIT + trxw_rbuf[0].buf_len) % trxw_rbuf[0].buf_len;
for(int i = 0; i < SEND_LIMIT; i++) {
for(int j = 0; j < TX_N_CHANNEL; j++)
fwrite((uint8_t *) (((Complex *) trxw_rbuf[j].buffer) + wi * trxw_rbuf[0].len), trxw_rbuf[0].len * sizeof(Complex), 1, f);
wi = (wi + 1) % trxw_rbuf[0].buf_len;
}
fclose(f);
memset(n, '\0', 256);
sprintf(n, "%s/trxr.trace", s->log_directory);
f = fopen(n, "wb+");
wi = (trxr_rbuf[0].write_index - SEND_LIMIT + trxr_rbuf[0].buf_len) % trxr_rbuf[0].buf_len;
for(int i = 0; i < SEND_LIMIT; i++) {
for(int j = 0; j < RX_N_CHANNEL; j++)
fwrite((uint8_t *) (((Complex *) trxr_rbuf[j].buffer) + wi * trxr_rbuf[0].len), trxr_rbuf[0].len * sizeof(Complex), 1, f);
wi = (wi + 1) % trxr_rbuf[0].buf_len;
}
fclose(f);
#endif
exit(EXIT_SUCCESS);
log_exit("", "Send limit reached");
}
}
#endif
// Receives as fast as possible
// Signal to decode thread when packets receiving
static void *recv_thread(void *p) {
cpu_set_t mask;
......@@ -403,50 +505,61 @@ static void *recv_thread(void *p) {
if (sched_setaffinity(0, sizeof(mask), &mask))
error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU %d\n", s->recv_affinity);
#define RTE_MBUF_SIZE 20000
#define MIN_RX 10000
for(;;) {
struct rte_mbuf * pkt[1024];
struct rte_mbuf * pkt[RTE_MBUF_SIZE];
uint8_t * buf, * rtebuf;
int port = 0;
int nb_rx;
while(1) {
int n = rbuf_write_amount(&rx_rbuf);
int drop_packet = 0;
usleep(1000);
nb_rx = rte_eth_rx_burst(port, 0, pkt, 1024);
drop_packet = nb_rx > n;
if(drop_packet) {
for(int i = 0; i < nb_rx; i++)
rte_pktmbuf_free(pkt[i]);
if(nb_rx)
update_counter(&rx_drop_counter, nb_rx);
}
else {
for(int i = 0; i < nb_rx; i++) {
buf = ((uint8_t *) rx_rbuf.buffer) + (rx_rbuf.write_index * rx_rbuf.len);
rtebuf = (uint8_t *) (pkt[i])->buf_addr + (pkt[i])->data_off;
if((pkt[i])->pkt_len > RX_MAX_PACKET_SIZE)
log_exit("RECV_THREAD", "Received packet of length %u, but RX_MAX_PACKET_SIZE = %u", (pkt[i])->pkt_len, RX_MAX_PACKET_SIZE);
memcpy(buf, rtebuf, (pkt[i])->pkt_len);
#ifdef TRACE
write_buffer(&rx_trace_buffer, 0, (uint8_t*) rtebuf, RX_MAX_PACKET_SIZE);
#endif
rbuf_update_write_index(&rx_rbuf);
rte_pktmbuf_free(pkt[i]);
}
}
int nb_rx = 0;
int n;
int drop_packet = 0;
#ifdef DPDK
while(!nb_rx)
nb_rx = rte_eth_rx_burst(port, 0, pkt + nb_rx, 1024);
#else
for(int i = 0; i < 150000; i++)
asm("NOP");
nb_rx = 1024;
#endif
n = rbuf_write_amount(&rx_rbuf);
drop_packet = nb_rx > n;
if(drop_packet) {
for(int i = 0; i < nb_rx; i++)
rte_pktmbuf_free(pkt[i]);
if(nb_rx)
break;
update_counter(&rx_drop_counter, nb_rx);
}
else {
int nc; int nr;
nr = nb_rx;
while((nc = rbuf_contiguous_copy(NULL, &rx_rbuf, nr))) {
buf = ((uint8_t *) rx_rbuf.buffer) + (rx_rbuf.write_index * rx_rbuf.len);
for(int i = 0; i < nc; i++) {
#ifdef DPDK
rtebuf = (uint8_t *) (pkt[i])->buf_addr + (pkt[i])->data_off;
memcpy(buf + i * rx_rbuf.len, rtebuf + 22, rx_rbuf.len);
#else
memcpy(buf + i * rx_rbuf.len, pkt_frame_full, rx_rbuf.len);
#endif
#ifdef TRACE
write_buffer(&rx_trace_buffer, 0, rtebuf, 262);
#endif
}
rx_rbuf.write_index = (rx_rbuf.write_index + nc) % rx_rbuf.buf_len;
for(int i = 0; i < nc; i++)
rte_pktmbuf_free(pkt[i]);
nr -= nc;
}
}
update_counter(&recv_counter, nb_rx);
if((recv_counter.counter - rx_cond_counter) >= 1000) {
pthread_mutex_lock(&rx_mutex); pthread_cond_signal(&rx_cond); pthread_mutex_unlock(&rx_mutex);
rx_cond_counter = recv_counter.counter;
}
}
pthread_exit(EXIT_SUCCESS);
}
......@@ -474,21 +587,25 @@ static void *send_thread(void *p) {
#endif
int64_t n = rbuf_read_amount(&tx_rbuf);
pthread_mutex_lock(&encode_mutex);
if(n >= BURST_SIZE) {
pthread_mutex_unlock(&encode_mutex);
for(int k = 0; k < BURST_SIZE; k++) {
memcpy(tx_data[k], RBUF_READ0(tx_rbuf, uint8_t), tx_rbuf.len);
rbuf_update_read_index(&tx_rbuf);
int nb_burst = n / BURST_SIZE;
for(int j = 0; j < nb_burst; j++) {
for(int k = 0; k < BURST_SIZE; k++) {
memcpy(tx_data[k], RBUF_READ0(tx_rbuf, uint8_t), tx_rbuf.len);
rbuf_update_read_index(&tx_rbuf);
}
#ifdef DPDK
send_packets(0);
#else
for(int i = 0; i < 3000; i++)
asm("NOP");
#endif
}
send_packets(0);
update_counter(&sent_counter, BURST_SIZE);
pthread_mutex_lock(&tx_mutex); pthread_cond_signal(&tx_cond); pthread_mutex_unlock(&tx_mutex);
}
else {
pthread_cond_wait(&encode_cond, &encode_mutex);
pthread_mutex_unlock(&encode_mutex);
update_counter(&sent_counter, nb_burst * BURST_SIZE);
}
}
pthread_exit(EXIT_SUCCESS);
}
......@@ -509,17 +626,15 @@ static void *encode_thread(void *p) {
struct timespec next;
int64_t target_counter = 0;
int reset_encode_counter = 1;
int first_ts = 1;
log_info("PREPARE_THREAD", "Thread init");
// Set thread CPU affinity
CPU_ZERO(&mask);
CPU_SET(s->encode_affinity, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask))
error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU %d\n", s->encode_affinity);
#if 1
for(int64_t i = 0;; i++) {
int16_t samples_int[TX_N_SAMPLES];
int n;
if(sync_complete && reset_encode_counter) {
......@@ -529,16 +644,11 @@ static void *encode_thread(void *p) {
}
// If we have frames to encode (is there space in TX buffer)
pthread_mutex_lock(&tx_mutex);
n = rbuf_write_amount(&tx_rbuf);
if(n) {
pthread_mutex_unlock(&tx_mutex);
// If there are frames from trx_write callback to encode
pthread_mutex_lock(&trxw_mutex);
if(rbuf_read_amount(&trxw_rbuf) && rbuf_read_amount(&trxw_group_rbuf)) {
int64_t ts, frames_until_ts; sample_group_t * g; int nb_frames;
pthread_mutex_unlock(&trxw_mutex);
if(rbuf_read_amount(&trxw_rbuf[0]) && rbuf_read_amount(&trxw_group_rbuf)) {
sample_group_t * g; int nb_frames;
trx_started = 1;
g = RBUF_READ0(trxw_group_rbuf, sample_group_t);
......@@ -554,144 +664,126 @@ static void *encode_thread(void *p) {
memset(RBUF_WRITE0(tx_rbuf, uint8_t) + 8, 0x00, 240);
*((uint16_t *) (RBUF_WRITE0(tx_rbuf, uint8_t) + 6)) = htons(seq_id++);
#ifdef TRACE
write_buffer(&tx_trace_buffer, 14, (uint8_t*) RBUF_WRITE0(tx_rbuf, uint8_t), 248);
//write_buffer(&tx_trace_buffer, 14, (uint8_t*) RBUF_WRITE0(tx_rbuf, uint8_t), 248);
write_buffer(&tx_trace_buffer, 14, (uint8_t*) pkt_frame_full, 248);
#endif
rbuf_update_write_index(&tx_rbuf);
}
trxw_rbuf[0].read_index = (trxw_rbuf[0].read_index + nb_frames) % trxw_rbuf[0].buf_len;
} else {
for(int j = 0; j < nb_frames; j++) {
float * const trx_samples = RBUF_READ0(trxw_rbuf, float);
uint8_t * const tx_frame = RBUF_WRITE0(tx_rbuf, uint8_t);
memset(samples_int, 0, 512);
float_to_int16(samples_int, trx_samples, TX_N_SAMPLES, 32767);
encode_bf1(tx_frame + 8 , samples_int);
encode_bf1(tx_frame + 8 + 60 , samples_int + 64);
encode_bf1(tx_frame + 8 + 120, samples_int + 128);
encode_bf1(tx_frame + 8 + 180, samples_int + 192);
*((uint16_t *)(tx_frame + 6)) = htons(seq_id++);
int nc;
int nf = nb_frames;
while((nc = rbuf_contiguous_copy(&trxw_rbuf[0], &tx_rbuf, nf))) {
Complex * iq_samples[4];
uint8_t * buf = RBUF_WRITE0(tx_rbuf, uint8_t) + 8;
for(int j = 0; j < TX_N_CHANNEL; j++)
iq_samples[j] = ((Complex *) trxw_rbuf[j].buffer) + (trxw_rbuf[0].read_index * trxw_rbuf[0].len);
for(int i = 0; i < nc; i++) {
for(int i = 0; i < TX_N_CHANNEL ; i++)
encode_s64_b60_2(buf + i * 60, (float *) iq_samples[i]);
*((uint16_t *)(buf + 6)) = htons(seq_id++);
for(int j = 0; j < TX_N_CHANNEL; j++)
iq_samples[j] += trxw_rbuf[0].len;
#ifdef TRACE
write_buffer(&tx_trace_buffer, 14, (uint8_t*) RBUF_WRITE0(tx_rbuf, uint8_t), 248);
//write_buffer(&tx_trace_buffer, 14, buf - 8, 248);
write_buffer(&tx_trace_buffer, 14, (uint8_t*) pkt_frame_empty, 248);
#endif
rbuf_update_write_index(&tx_rbuf); // TODO update multiple indexes at once
rbuf_update_read_index(&trxw_rbuf);
buf += tx_rbuf.len;
}
tx_rbuf.write_index = (tx_rbuf.write_index + nc) % tx_rbuf.buf_len;
trxw_rbuf[0].read_index = (trxw_rbuf[0].read_index + nc) % trxw_rbuf[0].buf_len;
nf -= nc;
}
if(nf)
exit(EXIT_FAILURE);
}
update_counter(&encode_counter, nb_frames);
if(!g->count) {
rbuf_update_read_index(&trxw_group_rbuf);
}
pthread_mutex_lock(&encode_mutex); pthread_cond_signal(&encode_cond); pthread_mutex_unlock(&encode_mutex);
}
else {
// Send empty frames until we receive something
#if 1
if(!trx_started && !sync_complete) {
pthread_mutex_unlock(&trxw_mutex);
if(i == 0)
clock_gettime(CLOCK_TAI, &next);
// Limit packets sent
if(encode_counter.counter > target_counter) {
add_ns(&next, 10000000); // 10ms to send 38400 packets
int k = (encode_counter.counter - target_counter + EFREQ - 1) / EFREQ;
add_ns(&next, k * 1000 * 1000 * 10); // 10ms to send 38400 packets
clock_nanosleep(CLOCK_TAI, TIMER_ABSTIME, &next, NULL);
target_counter += 38400;
target_counter += k * EFREQ;
}
n = (n > TX_SYNC_BURST_SIZE) ? n : TX_SYNC_BURST_SIZE;
n = (n < EFREQ) ? n : EFREQ;
for(int j = 0; j < n; j++) {
*((uint16_t *) (RBUF_WRITE0(tx_rbuf, uint8_t) + 6)) = htons(seq_id++);
rbuf_update_write_index(&tx_rbuf);
}
update_counter(&encode_counter, n);
pthread_mutex_lock(&encode_mutex); pthread_cond_signal(&encode_cond); pthread_mutex_unlock(&encode_mutex);
}
// Wait for TRX TODO: maybe poll here ?
else {
pthread_cond_wait(&trxw_cond, &trxw_mutex);
pthread_mutex_unlock(&trxw_mutex);
}
#endif
}
}
else {
pthread_cond_wait(&tx_cond, &tx_mutex);
pthread_mutex_unlock(&tx_mutex);
}
}
pthread_exit(EXIT_SUCCESS);
#endif
}
static void *decode_thread(void *p) {
struct timespec a,b;
cpu_set_t mask;
TRXEcpriState * s = (TRXEcpriState *) p;
const float mult = 1. / 32767.;
FILE * trace_file_desc;
log_info("DECOMPRESS_THREAD", "Thread init");
if(s->trace_period) {
uint8_t trace_file_name[256];
memset(trace_file_name, '\0', 256);
sprintf(trace_file_name, "%s/partial-rx.trace", s->log_directory);
trace_file_desc = fopen(trace_file_name, "w+");
}
// Set thread CPU affinity
CPU_ZERO(&mask);
CPU_SET(s->decode_affinity, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask))
error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU %d\n", s->decode_affinity);
for(int64_t k = 0;;) {
pthread_mutex_lock(&rx_mutex);
int n = rbuf_read_amount(&rx_rbuf);
if(!n)
pthread_cond_wait(&rx_cond, &rx_mutex);
pthread_mutex_unlock(&rx_mutex);
int n_rbuf = rbuf_write_amount(&trxr_rbuf);
while(n_rbuf < n) {
usleep(100);
n_rbuf = rbuf_write_amount(&trxr_rbuf);
}
for(;;) {
int n;
while(!(n = rbuf_read_amount(&rx_rbuf)));
while(rbuf_write_amount(&trxr_rbuf[0]) < n);
for(int j = 0; j < n; j++) {
int16_t samples_int[RX_N_SAMPLES];
//const uint8_t * dst_mac = RBUF_READ0(rx_rbuf, uint8_t);
//const uint8_t * src_mac = RBUF_READ0(rx_rbuf, uint8_t) + 6;
//const uint16_t ether_type = htons(*((uint16_t*) (RBUF_READ0(rx_rbuf, uint8_t) + 12)));
//const uint8_t ecpri_protocol_rev = *(RBUF_READ0(rx_rbuf, uint8_t) + 14);
//const uint8_t ecpri_message_type = *(RBUF_READ0(rx_rbuf, uint8_t) + 15);
//const uint16_t ecpri_payload_size = htons(*((uint16_t*) (RBUF_READ0(rx_rbuf, uint8_t) + 16)));
//const uint16_t pc_id = htons(*((uint16_t*) (RBUF_READ0(rx_rbuf, uint8_t) + 18)));
//const uint16_t seq_id = htons(*((uint16_t*) (RBUF_READ0(rx_rbuf, uint8_t) + 20)));
const uint8_t * rx_samples = RBUF_READ0(rx_rbuf, uint8_t) + 22;
int nc;
while((nc = rbuf_contiguous_copy(&rx_rbuf, &trxr_rbuf[0], n))) {
k++;
rbuf_update_read_index(&rx_rbuf);
uint8_t * buf = ((uint8_t *) rx_rbuf.buffer) + (rx_rbuf.read_index * rx_rbuf.len) + 22;
memset((uint8_t * ) samples_int, 0, sizeof(int16_t) * RX_N_SAMPLES);
decode_bf1(samples_int, rx_samples, 16);
int16_to_float(RBUF_WRITE0(trxr_rbuf, float), samples_int, RX_N_SAMPLES, mult);
rbuf_update_write_index(&trxr_rbuf);
update_counter(&decode_counter, 1);
}
if((decode_counter.counter - decode_cond_counter) >= 9216) {
pthread_mutex_lock(&decode_mutex); pthread_cond_signal(&decode_cond); pthread_mutex_unlock(&decode_mutex);
decode_cond_counter = decode_counter.counter;
Complex * iq_samples[4];
for(int i = 0; i < RX_N_CHANNEL; i++)
iq_samples[i] = (((Complex *) trxr_rbuf[i].buffer) + (trxr_rbuf[0].write_index * trxr_rbuf[0].len));
for(int i = 0; i < nc; i++) {
for(int j = 0; j < RX_N_CHANNEL ; j++) {
decode_s64_b60_2((float *) (iq_samples[j] + i * 32), buf + j * 60 + i * rx_rbuf.len);
}
}
trxr_rbuf[0].write_index = (trxr_rbuf[0].write_index + nc) % trxr_rbuf[0].buf_len;
rx_rbuf.read_index = (rx_rbuf.read_index + nc) % rx_rbuf.buf_len;
n -= nc;
update_counter(&decode_counter, nc);
}
}
pthread_exit(EXIT_SUCCESS);
}
static void *statistic_thread(void *p) {
struct timespec next, initial;
cpu_set_t mask;
int64_t duration_ns;
TRXEcpriState * s = (TRXEcpriState *) p;
FILE * stats_file_desc;
log_info("STATISTIC_THREAD", "Thread init");
uint8_t stats_file_name[256];
char stats_file_name[256];
memset(stats_file_name, '\0', 256);
sprintf(stats_file_name, "%s/ecpri.stats", s->log_directory);
stats_file_desc = fopen(stats_file_name, "w+");
......@@ -709,39 +801,20 @@ static void *statistic_thread(void *p) {
next = initial;
for(int64_t i = 0;; i++) {
add_ns(&next, STATISTIC_REFRESH_RATE);
if((i % 50) == 0)
fprintf(stats_file_desc,
"%13s %13s %13s %13s %13s %13s %13s %13s %13s %13s %13s %13s %13s %13s \n",
"rx dropped",
"tx dropped",
"received",
"decode",
"read",
"write",
"encode",
"sent",
"received pps",
"decode pps",
"read pps",
"write pps",
"encode pps",
"sent pps");
print_stats(stats_file_desc, (i % 50) == 0);
#ifdef DEBUG
fprintf(stats_file_desc,
"%13" PRIi64 " %13" PRIi64 " %13" PRIi64 " %13" PRIi64 " %13" PRIi64 " %13" PRIi64 " %13" PRIi64 " %13" PRIi64 " %13" PRIi64 "pps %13" PRIi64 "pps %13" PRIi64 "pps %13" PRIi64 "pps %13" PRIi64 "pps %13" PRIi64 "pps\n",
rx_drop_counter.counter,
tx_drop_counter.counter,
recv_counter.counter,
decode_counter.counter,
read_counter.counter,
write_counter.counter,
encode_counter.counter,
sent_counter.counter,
recv_counter.pps,
decode_counter.pps,
read_counter.pps,
write_counter.pps,
encode_counter.pps,
sent_counter.pps);
"%d %d %d %d %d %d %d %d\n",
rx_rbuf.write_index,
rx_rbuf.read_index,
trxr_rbuf[0].write_index,
trxr_rbuf[0].read_index,
trxw_rbuf[0].write_index,
trxw_rbuf[0].read_index,
tx_rbuf.write_index,
tx_rbuf.read_index);
fprintf(stats_file_desc, "TRXW RBUF: ri %d wi %d ra %d wa %d\n", trxw_rbuf[0].read_index, trxw_rbuf[0].write_index, rbuf_read_amount(&trxw_rbuf[0]), rbuf_write_amount(&trxw_rbuf[0]));
#endif
fflush(stats_file_desc);
clock_nanosleep(CLOCK_TAI, TIMER_ABSTIME, &next, NULL);
}
......@@ -831,17 +904,20 @@ static int start_threads(TRXEcpriState * s) {
if (pthread_attr_setinheritsched(&statistic_attr, PTHREAD_EXPLICIT_SCHED))
log_error("TRX_ECPRI", "pthread setinheritsched failed\n");
if (pthread_create(&statistic_pthread, NULL, statistic_thread, s))
error(EXIT_FAILURE, errno, "Couldn't create statistic thread");
usleep(1000 * 20);
if (pthread_create(&encode_pthread, NULL, encode_thread, s))
error(EXIT_FAILURE, errno, "Couldn't create encode thread");
usleep(1000 * 20);
if (pthread_create(&decode_pthread, NULL, decode_thread, s))
error(EXIT_FAILURE, errno, "Couldn't create decode thread");
usleep(1000 * 100);
usleep(1000 * 20);
if (pthread_create(&send_pthread, NULL, send_thread, s))
error(EXIT_FAILURE, errno, "Couldn't create send thread");
usleep(1000 * 500);
if (pthread_create(&recv_pthread, NULL, recv_thread, s))
error(EXIT_FAILURE, errno, "Couldn't create recv thread");
if (pthread_create(&statistic_pthread, NULL, statistic_thread, s))
error(EXIT_FAILURE, errno, "Couldn't create statistic thread");
return 0;
}
......@@ -852,12 +928,20 @@ int startdpdk(TRXEcpriState * s) {
int argc = 1;
int k = 1;
int prev_space = -1;
uint8_t ** argv;
char ** argv;
for(int i = 0; i < 1024; i++)
iq_frame_full[i] = 0xff;
for(int i = 0; i < 256; i++) {
iq_frame_full[i * 4 + 0] = 0x00;
iq_frame_full[i * 4 + 1] = 0x00;
iq_frame_full[i * 4 + 2] = 0x00;
iq_frame_full[i * 4 + 3] = 0x3f;
}
for(int i = 0; i < 1024; i++)
iq_frame_empty[i] = 0x00;
for(int i = 0; i < 262; i++)
pkt_frame_empty[i] = 0x00;
for(int i = 0; i < 262; i++)
pkt_frame_full[i] = 0xff;
for(int i = 0;; i++) {
......@@ -866,11 +950,11 @@ int startdpdk(TRXEcpriState * s) {
else if(s->dpdk_options[i] == '\0')
break;
}
argv = (uint8_t **) malloc(sizeof(uint8_t *) * argc);
argv = (char **) malloc(sizeof(char *) * argc);
for(int i = 0;; i++) {
if(s->dpdk_options[i] == ' ') {
argv[k] = (uint8_t *) malloc(i - prev_space);
argv[k] = (char *) malloc(i - prev_space);
strncpy(argv[k], s->dpdk_options + prev_space + 1, i - prev_space -1);
argv[k][i - prev_space-1] = '\0';
prev_space = i;
......@@ -882,10 +966,12 @@ int startdpdk(TRXEcpriState * s) {
}
argv[0] = "";
#ifdef DPDK
init_dpdk(argc, argv);
#endif
log_info("TRX_ECPRI", "Start");
set_latency_target();
//set_latency_target();
seq_id = 0;
init_counter(&rx_drop_counter);
......@@ -899,22 +985,22 @@ int startdpdk(TRXEcpriState * s) {
RBUF_INIT(rx_rbuf, "RX ring buffer", TXRX_BUF_MAX_SIZE, RX_MAX_PACKET_SIZE, uint8_t);
RBUF_INIT(tx_rbuf, "TX ring buffer", TXRX_BUF_MAX_SIZE, TX_ECPRI_PACKET_SIZE, uint8_t);
RBUF_INIT(trxr_rbuf, "TRXRead ring buffer", TRX_BUF_MAX_SIZE, RX_N_SAMPLES, float);
RBUF_INIT(trxw_rbuf, "TRXWrite ring buffer", TRX_BUF_MAX_SIZE, TX_N_SAMPLES, float);
for(int i = 0; i < TX_N_CHANNEL; i++) {
char s[256];
sprintf(s, "TRXWrite Ring Buffer %d", i);
RBUF_INIT(trxw_rbuf[i], s, TRX_BUF_MAX_SIZE, N_SAMPLES, Complex);
}
for(int i = 0; i < RX_N_CHANNEL; i++) {
char s[256];
sprintf(s, "TRXRead Ring Buffer %d", i);
RBUF_INIT(trxr_rbuf[i], s, TRX_BUF_MAX_SIZE, N_SAMPLES, Complex);
}
RBUF_INIT(trxw_group_rbuf, "TRXGroupWrite ring buffer", TRX_MAX_GROUP, 1, sample_group_t);
pthread_mutex_init(&tx_mutex, NULL);
pthread_mutex_init(&encode_mutex, NULL);
pthread_mutex_init(&rx_mutex, NULL);
pthread_mutex_init(&decode_mutex, NULL);
pthread_cond_init(&tx_cond, NULL);
pthread_cond_init(&encode_cond, NULL);
pthread_cond_init(&rx_cond, NULL);
pthread_cond_init(&decode_cond, NULL);
memset((uint8_t *) ecpri_message, 0, TX_ECPRI_PACKET_SIZE);
if(sscanf(s->re_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%*c",
#ifdef DPDK
if(sscanf((char *) s->re_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%*c",
&d_addr.addr_bytes[0],
&d_addr.addr_bytes[1],
&d_addr.addr_bytes[2],
......@@ -922,7 +1008,7 @@ int startdpdk(TRXEcpriState * s) {
&d_addr.addr_bytes[4],
&d_addr.addr_bytes[5]) != 6)
fprintf(stderr, "Invalid eRE MAC address\n");
if(sscanf(s->rec_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%*c",
if(sscanf((char *) s->rec_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%*c",
&s_addr.addr_bytes[0],
&s_addr.addr_bytes[1],
&s_addr.addr_bytes[2],
......@@ -930,6 +1016,7 @@ int startdpdk(TRXEcpriState * s) {
&s_addr.addr_bytes[4],
&s_addr.addr_bytes[5]) != 6)
fprintf(stderr, "Invalid eREC MAC address\n");
#endif
/* Standard Header */
ecpri_message[0] = 0x10; // Protocol data revision 0x1, C = 0
......@@ -960,22 +1047,24 @@ int64_t prev_count = 0;
static void trx_ecpri_write(TRXState *s1, trx_timestamp_t timestamp, const void **__samples, int count, int tx_port_index, TRXWriteMetadata *md)
{
(void) s1;
float ** _samples; int write_count; int64_t ts; sample_group_t * g;
_samples = (float **) __samples;
int write_count; int64_t ts; sample_group_t * g; int nc;
float ** _samples = (float **) __samples;
write_count = count / M;
ts = timestamp / M;
log_debug("TRX_ECPRI_WRITE", "trx_ecpri_write, count = %ld", count);
if(prev_count && (ts - prev_ts) != prev_count) {
log_exit("TRX_ECPRI_WRITE", "Gap between timestamps: prev_ts %li ts %li prev_count %li count %li diff_ts %li", prev_ts, ts, prev_count, count, (ts - prev_ts));
}
prev_ts = ts; prev_count = write_count;
int n_rbuf = rbuf_write_amount(&trxw_rbuf);
if(write_count > n_rbuf) {
if(write_count > rbuf_write_amount(&trxw_rbuf[0])) {
//log_exit("TRX_ECPRI_WRITE", "Not enough space to write in trxw_rbuf (write count = %d)", write_count);
update_counter(&tx_drop_counter, write_count);
return;
}
if(first_trx_write) {
sample_group_t * g2 = RBUF_WRITE0(trxw_group_rbuf, sample_group_t);
g2->count = ts;
......@@ -983,60 +1072,60 @@ static void trx_ecpri_write(TRXState *s1, trx_timestamp_t timestamp, const void
g2->zeroes = 1;
rbuf_update_write_index(&trxw_group_rbuf);
}
g = RBUF_WRITE0(trxw_group_rbuf, sample_group_t);
g->zeroes = __samples ? 0 : 1;
g->wait = 0;
g->count = write_count;
for(int k = 0; k < write_count; k++) {
if(__samples) {
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 64; j++) {
RBUF_WRITE(trxw_rbuf, k, float)[i * 64 + j] = _samples[i][j + (k * 64)];
}
}
}
while((nc = rbuf_contiguous_copy(NULL, &trxw_rbuf[0], write_count))) {
if(__samples)
for(int i = 0; i < TX_N_CHANNEL; i++)
memcpy((((float *) trxw_rbuf[i].buffer) + trxw_rbuf[0].write_index * trxw_rbuf[0].len * 2), _samples[i], nc * trxw_rbuf[0].len * 2);
trxw_rbuf[0].write_index = (trxw_rbuf[0].write_index + nc) % trxw_rbuf[0].buf_len;
write_count -= nc;
}
#ifdef TRACE
write_buffer(&trxw_trace_buffer, 0, __samples ? iq_frame_full : iq_frame_empty, 1024);
for(int i = 0; i * M < count; i++)
write_buffer(&trxw_trace_buffer, 0, __samples ? ((uint8_t*) iq_frame_full) : ((uint8_t *)iq_frame_empty), 1024);
#endif
}
if(__samples) {
// Update write index at the end so that everything stays consistent
for(int k = 0; k < write_count; k++)
rbuf_update_write_index(&trxw_rbuf);
}
rbuf_update_write_index(&trxw_group_rbuf);
update_counter(&write_counter, count / M);
pthread_mutex_lock(&trxw_mutex); pthread_cond_signal(&trxw_cond); pthread_mutex_unlock(&trxw_mutex);
}
static int trx_ecpri_read(TRXState *s1, trx_timestamp_t *ptimestamp, void **__samples, int count, int rx_port_index, TRXReadMetadata *md)
{
(void) s1;
int nc; int n;
float ** _samples = (float **) __samples;
int read_count = (count / M);
int offset = 0;
log_limit("TRX_ECPRI_READ", "count = %ld", count);
while(rbuf_read_amount(&trxr_rbuf[0]) < read_count);
int nr = rbuf_read_amount(&trxr_rbuf);
while(nr < read_count) {
usleep(50);
nr = rbuf_read_amount(&trxr_rbuf);
}
log_debug("TRX_ECPRI_READ", "count = %ld (%li)", read_count, read_counter.counter);
sync_complete = 1;
for(int k = 0; k < read_count; k++) {
float * trx_samples;
trx_samples = RBUF_READ0(trxr_rbuf, float);
for(int i = 0; i < 64; i++)
_samples[0][i] = trx_samples[i];
n = read_count;
while((nc = rbuf_contiguous_copy(&trxr_rbuf[0], NULL, n))) {
int len = nc * trxr_rbuf[0].len * 2;
for(int i = 0; i < RX_N_CHANNEL; i++ ) {
memcpy(_samples[i] + offset, (((float *) trxr_rbuf[i].buffer) + trxr_rbuf[0].read_index * trxr_rbuf[0].len * 2), len);
}
#ifdef TRACE
write_buffer(&trxr_trace_buffer, 0, (uint8_t *) trx_samples, 64 * sizeof(float));
for(int i = 0; i < nc; i++) {
int l = 64 * sizeof(float);
for(int j = 0; j < RX_N_CHANNEL; j++ ) {
write_buffer(&trxr_trace_buffer, 0, (uint8_t*) (_samples[j] + offset + i * 64), l);
}
}
#endif
rbuf_update_read_index(&trxr_rbuf);
trxr_rbuf[0].read_index = (trxr_rbuf[0].read_index + nc) % trxr_rbuf[0].buf_len;
n -= nc;
offset += len;
}
*ptimestamp = recv_counter.counter * M;
update_counter(&read_counter, read_count);
......@@ -1123,8 +1212,6 @@ int trx_driver_init(TRXState *s1)
s->statistic_affinity = (int) val;
trx_get_param_double(s1, &val, "flow_id");
s->flow_id = (int) val;
trx_get_param_double(s1, &val, "trace_period");
s->trace_period = (int) val;
trx_get_param_double(s1, &val, "ecpri_period");
if(((int) val) == 0) {
fprintf(stderr, "ecpri_period parameter can't be null\n");
......@@ -1132,11 +1219,11 @@ int trx_driver_init(TRXState *s1)
}
s->ecpri_period = (int) val;
s->re_mac = trx_get_param_string(s1, "re_mac");
s->rec_mac = trx_get_param_string(s1, "rec_mac");
s->rec_if = trx_get_param_string(s1, "rec_if");
s->re_mac = (uint8_t *) trx_get_param_string(s1, "re_mac");
s->rec_mac = (uint8_t *) trx_get_param_string(s1, "rec_mac");
s->rec_if = (uint8_t *) trx_get_param_string(s1, "rec_if");
s->dpdk_options = trx_get_param_string(s1, "dpdk_options");
s->log_directory = trx_get_param_string(s1, "log_directory");
s->log_directory = (uint8_t *) trx_get_param_string(s1, "log_directory");
s1->opaque = s;
s1->trx_end_func = trx_ecpri_end;
......
......@@ -36,6 +36,7 @@ static inline void log_limit(const char * section, const char * msg, ...) {
puts(line);
}
#if 0
static void log_info(const char * section, const char * msg, ...) {
time_t t;
struct tm ts;
......@@ -51,6 +52,11 @@ static void log_info(const char * section, const char * msg, ...) {
va_end(arglist);
puts(line);
}
#else
static void log_info(const char * section, const char * msg, ...) {
return;
}
#endif
#ifdef DEBUG
static void log_debug(const char * section, const char * msg, ...) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment