Revision bbeb2189
| b/host/usrp_e_utils/CMakeLists.txt | ||
|---|---|---|
| 27 | 27 |
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp/e100/include)
|
| 28 | 28 |
|
| 29 | 29 |
SET(usrp_e_utils_sources |
| 30 |
usrp-e-loopback.c |
|
| 30 |
usrp-e-loopback.cpp
|
|
| 31 | 31 |
usrp-e-wb-test.cpp |
| 32 | 32 |
usrp-e-debug-pins.c |
| 33 | 33 |
usrp-e-gpio.c |
| b/host/usrp_e_utils/common.hpp | ||
|---|---|---|
| 23 | 23 |
|
| 24 | 24 |
static int fp; |
| 25 | 25 |
|
| 26 |
static int peek16(int reg){
|
|
| 26 |
static inline int peek16(int reg){
|
|
| 27 | 27 |
int ret; |
| 28 | 28 |
struct usrp_e_ctl16 d; |
| 29 | 29 |
|
| ... | ... | |
| 33 | 33 |
return d.buf[0]; |
| 34 | 34 |
} |
| 35 | 35 |
|
| 36 |
static void poke16(int reg, int val){
|
|
| 36 |
static inline void poke16(int reg, int val){
|
|
| 37 | 37 |
int ret; |
| 38 | 38 |
struct usrp_e_ctl16 d; |
| 39 | 39 |
|
| ... | ... | |
| 43 | 43 |
ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); |
| 44 | 44 |
} |
| 45 | 45 |
|
| 46 |
static int peek32(int reg){
|
|
| 46 |
static inline int peek32(int reg){
|
|
| 47 | 47 |
int ret; |
| 48 | 48 |
struct usrp_e_ctl32 d; |
| 49 | 49 |
|
| ... | ... | |
| 53 | 53 |
return d.buf[0]; |
| 54 | 54 |
} |
| 55 | 55 |
|
| 56 |
static void poke32(int reg, int val){
|
|
| 56 |
static inline void poke32(int reg, int val){
|
|
| 57 | 57 |
int ret; |
| 58 | 58 |
struct usrp_e_ctl32 d; |
| 59 | 59 |
|
| /dev/null | ||
|---|---|---|
| 1 |
#include <stdio.h> |
|
| 2 |
#include <string.h> |
|
| 3 |
#include <sys/types.h> |
|
| 4 |
#include <sys/ioctl.h> |
|
| 5 |
#include <fcntl.h> |
|
| 6 |
#include <pthread.h> |
|
| 7 |
#include <stdlib.h> |
|
| 8 |
#include <unistd.h> |
|
| 9 |
#include <stddef.h> |
|
| 10 |
#include <stdint.h> |
|
| 11 |
#include <sys/mman.h> |
|
| 12 |
#include <sys/time.h> |
|
| 13 |
#include <poll.h> |
|
| 14 |
#include "linux/usrp_e.h" |
|
| 15 |
|
|
| 16 |
// max length #define PKT_DATA_LENGTH 1016 |
|
| 17 |
static int packet_data_length; |
|
| 18 |
static int error; |
|
| 19 |
|
|
| 20 |
struct pkt {
|
|
| 21 |
uint32_t words32; |
|
| 22 |
uint32_t len; |
|
| 23 |
uint32_t checksum; |
|
| 24 |
uint32_t seq_num; |
|
| 25 |
uint16_t data[1024-8]; |
|
| 26 |
}; |
|
| 27 |
|
|
| 28 |
void print_pkt(const struct pkt *p){
|
|
| 29 |
printf("p->words32 %d\n", p->words32);
|
|
| 30 |
printf("p->len %d\n", p->len);
|
|
| 31 |
printf("p->checksum %d\n", p->checksum);
|
|
| 32 |
printf("p->seq_num %d\n", p->seq_num);
|
|
| 33 |
size_t i; |
|
| 34 |
for (i = 0; i < 5; i++){
|
|
| 35 |
printf(" buff[%u] = 0x%.4x\n", i, p->data[i]);
|
|
| 36 |
} |
|
| 37 |
} |
|
| 38 |
|
|
| 39 |
struct ring_buffer_info (*rxi)[]; |
|
| 40 |
struct ring_buffer_info (*txi)[]; |
|
| 41 |
struct pkt (*rx_buf)[200]; |
|
| 42 |
struct pkt (*tx_buf)[200]; |
|
| 43 |
|
|
| 44 |
static int fp; |
|
| 45 |
static struct usrp_e_ring_buffer_size_t rb_size; |
|
| 46 |
|
|
| 47 |
static int calc_checksum(struct pkt *p) |
|
| 48 |
{
|
|
| 49 |
int i, sum; |
|
| 50 |
|
|
| 51 |
i = 0; |
|
| 52 |
sum = 0; |
|
| 53 |
|
|
| 54 |
if (p->len < 1016) {
|
|
| 55 |
for (i=0; i < p->len; i++) |
|
| 56 |
sum += p->data[i]; |
|
| 57 |
|
|
| 58 |
sum += p->seq_num; |
|
| 59 |
sum += p->len; |
|
| 60 |
} else {
|
|
| 61 |
printf("Bad packet length = %d received.\n", p->len);
|
|
| 62 |
} |
|
| 63 |
|
|
| 64 |
return sum; |
|
| 65 |
} |
|
| 66 |
|
|
| 67 |
static struct timeval delta_time(struct timeval f, struct timeval s) |
|
| 68 |
{
|
|
| 69 |
struct timeval d; |
|
| 70 |
|
|
| 71 |
if (f.tv_usec > s.tv_usec) {
|
|
| 72 |
d.tv_usec = f.tv_usec - s.tv_usec; |
|
| 73 |
d.tv_sec = f.tv_sec - s.tv_sec; |
|
| 74 |
} else {
|
|
| 75 |
d.tv_usec = f.tv_usec - s.tv_usec + 1e6; |
|
| 76 |
d.tv_sec = f.tv_sec - s.tv_sec - 1; |
|
| 77 |
} |
|
| 78 |
|
|
| 79 |
return d; |
|
| 80 |
} |
|
| 81 |
|
|
| 82 |
static void *read_thread(void *threadid) |
|
| 83 |
{
|
|
| 84 |
int cnt, prev_seq_num, pkt_count, seq_num_failure; |
|
| 85 |
struct pkt *p; |
|
| 86 |
unsigned long bytes_transfered; |
|
| 87 |
struct timeval start_time; |
|
| 88 |
int rb_read; |
|
| 89 |
|
|
| 90 |
printf("Greetings from the reading thread!\n");
|
|
| 91 |
printf("sizeof pkt = %d\n", sizeof(struct pkt));
|
|
| 92 |
|
|
| 93 |
rb_read = 0; |
|
| 94 |
|
|
| 95 |
bytes_transfered = 0; |
|
| 96 |
gettimeofday(&start_time, NULL); |
|
| 97 |
|
|
| 98 |
prev_seq_num = 0; |
|
| 99 |
pkt_count = 0; |
|
| 100 |
seq_num_failure = 0; |
|
| 101 |
|
|
| 102 |
while (1) {
|
|
| 103 |
|
|
| 104 |
if (!((*rxi)[rb_read].flags & RB_USER)) {
|
|
| 105 |
// printf("Waiting for data\n");
|
|
| 106 |
struct pollfd pfd; |
|
| 107 |
pfd.fd = fp; |
|
| 108 |
pfd.events = POLLIN; |
|
| 109 |
poll(&pfd, 1, -1); |
|
| 110 |
} |
|
| 111 |
|
|
| 112 |
(*rxi)[rb_read].flags = RB_USER_PROCESS; |
|
| 113 |
|
|
| 114 |
// printf("pkt received, rb_read = %d\n", rb_read);
|
|
| 115 |
|
|
| 116 |
cnt = (*rxi)[rb_read].len; |
|
| 117 |
p = &(*rx_buf)[rb_read]; |
|
| 118 |
|
|
| 119 |
// cnt = read(fp, rx_data, 2048); |
|
| 120 |
// if (cnt < 0) |
|
| 121 |
// printf("Error returned from read: %d, sequence number = %d\n", cnt, p->seq_num);
|
|
| 122 |
|
|
| 123 |
// printf("p = %X, p->seq_num = %d p->len = %d\n", p, p->seq_num, p->len);
|
|
| 124 |
|
|
| 125 |
|
|
| 126 |
pkt_count++; |
|
| 127 |
|
|
| 128 |
if (p->seq_num != prev_seq_num + 1) {
|
|
| 129 |
printf("Sequence number fail, current = %d, previous = %d, pkt_count = %d\n",
|
|
| 130 |
p->seq_num, prev_seq_num, pkt_count); |
|
| 131 |
printf("pkt received, rb_read = %d\n", rb_read);
|
|
| 132 |
printf("p = %p, p->seq_num = %d p->len = %d\n", p, p->seq_num, p->len);
|
|
| 133 |
|
|
| 134 |
seq_num_failure ++; |
|
| 135 |
if (seq_num_failure > 2) |
|
| 136 |
error = 1; |
|
| 137 |
} |
|
| 138 |
|
|
| 139 |
prev_seq_num = p->seq_num; |
|
| 140 |
|
|
| 141 |
if (calc_checksum(p) != p->checksum) {
|
|
| 142 |
printf("Checksum fail packet = %X, expected = %X, pkt_count = %d\n",
|
|
| 143 |
calc_checksum(p), p->checksum, pkt_count); |
|
| 144 |
error = 1; |
|
| 145 |
} |
|
| 146 |
|
|
| 147 |
(*rxi)[rb_read].flags = RB_KERNEL; |
|
| 148 |
|
|
| 149 |
rb_read++; |
|
| 150 |
if (rb_read == rb_size.num_rx_frames) |
|
| 151 |
rb_read = 0; |
|
| 152 |
|
|
| 153 |
bytes_transfered += p->len*2;//cnt; |
|
| 154 |
|
|
| 155 |
if (bytes_transfered > (100 * 1000000)) {
|
|
| 156 |
struct timeval finish_time, d_time; |
|
| 157 |
float elapsed_seconds; |
|
| 158 |
|
|
| 159 |
gettimeofday(&finish_time, NULL); |
|
| 160 |
d_time = delta_time(finish_time, start_time); |
|
| 161 |
elapsed_seconds = (float)d_time.tv_sec + ((float)d_time.tv_usec * 1e-6f); |
|
| 162 |
|
|
| 163 |
printf("RX data transfer rate = %f K Samples/second\n",
|
|
| 164 |
(float) bytes_transfered / elapsed_seconds / 4000.0f); |
|
| 165 |
|
|
| 166 |
|
|
| 167 |
start_time = finish_time; |
|
| 168 |
bytes_transfered = 0; |
|
| 169 |
} |
|
| 170 |
|
|
| 171 |
|
|
| 172 |
// printf(".");
|
|
| 173 |
// fflush(stdout); |
|
| 174 |
// printf("\n");
|
|
| 175 |
} |
|
| 176 |
return NULL; |
|
| 177 |
} |
|
| 178 |
|
|
| 179 |
static void *write_thread(void *threadid) |
|
| 180 |
{
|
|
| 181 |
int seq_number, i, cnt, rb_write; |
|
| 182 |
void *tx_data; |
|
| 183 |
struct pkt *p; |
|
| 184 |
|
|
| 185 |
printf("Greetings from the write thread!\n");
|
|
| 186 |
|
|
| 187 |
tx_data = malloc(2048); |
|
| 188 |
p = (struct pkt *) ((void *)tx_data); |
|
| 189 |
|
|
| 190 |
for (i=0; i < packet_data_length; i++) |
|
| 191 |
// p->data[i] = random() >> 16; |
|
| 192 |
p->data[i] = i; |
|
| 193 |
|
|
| 194 |
seq_number = 1; |
|
| 195 |
rb_write = 0; |
|
| 196 |
|
|
| 197 |
while (1) {
|
|
| 198 |
p->seq_num = seq_number++; |
|
| 199 |
|
|
| 200 |
if (packet_data_length > 0) |
|
| 201 |
p->len = packet_data_length; |
|
| 202 |
else |
|
| 203 |
p->len = (random() & 0x1fe) + (1000 - 512); |
|
| 204 |
|
|
| 205 |
p->words32 = 4 /*hdr*/ + p->len/2; |
|
| 206 |
|
|
| 207 |
p->checksum = calc_checksum(p); |
|
| 208 |
|
|
| 209 |
if (!((*txi)[rb_write].flags & RB_KERNEL)) {
|
|
| 210 |
// printf("Waiting for space\n");
|
|
| 211 |
struct pollfd pfd; |
|
| 212 |
pfd.fd = fp; |
|
| 213 |
pfd.events = POLLOUT; |
|
| 214 |
poll(&pfd, 1, -1); |
|
| 215 |
} |
|
| 216 |
|
|
| 217 |
memcpy(&(*tx_buf)[rb_write], tx_data, p->words32*sizeof(uint32_t)); |
|
| 218 |
|
|
| 219 |
(*txi)[rb_write].len = p->words32*sizeof(uint32_t); |
|
| 220 |
(*txi)[rb_write].flags = RB_USER; |
|
| 221 |
|
|
| 222 |
rb_write++; |
|
| 223 |
if (rb_write == rb_size.num_tx_frames) |
|
| 224 |
rb_write = 0; |
|
| 225 |
|
|
| 226 |
cnt = write(fp, NULL, 0); |
|
| 227 |
// if (cnt < 0) |
|
| 228 |
// printf("Error returned from write: %d\n", cnt);
|
|
| 229 |
// sleep(1); |
|
| 230 |
|
|
| 231 |
} |
|
| 232 |
return NULL; |
|
| 233 |
} |
|
| 234 |
|
|
| 235 |
|
|
| 236 |
int main(int argc, char *argv[]) |
|
| 237 |
{
|
|
| 238 |
pthread_t tx, rx; |
|
| 239 |
long int t = 0; |
|
| 240 |
struct sched_param s = {
|
|
| 241 |
.sched_priority = 1 |
|
| 242 |
}; |
|
| 243 |
int ret, map_size, page_size; |
|
| 244 |
void *rb; |
|
| 245 |
struct usrp_e_ctl16 d; |
|
| 246 |
|
|
| 247 |
if (argc < 2) {
|
|
| 248 |
printf("%s data_size\n", argv[0]);
|
|
| 249 |
return -1; |
|
| 250 |
} |
|
| 251 |
|
|
| 252 |
packet_data_length = atoi(argv[1]); |
|
| 253 |
|
|
| 254 |
if (packet_data_length > 1016) {
|
|
| 255 |
packet_data_length = 1016; |
|
| 256 |
printf("Max data length = 1016, clamping.\n");
|
|
| 257 |
} |
|
| 258 |
|
|
| 259 |
fp = open("/dev/usrp_e0", O_RDWR);
|
|
| 260 |
printf("fp = %d\n", fp);
|
|
| 261 |
|
|
| 262 |
d.offset = 14; |
|
| 263 |
d.count = 1; |
|
| 264 |
d.buf[0] = (1<<8) | (1<<9); |
|
| 265 |
ioctl(fp, USRP_E_WRITE_CTL16, &d); |
|
| 266 |
|
|
| 267 |
page_size = getpagesize(); |
|
| 268 |
|
|
| 269 |
ret = ioctl(fp, USRP_E_GET_RB_INFO, &rb_size); |
|
| 270 |
|
|
| 271 |
map_size = (rb_size.num_pages_rx_flags + rb_size.num_pages_tx_flags) * page_size + |
|
| 272 |
(rb_size.num_rx_frames + rb_size.num_tx_frames) * (page_size >> 1); |
|
| 273 |
|
|
| 274 |
rb = mmap(0, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fp, 0); |
|
| 275 |
if (rb == MAP_FAILED) {
|
|
| 276 |
perror("mmap failed");
|
|
| 277 |
return -1; |
|
| 278 |
} |
|
| 279 |
|
|
| 280 |
printf("rb = %p\n", rb);
|
|
| 281 |
|
|
| 282 |
rxi = rb; |
|
| 283 |
rx_buf = rb + (rb_size.num_pages_rx_flags * page_size); |
|
| 284 |
txi = rb + (rb_size.num_pages_rx_flags * page_size) + |
|
| 285 |
(rb_size.num_rx_frames * page_size >> 1); |
|
| 286 |
tx_buf = rb + (rb_size.num_pages_rx_flags * page_size) + |
|
| 287 |
(rb_size.num_rx_frames * page_size >> 1) + |
|
| 288 |
(rb_size.num_pages_tx_flags * page_size); |
|
| 289 |
|
|
| 290 |
printf("rxi = %p, rx_buf = %p, txi = %p, tx_buf = %p\n", rxi, rx_buf, txi, tx_buf);
|
|
| 291 |
|
|
| 292 |
if ((ret = sched_setscheduler(0, SCHED_RR, &s))) |
|
| 293 |
perror("sched_setscheduler");
|
|
| 294 |
|
|
| 295 |
error = 0; |
|
| 296 |
|
|
| 297 |
#if 1 |
|
| 298 |
if (pthread_create(&rx, NULL, read_thread, (void *) t)) {
|
|
| 299 |
printf("Failed to create rx thread\n");
|
|
| 300 |
exit(-1); |
|
| 301 |
} |
|
| 302 |
|
|
| 303 |
sleep(1); |
|
| 304 |
#endif |
|
| 305 |
|
|
| 306 |
#if 1 |
|
| 307 |
if (pthread_create(&tx, NULL, write_thread, (void *) t)) {
|
|
| 308 |
printf("Failed to create tx thread\n");
|
|
| 309 |
exit(-1); |
|
| 310 |
} |
|
| 311 |
|
|
| 312 |
sleep(1); |
|
| 313 |
#endif |
|
| 314 |
|
|
| 315 |
// while (!error) |
|
| 316 |
sleep(1000000000); |
|
| 317 |
|
|
| 318 |
printf("Done sleeping\n");
|
|
| 319 |
|
|
| 320 |
return 0; |
|
| 321 |
} |
|
| b/host/usrp_e_utils/usrp-e-loopback.cpp | ||
|---|---|---|
| 1 |
// |
|
| 2 |
// Copyright 2011 Ettus Research LLC |
|
| 3 |
// |
|
| 4 |
// This program is free software: you can redistribute it and/or modify |
|
| 5 |
// it under the terms of the GNU General Public License as published by |
|
| 6 |
// the Free Software Foundation, either version 3 of the License, or |
|
| 7 |
// (at your option) any later version. |
|
| 8 |
// |
|
| 9 |
// This program is distributed in the hope that it will be useful, |
|
| 10 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 11 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 12 |
// GNU General Public License for more details. |
|
| 13 |
// |
|
| 14 |
// You should have received a copy of the GNU General Public License |
|
| 15 |
// along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
| 16 |
// |
|
| 17 |
|
|
| 18 |
#include "common.hpp" |
|
| 19 |
#include <cstdlib> |
|
| 20 |
#include <cstdio> |
|
| 21 |
#include <ctime> |
|
| 22 |
#include <iostream> |
|
| 23 |
#include <boost/thread/thread.hpp> |
|
| 24 |
#include <boost/format.hpp> |
|
| 25 |
#include <boost/cstdint.hpp> |
|
| 26 |
#include <sys/mman.h> //mmap |
|
| 27 |
#include <unistd.h> //getpagesize |
|
| 28 |
#include <poll.h> //poll |
|
| 29 |
|
|
| 30 |
static const size_t bytes_per_frame = 2048; |
|
| 31 |
|
|
| 32 |
static const int poll_timeout_ms = 100; |
|
| 33 |
|
|
| 34 |
struct loopback_pkt_hdr_type{
|
|
| 35 |
boost::uint32_t words32; |
|
| 36 |
boost::uint32_t checksum; |
|
| 37 |
boost::uint64_t seq_num; |
|
| 38 |
}; |
|
| 39 |
|
|
| 40 |
struct loopback_pkt_type{
|
|
| 41 |
loopback_pkt_hdr_type hdr; |
|
| 42 |
boost::uint32_t data[(bytes_per_frame-sizeof(loopback_pkt_hdr_type))/sizeof(boost::uint32_t)]; |
|
| 43 |
}; |
|
| 44 |
|
|
| 45 |
static ring_buffer_info (*recv_info)[]; |
|
| 46 |
static ring_buffer_info (*send_info)[]; |
|
| 47 |
static loopback_pkt_type (*recv_buff)[]; |
|
| 48 |
static loopback_pkt_type (*send_buff)[]; |
|
| 49 |
|
|
| 50 |
static struct usrp_e_ring_buffer_size_t rb_size; |
|
| 51 |
|
|
| 52 |
static bool running = true; |
|
| 53 |
|
|
| 54 |
static boost::uint64_t seq_errors = 0; |
|
| 55 |
static boost::uint64_t checksum_errors = 0; |
|
| 56 |
static boost::uint64_t sent_words32 = 0; |
|
| 57 |
static boost::uint64_t recvd_words32 = 0; |
|
| 58 |
|
|
| 59 |
static inline void print_pkt(const loopback_pkt_type &pkt){
|
|
| 60 |
std::cout << std::endl; |
|
| 61 |
std::cout << "pkt.hdr.words32 " << pkt.hdr.words32 << std::endl; |
|
| 62 |
std::cout << "pkt.hdr.checksum " << pkt.hdr.checksum << std::endl; |
|
| 63 |
std::cout << "pkt.hdr.seq_num " << pkt.hdr.seq_num << std::endl; |
|
| 64 |
} |
|
| 65 |
|
|
| 66 |
boost::uint32_t my_checksum(void *buff, size_t size32){
|
|
| 67 |
boost::uint32_t x = 0; |
|
| 68 |
for (size_t i = 0; i < size32; i++){
|
|
| 69 |
x += reinterpret_cast<boost::uint32_t *>(buff)[i]; |
|
| 70 |
x ^= reinterpret_cast<boost::uint32_t *>(buff)[i]; |
|
| 71 |
} |
|
| 72 |
return x; |
|
| 73 |
} |
|
| 74 |
|
|
| 75 |
/*********************************************************************** |
|
| 76 |
* Read thread - recv frames and verify checksum |
|
| 77 |
**********************************************************************/ |
|
| 78 |
static void read_thread(void){
|
|
| 79 |
std::cout << "start read thread... " << std::endl; |
|
| 80 |
|
|
| 81 |
boost::uint64_t seq_num = 0; |
|
| 82 |
size_t index = 0; |
|
| 83 |
|
|
| 84 |
while (running){
|
|
| 85 |
|
|
| 86 |
loopback_pkt_type &pkt = (*recv_buff)[index]; |
|
| 87 |
ring_buffer_info &info = (*recv_info)[index]; |
|
| 88 |
|
|
| 89 |
//wait for frame available |
|
| 90 |
if (not (info.flags & RB_USER)){
|
|
| 91 |
pollfd pfd; |
|
| 92 |
pfd.fd = fp; |
|
| 93 |
pfd.events = POLLIN; |
|
| 94 |
if (poll(&pfd, 1, poll_timeout_ms) <= 0){
|
|
| 95 |
std::cout << "Read poll timeout, exiting read thread!" << std::endl; |
|
| 96 |
running = false; |
|
| 97 |
return; |
|
| 98 |
} |
|
| 99 |
} |
|
| 100 |
info.flags = RB_USER_PROCESS; |
|
| 101 |
|
|
| 102 |
//print_pkt(pkt); |
|
| 103 |
|
|
| 104 |
//handle checksum |
|
| 105 |
const boost::uint32_t expected_checksum = pkt.hdr.checksum; |
|
| 106 |
pkt.hdr.checksum = 0; //set to zero for calculation |
|
| 107 |
const boost::uint32_t actual_checksum = my_checksum(&pkt, pkt.hdr.words32); |
|
| 108 |
if (expected_checksum != actual_checksum){
|
|
| 109 |
checksum_errors++; |
|
| 110 |
std::cerr << "C"; |
|
| 111 |
} |
|
| 112 |
else{
|
|
| 113 |
recvd_words32 += pkt.hdr.words32; |
|
| 114 |
} |
|
| 115 |
|
|
| 116 |
//handle sequence |
|
| 117 |
if (seq_num != pkt.hdr.seq_num){
|
|
| 118 |
seq_errors++; |
|
| 119 |
std::cerr << "S"; |
|
| 120 |
} |
|
| 121 |
seq_num = pkt.hdr.seq_num+1; |
|
| 122 |
|
|
| 123 |
//release the packet |
|
| 124 |
info.flags = RB_KERNEL; |
|
| 125 |
|
|
| 126 |
//increment index and wrap around to zero |
|
| 127 |
index++; |
|
| 128 |
if (index == size_t(rb_size.num_rx_frames)) index = 0; |
|
| 129 |
} |
|
| 130 |
|
|
| 131 |
} |
|
| 132 |
|
|
| 133 |
/*********************************************************************** |
|
| 134 |
* Write thread - send frames and calculate checksum |
|
| 135 |
**********************************************************************/ |
|
| 136 |
static void write_thread(const size_t num_words32){
|
|
| 137 |
std::cout << "start write thread... " << std::endl; |
|
| 138 |
|
|
| 139 |
srandom(std::time(NULL)); |
|
| 140 |
|
|
| 141 |
boost::uint64_t seq_num = 0; |
|
| 142 |
size_t index = 0; |
|
| 143 |
|
|
| 144 |
//write into tmp and memcopy into pkt to avoid cache issues |
|
| 145 |
loopback_pkt_type pkt_tmp; |
|
| 146 |
|
|
| 147 |
while (running){
|
|
| 148 |
|
|
| 149 |
ring_buffer_info &info = (*send_info)[index]; |
|
| 150 |
|
|
| 151 |
//wait for frame available |
|
| 152 |
if (not (info.flags & RB_KERNEL)){
|
|
| 153 |
pollfd pfd; |
|
| 154 |
pfd.fd = fp; |
|
| 155 |
pfd.events = POLLOUT; |
|
| 156 |
if (poll(&pfd, 1, poll_timeout_ms) <= 0){
|
|
| 157 |
std::cout << "Write poll timeout, exiting write thread!" << std::endl; |
|
| 158 |
running = false; |
|
| 159 |
return; |
|
| 160 |
} |
|
| 161 |
} |
|
| 162 |
|
|
| 163 |
//fill packet header and body |
|
| 164 |
const boost::uint32_t seed = random(); |
|
| 165 |
pkt_tmp.hdr.words32 = sizeof(pkt_tmp.hdr)/sizeof(boost::uint32_t) + num_words32; |
|
| 166 |
pkt_tmp.hdr.checksum = 0; //set to zero for checksum() |
|
| 167 |
pkt_tmp.hdr.seq_num = seq_num++; |
|
| 168 |
for (size_t i = 0; i < num_words32; i++) pkt_tmp.data[i] = seed + i; |
|
| 169 |
pkt_tmp.hdr.checksum = my_checksum(&pkt_tmp, pkt_tmp.hdr.words32); |
|
| 170 |
sent_words32 += pkt_tmp.hdr.words32; |
|
| 171 |
|
|
| 172 |
loopback_pkt_type &pkt = (*send_buff)[index]; |
|
| 173 |
std::memcpy(&pkt, &pkt_tmp, pkt_tmp.hdr.words32*sizeof(boost::uint32_t)); |
|
| 174 |
|
|
| 175 |
//print_pkt(pkt); |
|
| 176 |
|
|
| 177 |
//commit the packet |
|
| 178 |
info.len = pkt_tmp.hdr.words32*sizeof(boost::uint32_t); |
|
| 179 |
info.flags = RB_USER; |
|
| 180 |
::write(fp, NULL, 0); |
|
| 181 |
|
|
| 182 |
//increment index and wrap around to zero |
|
| 183 |
index++; |
|
| 184 |
if (index == size_t(rb_size.num_tx_frames)) index = 0; |
|
| 185 |
} |
|
| 186 |
} |
|
| 187 |
|
|
| 188 |
/*********************************************************************** |
|
| 189 |
* Setup memory mapped ring buffer |
|
| 190 |
**********************************************************************/ |
|
| 191 |
static void setup_ring(void){
|
|
| 192 |
std::cout << "setup memory mapped ring buffer... " << std::flush; |
|
| 193 |
|
|
| 194 |
//calculate various sizes |
|
| 195 |
const size_t page_size = getpagesize(); |
|
| 196 |
ioctl(fp, USRP_E_GET_RB_INFO, &rb_size); |
|
| 197 |
const size_t map_size = (rb_size.num_pages_rx_flags + rb_size.num_pages_tx_flags) * page_size + |
|
| 198 |
(rb_size.num_rx_frames + rb_size.num_tx_frames) * (page_size >> 1); |
|
| 199 |
|
|
| 200 |
//call into memory map |
|
| 201 |
void *mem = ::mmap(0, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fp, 0); |
|
| 202 |
if (mem == MAP_FAILED) {
|
|
| 203 |
std::cerr << "mmap failed" << std::endl; |
|
| 204 |
std::exit(-1); |
|
| 205 |
} |
|
| 206 |
|
|
| 207 |
//calculate the memory offsets for info and buffers |
|
| 208 |
const size_t recv_info_off = 0; |
|
| 209 |
const size_t recv_buff_off = recv_info_off + (rb_size.num_pages_rx_flags * page_size); |
|
| 210 |
const size_t send_info_off = recv_buff_off + (rb_size.num_rx_frames * page_size/2); |
|
| 211 |
const size_t send_buff_off = send_info_off + (rb_size.num_pages_tx_flags * page_size); |
|
| 212 |
|
|
| 213 |
//set the internal pointers for info and buffers |
|
| 214 |
typedef ring_buffer_info (*rbi_pta)[]; |
|
| 215 |
typedef loopback_pkt_type (*pkt_pta)[]; |
|
| 216 |
char *rb_ptr = reinterpret_cast<char *>(mem); |
|
| 217 |
recv_info = reinterpret_cast<rbi_pta>(rb_ptr + recv_info_off); |
|
| 218 |
recv_buff = reinterpret_cast<pkt_pta>(rb_ptr + recv_buff_off); |
|
| 219 |
send_info = reinterpret_cast<rbi_pta>(rb_ptr + send_info_off); |
|
| 220 |
send_buff = reinterpret_cast<pkt_pta>(rb_ptr + send_buff_off); |
|
| 221 |
|
|
| 222 |
std::cout << "done" << std::endl; |
|
| 223 |
} |
|
| 224 |
|
|
| 225 |
/*********************************************************************** |
|
| 226 |
* Main |
|
| 227 |
**********************************************************************/ |
|
| 228 |
#include <boost/program_options.hpp> |
|
| 229 |
|
|
| 230 |
int main(int argc, char *argv[]){
|
|
| 231 |
|
|
| 232 |
//variables to be set by po |
|
| 233 |
double duration; |
|
| 234 |
size_t nwords; |
|
| 235 |
|
|
| 236 |
//setup the program options |
|
| 237 |
namespace po = boost::program_options; |
|
| 238 |
po::options_description desc("Allowed options");
|
|
| 239 |
desc.add_options() |
|
| 240 |
("help", "help message")
|
|
| 241 |
("duration", po::value<double>(&duration)->default_value(10), "number of seconds to run loopback")
|
|
| 242 |
("nwords", po::value<size_t>(&nwords)->default_value(400), "number of words32 to send per packet")
|
|
| 243 |
; |
|
| 244 |
po::variables_map vm; |
|
| 245 |
po::store(po::parse_command_line(argc, argv, desc), vm); |
|
| 246 |
po::notify(vm); |
|
| 247 |
|
|
| 248 |
//print the help message |
|
| 249 |
if (vm.count("help")){
|
|
| 250 |
std::cout << boost::format("UHD USRP-E-Loopback %s") % desc << std::endl;
|
|
| 251 |
return ~0; |
|
| 252 |
} |
|
| 253 |
|
|
| 254 |
if ((fp = ::open("/dev/usrp_e0", O_RDWR)) < 0){
|
|
| 255 |
std::cerr << "Open failed" << std::endl; |
|
| 256 |
return -1; |
|
| 257 |
} |
|
| 258 |
|
|
| 259 |
//set the mode to loopback |
|
| 260 |
poke16(E100_REG_MISC_XFER_RATE, (1<<8) | (1<<9)); |
|
| 261 |
|
|
| 262 |
//setup the ring buffer |
|
| 263 |
setup_ring(); |
|
| 264 |
|
|
| 265 |
//spawn threads |
|
| 266 |
boost::thread_group tg; |
|
| 267 |
tg.create_thread(boost::bind(&read_thread)); |
|
| 268 |
tg.create_thread(boost::bind(&write_thread, nwords)); |
|
| 269 |
|
|
| 270 |
const boost::system_time start_time = boost::get_system_time(); |
|
| 271 |
const boost::system_time finish_time = start_time + boost::posix_time::milliseconds(long(duration*1000)); |
|
| 272 |
while (boost::get_system_time() < finish_time){
|
|
| 273 |
boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); |
|
| 274 |
std::cerr << "."; |
|
| 275 |
} |
|
| 276 |
running = false; |
|
| 277 |
tg.join_all(); |
|
| 278 |
|
|
| 279 |
std::cout << std::endl; |
|
| 280 |
std::cout << "seq_errors " << seq_errors << std::endl; |
|
| 281 |
std::cout << "checksum_errors " << checksum_errors << std::endl; |
|
| 282 |
std::cout << "sent_words32 " << sent_words32 << std::endl; |
|
| 283 |
std::cout << "recvd_words32 " << recvd_words32 << std::endl; |
|
| 284 |
std::cout << "approx send rate " << (sent_words32/duration)/1e6 << "Msps" << std::endl; |
|
| 285 |
std::cout << "approx recv rate " << (recvd_words32/duration)/1e6 << "Msps" << std::endl; |
|
| 286 |
|
|
| 287 |
::close(fp); |
|
| 288 |
return seq_errors + checksum_errors; |
|
| 289 |
} |
|
Also available in: Unified diff