diff options
Diffstat (limited to 'host/usrp_e_utils')
-rw-r--r-- | host/usrp_e_utils/CMakeLists.txt | 42 | ||||
-rw-r--r-- | host/usrp_e_utils/common.hpp | 64 | ||||
-rw-r--r-- | host/usrp_e_utils/usrp-e-loopback.cpp | 298 | ||||
-rw-r--r-- | host/usrp_e_utils/usrp-e-wb-test.cpp | 68 |
4 files changed, 472 insertions, 0 deletions
diff --git a/host/usrp_e_utils/CMakeLists.txt b/host/usrp_e_utils/CMakeLists.txt new file mode 100644 index 000000000..0d6763174 --- /dev/null +++ b/host/usrp_e_utils/CMakeLists.txt @@ -0,0 +1,42 @@ +# +# Copyright 2011 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# USRP embedded utilities that get installed into the share path +######################################################################## +LIBUHD_REGISTER_COMPONENT("USRP-E Utils" ENABLE_USRP_E_UTILS OFF "LINUX" OFF) + +IF(ENABLE_USRP_E_UTILS) + ENABLE_LANGUAGE(C) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp/e100) + INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp/e100/include) + + SET(usrp_e_utils_sources + usrp-e-loopback.cpp + usrp-e-wb-test.cpp + ) + + #for each source: build an executable and install + FOREACH(util_source ${usrp_e_utils_sources}) + GET_FILENAME_COMPONENT(util_name ${util_source} NAME_WE) + ADD_EXECUTABLE(${util_name} ${util_source}) + TARGET_LINK_LIBRARIES(${util_name} ${Boost_LIBRARIES}) + INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/usrp_e_utils) + ENDFOREACH(util_source) + +ENDIF(ENABLE_USRP_E_UTILS) diff --git a/host/usrp_e_utils/common.hpp b/host/usrp_e_utils/common.hpp new file mode 100644 index 000000000..989aecd06 --- /dev/null +++ b/host/usrp_e_utils/common.hpp @@ -0,0 +1,64 @@ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <sys/ioctl.h> //ioctl +#include <fcntl.h> //open, close + +#include <linux/usrp_e.h> +#include "e100_regs.hpp" + +static int fp; + +static inline int peek16(int reg){ + int ret; + struct usrp_e_ctl16 d; + + d.offset = reg; + d.count = 1; + ret = ioctl(fp, USRP_E_READ_CTL16, &d); + return d.buf[0]; +} + +static inline void poke16(int reg, int val){ + int ret; + struct usrp_e_ctl16 d; + + d.offset = reg; + d.count = 1; + d.buf[0] = val; + ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); +} + +static inline int peek32(int reg){ + int ret; + struct usrp_e_ctl32 d; + + d.offset = reg; + d.count = 1; + ret = ioctl(fp, USRP_E_READ_CTL32, &d); + return d.buf[0]; +} + +static inline void poke32(int reg, int val){ + int ret; + struct usrp_e_ctl32 d; + + d.offset = reg; + d.count = 1; + d.buf[0] = val; + ret = ioctl(fp, USRP_E_WRITE_CTL32, &d); +} diff --git a/host/usrp_e_utils/usrp-e-loopback.cpp b/host/usrp_e_utils/usrp-e-loopback.cpp new file mode 100644 index 000000000..d4220adc3 --- /dev/null +++ b/host/usrp_e_utils/usrp-e-loopback.cpp @@ -0,0 +1,298 @@ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "common.hpp" +#include <cstdlib> +#include <cstdio> +#include <cstring> +#include <ctime> +#include <iostream> +#include <boost/thread/thread.hpp> +#include <boost/format.hpp> +#include <boost/cstdint.hpp> +#include <sys/mman.h> //mmap +#include <unistd.h> //getpagesize +#include <poll.h> //poll + +static const size_t bytes_per_frame = 2048; + +static const int poll_timeout_ms = 100; + +struct loopback_pkt_hdr_type{ + boost::uint32_t words32; + boost::uint32_t checksum; + boost::uint64_t seq_num; +}; + +struct loopback_pkt_type{ + loopback_pkt_hdr_type hdr; + boost::uint32_t data[(bytes_per_frame-sizeof(loopback_pkt_hdr_type))/sizeof(boost::uint32_t)]; +}; + +static ring_buffer_info (*recv_info)[]; +static ring_buffer_info (*send_info)[]; +static loopback_pkt_type (*recv_buff)[]; +static loopback_pkt_type (*send_buff)[]; + +static struct usrp_e_ring_buffer_size_t rb_size; + +static bool running = true; + +static boost::uint64_t seq_errors = 0; +static boost::uint64_t checksum_errors = 0; +static boost::uint64_t sent_words32 = 0; +static boost::uint64_t recvd_words32 = 0; + +static inline void print_pkt(const loopback_pkt_type &pkt){ + std::cout << std::endl; + std::cout << "pkt.hdr.words32 " << pkt.hdr.words32 << std::endl; + std::cout << "pkt.hdr.checksum " << pkt.hdr.checksum << std::endl; + std::cout << "pkt.hdr.seq_num " << pkt.hdr.seq_num << std::endl; +} + +boost::uint32_t my_checksum(void *buff, size_t size32){ + boost::uint32_t x = 0; + for (size_t i = 0; i < size32; i++){ + x += reinterpret_cast<boost::uint32_t *>(buff)[i]; + x ^= reinterpret_cast<boost::uint32_t *>(buff)[i]; + } + return x; +} + +/*********************************************************************** + * Read thread - recv frames and verify checksum + **********************************************************************/ +static void read_thread(void){ + std::cout << "start read thread... " << std::endl; + + boost::uint64_t seq_num = 0; + size_t index = 0; + + while (running){ + + loopback_pkt_type &pkt = (*recv_buff)[index]; + ring_buffer_info &info = (*recv_info)[index]; + + //wait for frame available + if (not (info.flags & RB_USER)){ + pollfd pfd; + pfd.fd = fp; + pfd.events = POLLIN; + if (poll(&pfd, 1, poll_timeout_ms) <= 0){ + std::cout << "Read poll timeout, exiting read thread!" << std::endl; + running = false; + return; + } + } + info.flags = RB_USER_PROCESS; + + //print_pkt(pkt); + + //handle checksum + const boost::uint32_t expected_checksum = pkt.hdr.checksum; + pkt.hdr.checksum = 0; //set to zero for calculation + const boost::uint32_t actual_checksum = my_checksum(&pkt, pkt.hdr.words32); + if (expected_checksum != actual_checksum){ + checksum_errors++; + std::cerr << "C"; + } + else{ + recvd_words32 += pkt.hdr.words32; + } + + //handle sequence + if (seq_num != pkt.hdr.seq_num){ + seq_errors++; + std::cerr << "S"; + } + seq_num = pkt.hdr.seq_num+1; + + //release the packet + info.flags = RB_KERNEL; + + //increment index and wrap around to zero + index++; + if (index == size_t(rb_size.num_rx_frames)) index = 0; + } + +} + +/*********************************************************************** + * Write thread - send frames and calculate checksum + **********************************************************************/ +static void write_thread(const size_t num_words32){ + std::cout << "start write thread... " << std::endl; + + srandom(std::time(NULL)); + + boost::uint64_t seq_num = 0; + size_t index = 0; + + //write into tmp and memcopy into pkt to avoid cache issues + loopback_pkt_type pkt_tmp; + + while (running){ + + ring_buffer_info &info = (*send_info)[index]; + + //wait for frame available + if (not (info.flags & RB_KERNEL)){ + pollfd pfd; + pfd.fd = fp; + pfd.events = POLLOUT; + if (poll(&pfd, 1, poll_timeout_ms) <= 0){ + std::cout << "Write poll timeout, exiting write thread!" << std::endl; + running = false; + return; + } + } + + //fill packet header and body + const boost::uint32_t seed = random(); + pkt_tmp.hdr.words32 = sizeof(pkt_tmp.hdr)/sizeof(boost::uint32_t) + num_words32; + pkt_tmp.hdr.checksum = 0; //set to zero for checksum() + pkt_tmp.hdr.seq_num = seq_num++; + for (size_t i = 0; i < num_words32; i++) pkt_tmp.data[i] = seed + i; + pkt_tmp.hdr.checksum = my_checksum(&pkt_tmp, pkt_tmp.hdr.words32); + sent_words32 += pkt_tmp.hdr.words32; + + loopback_pkt_type &pkt = (*send_buff)[index]; + std::memcpy(&pkt, &pkt_tmp, pkt_tmp.hdr.words32*sizeof(boost::uint32_t)); + + //print_pkt(pkt); + + //commit the packet + info.len = pkt_tmp.hdr.words32*sizeof(boost::uint32_t); + info.flags = RB_USER; + ::write(fp, NULL, 0); + + //increment index and wrap around to zero + index++; + if (index == size_t(rb_size.num_tx_frames)) index = 0; + } +} + +/*********************************************************************** + * Setup memory mapped ring buffer + **********************************************************************/ +static void setup_ring(void){ + std::cout << "setup memory mapped ring buffer... " << std::flush; + + //calculate various sizes + const size_t page_size = getpagesize(); + ioctl(fp, USRP_E_GET_RB_INFO, &rb_size); + const size_t map_size = (rb_size.num_pages_rx_flags + rb_size.num_pages_tx_flags) * page_size + + (rb_size.num_rx_frames + rb_size.num_tx_frames) * (page_size >> 1); + + //call into memory map + void *mem = ::mmap(0, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fp, 0); + if (mem == MAP_FAILED) { + std::cerr << "mmap failed" << std::endl; + std::exit(-1); + } + + //calculate the memory offsets for info and buffers + const size_t recv_info_off = 0; + const size_t recv_buff_off = recv_info_off + (rb_size.num_pages_rx_flags * page_size); + const size_t send_info_off = recv_buff_off + (rb_size.num_rx_frames * page_size/2); + const size_t send_buff_off = send_info_off + (rb_size.num_pages_tx_flags * page_size); + + //set the internal pointers for info and buffers + typedef ring_buffer_info (*rbi_pta)[]; + typedef loopback_pkt_type (*pkt_pta)[]; + char *rb_ptr = reinterpret_cast<char *>(mem); + recv_info = reinterpret_cast<rbi_pta>(rb_ptr + recv_info_off); + recv_buff = reinterpret_cast<pkt_pta>(rb_ptr + recv_buff_off); + send_info = reinterpret_cast<rbi_pta>(rb_ptr + send_info_off); + send_buff = reinterpret_cast<pkt_pta>(rb_ptr + send_buff_off); + + std::cout << "done" << std::endl; +} + +/*********************************************************************** + * Main + **********************************************************************/ +#include <boost/program_options.hpp> + +int main(int argc, char *argv[]){ + + //variables to be set by po + double duration; + size_t nwords; + + //setup the program options + namespace po = boost::program_options; + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("duration", po::value<double>(&duration)->default_value(10), "number of seconds to run loopback") + ("nwords", po::value<size_t>(&nwords)->default_value(400), "number of words32 to send per packet") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //print the help message + if (vm.count("help")){ + std::cout << boost::format("UHD USRP-E-Loopback %s") % desc << std::endl; + return ~0; + } + + if ((fp = ::open("/dev/usrp_e0", O_RDWR)) < 0){ + std::cerr << "Open failed" << std::endl; + return -1; + } + + //set the mode to loopback + poke16(E100_REG_MISC_XFER_RATE, (1<<8) | (1<<9)); + + //clear FIFO state in FPGA and kernel + poke32(E100_REG_CLEAR_FIFO, 0); + ::close(fp); + if ((fp = ::open("/dev/usrp_e0", O_RDWR)) < 0){ + std::cerr << "Open failed" << std::endl; + return -1; + } + + //setup the ring buffer + setup_ring(); + + //spawn threads + boost::thread_group tg; + tg.create_thread(boost::bind(&read_thread)); + tg.create_thread(boost::bind(&write_thread, nwords)); + + const boost::system_time start_time = boost::get_system_time(); + const boost::system_time finish_time = start_time + boost::posix_time::milliseconds(long(duration*1000)); + while (boost::get_system_time() < finish_time){ + boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); + std::cerr << "."; + } + running = false; + tg.join_all(); + + std::cout << std::endl; + std::cout << "seq_errors " << seq_errors << std::endl; + std::cout << "checksum_errors " << checksum_errors << std::endl; + std::cout << "sent_words32 " << sent_words32 << std::endl; + std::cout << "recvd_words32 " << recvd_words32 << std::endl; + std::cout << "approx send rate " << (sent_words32/duration)/1e6 << "Msps" << std::endl; + std::cout << "approx recv rate " << (recvd_words32/duration)/1e6 << "Msps" << std::endl; + + ::close(fp); + return seq_errors + checksum_errors; +} diff --git a/host/usrp_e_utils/usrp-e-wb-test.cpp b/host/usrp_e_utils/usrp-e-wb-test.cpp new file mode 100644 index 000000000..2b793dc06 --- /dev/null +++ b/host/usrp_e_utils/usrp-e-wb-test.cpp @@ -0,0 +1,68 @@ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "common.hpp" +#include <cstdlib> +#include <cstdio> +#include <ctime> +#include <iostream> + +static const size_t num_test_iters = 10000000; + +int main(int, char *[]){ + + srandom(time(NULL)); //seed random() + + if ((fp = ::open("/dev/usrp_e0", O_RDWR)) < 0){ + std::cerr << "Open failed" << std::endl; + return -1; + } + + size_t num_pass = 0, num_fail = 0; + for (size_t i = 0; i < num_test_iters; i++){ + if(i%1000000 == 0) { + std::cout << "num pass: " << num_pass; + std::cout << "\tnum fail: " << num_fail << std::endl; + } + //make random values + int random_test32 = ::random(); + int random_test16 = ::random() & 0xffff; + //int random_secs = ::random(); + + //set a bunch of registers + poke16(E100_REG_MISC_TEST, random_test16); + poke32(E100_REG_SR_MISC_TEST32, random_test32); + //poke32(E100_REG_TIME64_TICKS, 0); + //poke32(E100_REG_TIME64_IMM, 1); //immediate + //poke32(E100_REG_TIME64_SECS, random_secs); + + //read a bunch of registers + if ( + (peek16(E100_REG_MISC_TEST) == random_test16) and + (peek32(E100_REG_RB_MISC_TEST32) == random_test32) and +// (peek32(E100_REG_RB_TIME_NOW_SECS) == random_secs) and +// (peek32(E100_REG_RB_TIME_NOW_TICKS) < 1000000) and + true) num_pass++; + else num_fail++; + } + + std::cout << "num pass: " << num_pass << std::endl; + std::cout << "num fail: " << num_fail << std::endl; + + ::close(fp); + return 0; +} |