From 9d1423b9506c89a51a10b6119d01ce9a82a13b0c Mon Sep 17 00:00:00 2001 From: eb Date: Wed, 30 Apr 2008 03:52:31 +0000 Subject: Merged features/inband-usb -r6431:8293 into trunk. git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@8295 221aa14e-8319-0410-a670-987f0aec2ac5 --- usrp/host/lib/inband/Makefile.am | 7 +- usrp/host/lib/inband/fake_usrp.cc | 135 ------- usrp/host/lib/inband/fake_usrp.h | 43 --- usrp/host/lib/inband/qa_inband_usrp_server.cc | 239 ++++++++++-- usrp/host/lib/inband/qa_inband_usrp_server.h | 2 + usrp/host/lib/inband/test_usrp_inband.cc | 298 -------------- usrp/host/lib/inband/usrp_inband_usb_packet.cc | 235 +++++++----- usrp/host/lib/inband/usrp_inband_usb_packet.h | 5 + usrp/host/lib/inband/usrp_rx.cc | 63 ++- usrp/host/lib/inband/usrp_rx.h | 6 +- usrp/host/lib/inband/usrp_rx_stub.cc | 181 +++++---- usrp/host/lib/inband/usrp_rx_stub.h | 12 +- usrp/host/lib/inband/usrp_server.cc | 512 +++++++++++++++++++++++-- usrp/host/lib/inband/usrp_server.h | 12 + usrp/host/lib/inband/usrp_server.mbh | 2 +- usrp/host/lib/inband/usrp_tx.cc | 14 +- usrp/host/lib/inband/usrp_tx_stub.cc | 6 +- usrp/host/lib/inband/usrp_usb_interface.cc | 114 ++++-- usrp/host/lib/inband/usrp_usb_interface.h | 4 +- 19 files changed, 1141 insertions(+), 749 deletions(-) delete mode 100644 usrp/host/lib/inband/fake_usrp.cc delete mode 100644 usrp/host/lib/inband/fake_usrp.h delete mode 100644 usrp/host/lib/inband/test_usrp_inband.cc (limited to 'usrp/host/lib/inband') diff --git a/usrp/host/lib/inband/Makefile.am b/usrp/host/lib/inband/Makefile.am index 77d1f6699..a41ac18b8 100644 --- a/usrp/host/lib/inband/Makefile.am +++ b/usrp/host/lib/inband/Makefile.am @@ -76,7 +76,6 @@ include_HEADERS = \ usrp_usb_interface.h noinst_HEADERS = \ - fake_usrp.h \ qa_inband.h \ qa_inband_packet_prims.h \ qa_inband_usrp_server.h \ @@ -109,14 +108,10 @@ libusrp_inband_qa_la_LIBADD = \ # ------------------------------------------------------------------------ noinst_PROGRAMS = \ - test_inband \ - test_usrp_inband + test_inband test_inband_SOURCES = test_inband.cc test_inband_LDADD = libusrp_inband-qa.la -test_usrp_inband_SOURCES = test_usrp_inband.cc -test_usrp_inband_LDADD = libusrp_inband-qa.la - MOSTLYCLEANFILES = \ $(BUILT_SOURCES) *~ *.pyc diff --git a/usrp/host/lib/inband/fake_usrp.cc b/usrp/host/lib/inband/fake_usrp.cc deleted file mode 100644 index 8a66d5c18..000000000 --- a/usrp/host/lib/inband/fake_usrp.cc +++ /dev/null @@ -1,135 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2007 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio 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, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif -#include -#include -#include -#include -#include - -typedef usrp_inband_usb_packet transport_pkt; // makes conversion to gigabit easy - -fake_usrp::fake_usrp() -{ - std::cout << "[fake_usrp] Initializing...\n"; -} - -fake_usrp::~fake_usrp() {} - -long -fake_usrp::write_bus(transport_pkt *pkts, long n_bytes) -{ - std::cout << "[fake_usrp] Bytes over bus: " << n_bytes << "\n"; - - // I'm assuming that a control packet cannot exist in a burst of data packets, - // therefore i read only the first packet's channel in the current burst - if(pkts[0].chan() == 0x1f) { - return control_block(pkts, n_bytes); - } else { - return data_block(pkts, n_bytes); - } - -} - -long -fake_usrp::data_block(transport_pkt *pkts, long n_bytes) -{ - std::cout << "[fake_usrp] Entering data block\n"; - - // Infer the number of packets from the byte count to do logical tests - long n_pkts = static_cast(std::ceil(n_bytes / (double)transport_pkt::max_pkt_size())); - - std::cout << "[fake_usrp] Number of packets: " << n_pkts << "\n"; - - // The first packet should have the start of burst, and the last packet should have end of burst - if(pkts[0].start_of_burst() && pkts[n_pkts-1].end_of_burst()) { - std::cout << "[fake_usrp] Correct burst flags set\n"; - } else { - std::cout << "[fake_usrp] Incorrect burst flags set!\n"; - return 0; - } - - // All other flags should be set to 0 (e.g., overrun should not be set yet) on ALL packets - for(int i=0; i < n_pkts; i++) { - if(pkts[i].overrun()) { - std::cout << "[fake_usrp] Incorrect set of overrun flag on transmit\n"; - return 0; - } else if(pkts[i].underrun()) { - std::cout << "[fake_usrp] Incorrect set of underrun flag on transmit\n"; - return 0; - } else if(pkts[i].dropped()) { - std::cout << "[fake_usrp] Incorrect set of drop flag on transmit\n"; - return 0; - } - } - std::cout << "[fake_usrp] Correct overrun, underrun, and drop flags on transmit (initialized to 0)\n"; - - // The first packet should have a timestamp, other packets should have "NOW" - if(pkts[0].timestamp() != 0xffffffff) { - std::cout << "[fake_usrp] Correct timestamp on first packet\n"; - } else { - std::cout << "[fake_usrp] Initial packet should not have the 0xffffffff timestamp\n"; - return 0; - } - - // Check that all of the other packets include the NOW timestamp - int check_stamps=1; - for(int i=1; i < n_pkts; i++) // start at 1 to skip the first packet - if(pkts[i].timestamp() != 0xffffffff) - check_stamps=0; - - if(check_stamps) { - std::cout << "[fake_usrp] Correct NOW timestamps (0xffffffff) on intermediate burst packets\n"; - } else { - std::cout << "[fake_usrp] Incorrect timestamps on intermediate burst packets\n"; - return 0; - } - - // Since we are being transparent about samples, we do not ensure the payload is correct, however - // it should be the case that if there are >1 packets, all packets except the last packet should - // have a full payload size - if(n_pkts > 1) { - int check_size=1; - for(int i=0; i < n_pkts-1; i++) - if(pkts[i].payload_len() != transport_pkt::max_payload()) - check_size=0; - - if(check_size) { - std::cout << "[fake_usrp] Correct payload size sanity check on packets\n"; - } else { - std::cout << "[fake_usrp] Failed payload size sanity check\n"; - return 0; - } - } - - return 1; -} - -long -fake_usrp::control_block(transport_pkt *pkts, long n_bytes) -{ - std::cout << "[fake_usrp] Entering control block\n"; - - return 1; -} diff --git a/usrp/host/lib/inband/fake_usrp.h b/usrp/host/lib/inband/fake_usrp.h deleted file mode 100644 index 818c5a506..000000000 --- a/usrp/host/lib/inband/fake_usrp.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2007 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio 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, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#ifndef INCLUDED_FAKE_USRP_H -#define INCLUDED_FAKE_USRP_H - -#include -typedef usrp_inband_usb_packet transport_pkt; - -/*! - * \brief Implements a fake USRP for testing without hardware - */ -class fake_usrp -{ - public: - fake_usrp(); - ~fake_usrp(); - long write_bus(transport_pkt *pkts, long n_bytes); - - protected: - long data_block(transport_pkt *pkts, long n_bytes); - long control_block(transport_pkt *pkts, long n_bytes); -}; - -#endif /* INCLUDED_FAKE_USRP_H */ - diff --git a/usrp/host/lib/inband/qa_inband_usrp_server.cc b/usrp/host/lib/inband/qa_inband_usrp_server.cc index b01e74e00..e457e8d64 100644 --- a/usrp/host/lib/inband/qa_inband_usrp_server.cc +++ b/usrp/host/lib/inband/qa_inband_usrp_server.cc @@ -46,6 +46,8 @@ typedef usrp_inband_usb_packet transport_pkt; // makes conversion to gigabit e static bool verbose = false; +static pmt_t s_timeout = pmt_intern("%timeout"); + // ---------------------------------------------------------------------------------------------- class qa_alloc_top : public mb_mblock @@ -903,10 +905,10 @@ class qa_rx_top : public mb_mblock long d_rx_chan; - long d_got_response_recv; + bool d_got_response_recv; - long d_nmsg_to_recv; - long d_nmsg_recvd; + mb_time d_t0; + double d_delta_t; public: qa_rx_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); @@ -927,18 +929,19 @@ qa_rx_top::qa_rx_top(mb_runtime *runtime, const std::string &instance_name, pmt_ d_got_response_recv(false) { - d_nmsg_to_recv=12; - d_nmsg_recvd=0; - d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); // Use the stub with the usrp_server - pmt_t usrp_server_dict = pmt_make_dict(); - pmt_dict_set(usrp_server_dict, pmt_intern("fake-usrp"), PMT_T); + pmt_t usrp_dict = pmt_make_dict(); + // Set TX and RX interpolations + pmt_dict_set(usrp_dict, + pmt_intern("decim-rx"), + pmt_from_long(128)); + pmt_dict_set(usrp_dict, pmt_intern("fake-usrp"), PMT_T); // Test the TX side - define_component("server", "usrp_server", usrp_server_dict); + define_component("server", "usrp_server", usrp_dict); connect("self", "rx0", "server", "rx0"); connect("self", "cs", "server", "cs"); } @@ -967,17 +970,10 @@ qa_rx_top::run_tests() pmt_list2(PMT_NIL, pmt_from_long(0))); - // A small sleep is used to ensure, if working properly, a recv - // response comes through successfully before the close gets - // through - usleep(1000); - - d_rx->send(s_cmd_stop_recv_raw_samples, - pmt_list2(PMT_NIL, - pmt_from_long(0))); - - d_cs->send(s_cmd_close, pmt_list1(pmt_list2(s_response_close,PMT_T))); - + // Schedule a small timeout in which we expect to have received at least one + // packet worth of samples from the stub + d_t0 = mb_time::time(); + schedule_one_shot_timeout(d_t0 + 0.01, PMT_NIL); } @@ -992,26 +988,37 @@ qa_rx_top::handle_message(mb_message_sptr msg) pmt_t expected = pmt_nth(0, data); pmt_t status = pmt_nth(1, data); + + // If we get a timeout we shutdown + if(pmt_eq(event, s_timeout)) { + if(verbose) + std::cout << "[qa_rx_top] Got timeout\n"; + d_rx->send(s_cmd_stop_recv_raw_samples, + pmt_list2(PMT_NIL, + pmt_from_long(0))); + + d_cs->send(s_cmd_close, pmt_list1(pmt_list2(s_response_close,PMT_T))); + return; + } // For testing RX, an invocation handle is not generated by the stub, // therefore the same approach for testing is not used. We simply // expect all responses to be true. if(pmt_eq(event, s_response_recv_raw_samples)) { - if(!pmt_eqv(status, PMT_T)) { - if(verbose) - std::cout << "Got: " << status << " Expected: " << PMT_T << "\n"; - shutdown_all(PMT_F); - return; - } - else { + if(pmt_eqv(status, PMT_T)) { + if(verbose) std::cout << "[qa_rx_top] Received expected response for message " - << d_nmsg_recvd << " (" << event << ")\n"; // All we want is 1 response receive! Can't guarantee exact numbers d_got_response_recv = true; } + else { + if(verbose) + std::cout << "Got: " << status << " Expected: " << PMT_T << "\n"; + shutdown_all(PMT_F); + } return; } @@ -1026,8 +1033,7 @@ qa_rx_top::handle_message(mb_message_sptr msg) } else { if(verbose) std::cout << "[qa_rx_top] Received expected response for message " - << d_nmsg_recvd - << " (" << event << ")\n"; + << " (" << event << ")\n"; } if (pmt_eq(msg->port_id(), d_rx->port_symbol())) { @@ -1051,12 +1057,7 @@ qa_rx_top::handle_message(mb_message_sptr msg) std::cout << "[qa_rx_top] No response message before close\n"; return; } - } - - - d_nmsg_recvd++; - } @@ -1079,6 +1080,160 @@ qa_rx_top::check_allocation(mb_message_sptr msg) REGISTER_MBLOCK_CLASS(qa_rx_top); +// ---------------------------------------------------------------------------------------------- + +class qa_rid_top : public mb_mblock +{ + mb_port_sptr d_tx; + mb_port_sptr d_rx; + mb_port_sptr d_cs; + + long d_npongs; + long d_tcycles; + long d_cycles; + long d_max_rid; + + mb_time d_t0; + double d_delta_t; + + public: + qa_rid_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~qa_rid_top(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void run_tests(); + void send_max_pings(); +}; + +qa_rid_top::qa_rid_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + d_npongs = 0; + d_tcycles = 3; + d_cycles = d_tcycles; + d_max_rid = usrp_server::D_MAX_RID; + d_delta_t = 0.1; + + + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Use the stub with the usrp_server + pmt_t usrp_server_dict = pmt_make_dict(); + pmt_dict_set(usrp_server_dict, pmt_intern("fake-usrp"),PMT_T); + + // Test the TX side + define_component("server", "usrp_server", usrp_server_dict); + connect("self", "tx0", "server", "tx0"); + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); + +} + +qa_rid_top::~qa_rid_top(){} + +void +qa_rid_top::initial_transition() +{ + run_tests(); +} + +void +qa_rid_top::run_tests() +{ + if(verbose) + std::cout << "[qa_rid_top] Starting tests...\n"; + + // Retrieve information about the USRP, then run tests + d_cs->send(s_cmd_open, + pmt_list2(pmt_list2(s_response_open, PMT_T), + pmt_from_long(0))); + + // should be able to allocate 1 byte + d_tx->send(s_cmd_allocate_channel, + pmt_list2(pmt_list2(s_response_allocate_channel, PMT_T), + pmt_from_long(1))); + + d_rx->send(s_cmd_allocate_channel, + pmt_list2(pmt_list2(s_response_allocate_channel, PMT_T), + pmt_from_long(1))); + + // Need to start receiving to read from the USRP to get C/S responses + d_rx->send(s_cmd_start_recv_raw_samples, + pmt_list2(PMT_NIL, + pmt_from_long(0))); + + // Build a subpacket of MAX_RID pings and wait a small amount for all of the + // responses and fire off another MAX_RID. If MAX_RID*2 responses are + // received, the RID recycling is working correctly. + // Schedule a timer in which we expect to have received all of the responses, + // which will send off another MAX_RID worth. + send_max_pings(); + d_t0 = mb_time::time(); + schedule_one_shot_timeout(d_t0 + d_delta_t, PMT_NIL); +} + +void +qa_rid_top::send_max_pings() +{ + pmt_t ping = pmt_list2(s_op_ping_fixed, + pmt_list2(pmt_from_long(0), + pmt_from_long(0))); + + pmt_t sub_packets = PMT_NIL; + + for(int i=0; isend(s_cmd_to_control_channel, + pmt_list2(pmt_list2(s_response_from_control_channel, PMT_T), + sub_packets)); +} + +void +qa_rid_top::handle_message(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + pmt_t event = msg->signal(); + + // If we get a timeout we ensure we got a maximum RID number of responses. + if(pmt_eq(event, s_timeout)) { + if(verbose) + std::cout << "[qa_rid_top] Got timeout, received so far: " + << d_npongs << "\n"; + + d_cycles--; + + if(d_cycles==0 && d_npongs == d_max_rid*d_tcycles) { + shutdown_all(PMT_T); + } + else if(d_cycles==0) { + + std::cout << "[qa_rid_top] d_npongs: " << d_npongs + << " expected: " << d_max_rid*d_tcycles + << std::endl; + + shutdown_all(PMT_F); + } + else { + send_max_pings(); + d_t0 = mb_time::time(); + schedule_one_shot_timeout(d_t0 + d_delta_t, PMT_NIL); + } + + } + else if(pmt_eq(event, s_response_from_control_channel)) + { + d_npongs++; + } + +} + +REGISTER_MBLOCK_CLASS(qa_rid_top); + // ---------------------------------------------------------------------------------------------- @@ -1398,3 +1553,17 @@ qa_inband_usrp_server::test_cs() CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); } + +void +qa_inband_usrp_server::test_rid() +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_T; + + // std::cout << "\n\n-----------------\n"; + // std::cout << " RUNNING RID TESTS \n"; + + rt->run("top", "qa_rid_top", PMT_F, &result); + + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} diff --git a/usrp/host/lib/inband/qa_inband_usrp_server.h b/usrp/host/lib/inband/qa_inband_usrp_server.h index 5db57c3ed..52a4a0b06 100644 --- a/usrp/host/lib/inband/qa_inband_usrp_server.h +++ b/usrp/host/lib/inband/qa_inband_usrp_server.h @@ -34,6 +34,7 @@ class qa_inband_usrp_server : public CppUnit::TestCase { CPPUNIT_TEST(test_tx); CPPUNIT_TEST(test_rx); CPPUNIT_TEST(test_cs); + CPPUNIT_TEST(test_rid); CPPUNIT_TEST_SUITE_END(); private: @@ -43,6 +44,7 @@ class qa_inband_usrp_server : public CppUnit::TestCase { void test_tx(); void test_rx(); void test_cs(); + void test_rid(); }; #endif /* INCLUDED_QA_INBAND_USRP_SERVER_H */ diff --git a/usrp/host/lib/inband/test_usrp_inband.cc b/usrp/host/lib/inband/test_usrp_inband.cc deleted file mode 100644 index 64fbf3a97..000000000 --- a/usrp/host/lib/inband/test_usrp_inband.cc +++ /dev/null @@ -1,298 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2007 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio 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, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "usrp_standard.h" - -typedef usrp_inband_usb_packet transport_pkt; - -// Signal set for the USRP server -static pmt_t s_cmd_open = pmt_intern("cmd-open"); -static pmt_t s_response_open = pmt_intern("response-open"); -static pmt_t s_cmd_close = pmt_intern("cmd-close"); -static pmt_t s_response_close = pmt_intern("response-close"); -static pmt_t s_cmd_allocate_channel = pmt_intern("cmd-allocate-channel"); -static pmt_t s_response_allocate_channel = pmt_intern("response-allocate-channel"); -static pmt_t s_send_allocate_channel = pmt_intern("send-allocate-channel"); -static pmt_t s_cmd_deallocate_channel = pmt_intern("cmd-deallocate-channel"); -static pmt_t s_response_deallocate_channel = pmt_intern("response-deallocate-channel"); -static pmt_t s_send_deallocate_channel = pmt_intern("send-deallocate-channel"); -static pmt_t s_cmd_max_capacity = pmt_intern("cmd-max-capacity"); -static pmt_t s_response_max_capacity = pmt_intern("response-max-capacity"); -static pmt_t s_cmd_ntx_chan = pmt_intern("cmd-ntx-chan"); -static pmt_t s_cmd_nrx_chan = pmt_intern("cmd-nrx-chan"); -static pmt_t s_response_ntx_chan = pmt_intern("response-ntx-chan"); -static pmt_t s_response_nrx_chan = pmt_intern("response-nrx-chan"); -static pmt_t s_cmd_current_capacity_allocation = pmt_intern("cmd-current-capacity-allocation"); -static pmt_t s_response_current_capacity_allocation = pmt_intern("response-current-capacity-allocation"); -static pmt_t s_cmd_xmit_raw_frame = pmt_intern("cmd-xmit-raw-frame"); -static pmt_t s_response_xmit_raw_frame = pmt_intern("response-xmit-raw-frame"); - -bool loopback_p = false; -bool counting_p = false; -bool fake_usrp_p = false; -char *prog_name; - -static void -set_progname (char *path) -{ - char *p = strrchr (path, '/'); - if (p != 0) - prog_name = p+1; - else - prog_name = path; -} - -static void -usage() -{ - fprintf (stderr, "usage: %s [-l]\n", prog_name); - fprintf (stderr, " [-l] digital loopback in FPGA\n"); - fprintf (stderr, " [-c] counting in FPGA\n"); - fprintf (stderr, " [-f] fake usrp\n"); - - exit(1); -} - -int -main(int argc, char **argv) -{ - int ch; - - set_progname(argv[0]); - - mb_runtime_sptr rt = mb_make_runtime(); - pmt_t result = PMT_T; - - while ((ch = getopt(argc, argv, "flc")) != EOF) { - switch(ch) { - - case 'l': - loopback_p = true; - break; - - case 'c': - counting_p = true; - break; - - case 'f': - fake_usrp_p = true; - break; - - default: - usage(); - } - } - - - std::cout << "[test_usrp_inband] Starting...\n"; - - rt->run("top", "test_usrp_inband_top", PMT_F, &result); -} - -class test_usrp_inband_top : public mb_mblock -{ - mb_port_sptr d_tx; - mb_port_sptr d_cs; - - long d_tx_chan; - - public: - test_usrp_inband_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); - ~test_usrp_inband_top(); - void initial_transition(); - void handle_message(mb_message_sptr msg); - - protected: - void open_usrp(); - void close_usrp(); - void check_message(mb_message_sptr msg); - void allocate_channel(); - void send_packets(); -}; - -test_usrp_inband_top::test_usrp_inband_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) - : mb_mblock(runtime, instance_name, user_arg) -{ - std::cout << "[TEST_USRP_INBAND_TOP] Initializing...\n"; - - d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); - d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); - - // Test the TX side - - // Pass a dictionary to usrp_server which specifies which interface to use, the stub or USRP - pmt_t usrp_server_dict = pmt_make_dict(); - - if(fake_usrp_p) - pmt_dict_set(usrp_server_dict, pmt_intern("usrp-interface"), pmt_intern("usrp_usb_interface_stub")); - - define_component("server", "usrp_server", usrp_server_dict); - connect("self", "tx0", "server", "tx0"); - connect("self", "cs", "server", "cs"); -} - -test_usrp_inband_top::~test_usrp_inband_top() -{ -} - -void -test_usrp_inband_top::initial_transition() -{ - open_usrp(); -} - -void -test_usrp_inband_top::handle_message(mb_message_sptr msg) -{ - pmt_t event = msg->signal(); // the "name" of the message - pmt_t port_id = msg->port_id(); // which port it came in on - pmt_t data = msg->data(); - pmt_t metadata = msg->metadata(); - pmt_t status; - - if (pmt_eq(port_id, d_cs->port_symbol())) { // message came in on our control/status port - - //---------- OPEN RESPONSE ----------// - if (pmt_eq(event, s_response_open)) { - status = pmt_nth(1, data); - - if(pmt_eq(status, PMT_T)) { - std::cout << "[TEST_USRP_INBAND_TOP] Success opening USRP\n"; - } - else { - std::cout << "[TEST_USRP_INBAND_TOP] Received error message opening USRP\n"; - shutdown_all(PMT_F); - } - - allocate_channel(); - - return; - } - //--------- CLOSE RESPONSE -----------// - else if (pmt_eq(event, s_response_close)) { - status = pmt_nth(1, data); - - if(pmt_eq(status, PMT_T)) { - std::cout << "[TEST_USRP_INBAND_TOP] Successfully closed USRP\n"; - } - else { - std::cout << "[TEST_USRP_INBAND_TOP] Received error message closing USRP\n"; - shutdown_all(PMT_F); - } - - shutdown_all(PMT_T); - - return; - } - } - - if (pmt_eq(port_id, d_tx->port_symbol())) { - - //---------- ALLOCATE RESPONSE ---------// - if(pmt_eq(event, s_response_allocate_channel)) { - status = pmt_nth(1, data); - pmt_t channel = pmt_nth(2, data); - - if(pmt_eq(status, PMT_T)) { - d_tx_chan = pmt_to_long(channel); - std::cout << "[TEST_USRP_INBAND_TOP] Received allocation on channel " << d_tx_chan << "\n"; - } - else { - std::cout << "[TEST_USRP_INBAND_TOP] Error allocating channel\n"; - shutdown_all(PMT_F); - } - - send_packets(); - - return; - } - //----------- XMIT RESPONSE ------------// - else if(pmt_eq(event, s_response_xmit_raw_frame)) { - status = pmt_nth(1, data); - - if(pmt_eq(status, PMT_T)) { - std::cout << "[TEST_USRP_INBAND_TOP] Transmission successful\n"; - } - else { - std::cout << "[TEST_USRP_INBAND_TOP] Failed transmission\n"; - shutdown_all(PMT_F); - } - - close_usrp(); - - return; - } - } - - std::cout << "[TEST_USRP_INBAND_TOP] Received unhandled message: " << event << "\n"; -} - -void -test_usrp_inband_top::allocate_channel() -{ - std::cout << "[TEST_USRP_INBAND_TOP] Requesting channel allocation...\n"; - - d_tx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(1))); -} - -void -test_usrp_inband_top::send_packets() -{ - std::cout << "[TEST_USRP_INBAND_TOP] Sending single packet..\n"; - d_tx->send(s_cmd_xmit_raw_frame, pmt_list4(pmt_from_long(1), pmt_from_long(d_tx_chan), pmt_make_u32vector(transport_pkt::max_payload()/4, 0), pmt_from_long(0))); - -} - -void -test_usrp_inband_top::open_usrp() -{ - pmt_t usrp = pmt_from_long(0); - - long rx_mode = 0; - - if(loopback_p) - rx_mode |= usrp_standard_rx::FPGA_MODE_LOOPBACK; - if(counting_p) - rx_mode |= usrp_standard_rx::FPGA_MODE_COUNTING; - - d_cs->send(s_cmd_open, pmt_list2(PMT_NIL, usrp)); -} - -void -test_usrp_inband_top::close_usrp() -{ - d_cs->send(s_cmd_close, pmt_list1(PMT_NIL)); -} - -REGISTER_MBLOCK_CLASS(test_usrp_inband_top); diff --git a/usrp/host/lib/inband/usrp_inband_usb_packet.cc b/usrp/host/lib/inband/usrp_inband_usb_packet.cc index ee4cb22f3..2f02ecc3f 100644 --- a/usrp/host/lib/inband/usrp_inband_usb_packet.cc +++ b/usrp/host/lib/inband/usrp_inband_usb_packet.cc @@ -30,6 +30,14 @@ #include #include +/*! + * \brief Aligns the packet payload on a 32 bit boundary. This is essential to + * all control/status packets so that the inband FPGA code can parse them + * easily. + * + * \returns true if successful or if the packet was already aligned; false if it + * cannot be aligned. + */ bool usrp_inband_usb_packet::align32() { int p_len = payload_len(); @@ -44,18 +52,20 @@ bool usrp_inband_usb_packet::align32() if((MAX_PAYLOAD - p_len) < bytes_needed) return false; - p_len += bytes_needed; - - int h_flags = flags(); - int h_chan = chan(); - int h_tag = tag(); - int h_payload_len = p_len; - - set_header(h_flags, h_chan, h_tag, h_payload_len); + incr_header_len(bytes_needed); return true; } +/*! + * \brief Adds a ping command to the current control packet. + * + * The \p rid is the rid to be associated with the ping response and \p ping_val + * is currently unused. + * + * \returns true if adding the ping command was successful, false otherwise + * (i.e. no space in the current packet). + */ bool usrp_inband_usb_packet::cs_ping(long rid, long ping_val) { if(!align32()) @@ -78,17 +88,20 @@ bool usrp_inband_usb_packet::cs_ping(long rid, long ping_val) *payload = host_to_usrp_u32(ping); // Update payload length - int h_flags = flags(); - int h_chan = chan(); - int h_tag = tag(); - int h_payload_len = payload_len() + CS_FIXED_LEN + CS_PING_LEN; - - set_header(h_flags, h_chan, h_tag, h_payload_len); + incr_header_len(CS_FIXED_LEN + CS_PING_LEN); return true; } - +/*! + * \brief Adds a ping response to the packet. This is used by the fake USRP + * code to generate fake responses for pings. + * + * The \p rid is the RID to be associated with the response and \p ping_val is + * currently unused. + * + * \returns true if the ping reply was added successfully, false otherwise. + */ bool usrp_inband_usb_packet::cs_ping_reply(long rid, long ping_val) { if(!align32()) @@ -111,16 +124,20 @@ bool usrp_inband_usb_packet::cs_ping_reply(long rid, long ping_val) *payload = host_to_usrp_u32(ping); // Update payload length - int h_flags = flags(); - int h_chan = chan(); - int h_tag = tag(); - int h_payload_len = payload_len() + CS_FIXED_LEN + CS_PING_LEN; - - set_header(h_flags, h_chan, h_tag, h_payload_len); + incr_header_len(CS_FIXED_LEN + CS_PING_LEN); return true; } +/*! + * \brief Adds a write register command to the packet. + * + * The \p reg_num is the register number for which the value \p val will be + * written to. + * + * \returns true if the command was added to the packet successfully, false + * otherwise. + */ bool usrp_inband_usb_packet::cs_write_reg(long reg_num, long val) { if(!align32()) @@ -149,16 +166,19 @@ bool usrp_inband_usb_packet::cs_write_reg(long reg_num, long val) *payload = host_to_usrp_u32((uint32_t) val); // Rebuild the header to update the payload length - int h_flags = flags(); - int h_chan = chan(); - int h_tag = tag(); - int h_payload_len = payload_len() + CS_FIXED_LEN + CS_WRITEREG_LEN; - - set_header(h_flags, h_chan, h_tag, h_payload_len); + incr_header_len(CS_FIXED_LEN + CS_WRITEREG_LEN); return true; } +/*! + * \brief Adds a write register masked command to the packet. + * + * The \p reg_num is the register number for which the value \p val will be + * written, masked by \p mask + * + * \returns true if the command was added to the packet, false otherwise. + */ bool usrp_inband_usb_packet::cs_write_reg_masked(long reg_num, long val, long mask) { if(!align32()) @@ -190,16 +210,19 @@ bool usrp_inband_usb_packet::cs_write_reg_masked(long reg_num, long val, long ma *payload = host_to_usrp_u32((uint32_t) mask); // Rebuild the header to update the payload length - int h_flags = flags(); - int h_chan = chan(); - int h_tag = tag(); - int h_payload_len = payload_len() + CS_FIXED_LEN + CS_WRITEREGMASKED_LEN; - - set_header(h_flags, h_chan, h_tag, h_payload_len); + incr_header_len(CS_FIXED_LEN + CS_WRITEREGMASKED_LEN); return true; } +/*! + * \brief Adds a read register message to the packet. + * + * The \p rid will be the associated RID returned with the response, and \p + * reg_num is the register to be read. + * + * \returns true if the command was added to the packet, false otherwise. + */ bool usrp_inband_usb_packet::cs_read_reg(long rid, long reg_num) { if(!align32()) @@ -222,16 +245,22 @@ bool usrp_inband_usb_packet::cs_read_reg(long rid, long reg_num) *payload = host_to_usrp_u32(read_reg); // Update payload length - int h_flags = flags(); - int h_chan = chan(); - int h_tag = tag(); - int h_payload_len = payload_len() + CS_FIXED_LEN + CS_READREG_LEN; - - set_header(h_flags, h_chan, h_tag, h_payload_len); + incr_header_len(CS_FIXED_LEN + CS_READREG_LEN); return true; } +/*! + * \brief Adds a read register reply response to the current packet. This is + * used by the fake USRP code to generate fake register read responses for + * testing. + * + * The \p rid is the associated RID to be included in the response, \p reg_num + * is the register the read is coming from, and \p reg_val is the value of the + * read. + * + * \returns true if the command was added to the packet, false otherwise. + */ bool usrp_inband_usb_packet::cs_read_reg_reply(long rid, long reg_num, long reg_val) { if(!align32()) @@ -258,16 +287,19 @@ bool usrp_inband_usb_packet::cs_read_reg_reply(long rid, long reg_num, long reg_ *payload = host_to_usrp_u32((uint32_t) reg_val); // Update payload length - int h_flags = flags(); - int h_chan = chan(); - int h_tag = tag(); - int h_payload_len = payload_len() + CS_FIXED_LEN + CS_READREGREPLY_LEN; - - set_header(h_flags, h_chan, h_tag, h_payload_len); + incr_header_len(CS_FIXED_LEN + CS_READREGREPLY_LEN); return true; } +/*! + * \brief Adds a delay command to the current packet. + * + * The \p ticks parameter is the number of clock ticks the FPGA should delay + * parsing for, which is added to the packet. + * + * \returns true if the command was added to the packet, false otherwise. + */ bool usrp_inband_usb_packet::cs_delay(long ticks) { if(!align32()) @@ -289,16 +321,16 @@ bool usrp_inband_usb_packet::cs_delay(long ticks) *payload = host_to_usrp_u32(delay); // Update payload length - int h_flags = flags(); - int h_chan = chan(); - int h_tag = tag(); - int h_payload_len = payload_len() + CS_FIXED_LEN + CS_DELAY_LEN; - - set_header(h_flags, h_chan, h_tag, h_payload_len); + incr_header_len(CS_FIXED_LEN + CS_DELAY_LEN); return true; } +/*! + * \brief + * + * \returns true if the command was added to the packet, false otherwise. + */ bool usrp_inband_usb_packet::cs_i2c_write(long i2c_addr, uint8_t *i2c_data, size_t data_len) { if(!align32()) @@ -328,16 +360,20 @@ bool usrp_inband_usb_packet::cs_i2c_write(long i2c_addr, uint8_t *i2c_data, size memcpy(payload, i2c_data, data_len); // Update payload length - int h_flags = flags(); - int h_chan = chan(); - int h_tag = tag(); - int h_payload_len = payload_len() + CS_FIXED_LEN + i2c_len; - - set_header(h_flags, h_chan, h_tag, h_payload_len); + incr_header_len(CS_FIXED_LEN + i2c_len); return true; } +/*! + * \brief Adds an I2C read command to the current packet. + * + * The \p rid is the associated RID to return with the read response, \p + * i2c_addr is the address to read from on the I2C bus, and \p n_bytes is the + * number of bytes to be read from the bus. + * + * \returns true if the command was added to the packet, false otherwise. + */ bool usrp_inband_usb_packet::cs_i2c_read(long rid, long i2c_addr, long n_bytes) { if(!align32()) @@ -367,16 +403,20 @@ bool usrp_inband_usb_packet::cs_i2c_read(long rid, long i2c_addr, long n_bytes) *payload = host_to_usrp_u32(word1); // Update payload length - int h_flags = flags(); - int h_chan = chan(); - int h_tag = tag(); - int h_payload_len = payload_len() + CS_FIXED_LEN + CS_I2CREAD_LEN; - - set_header(h_flags, h_chan, h_tag, h_payload_len); + incr_header_len(CS_FIXED_LEN + CS_I2CREAD_LEN); return true; } +/*! + * \brief Adds an I2C read reply response to the current packet. This is used + * by the fake USRP code to generate fake I2C responses. + * + * The \p rid is the RID to be associated with the response, \p i2c_addr is the + * address on the I2C bus that the \p i2c_data of \p i2c_data_len was read from. + * + * \returns true if the command was added to the packet, false otherwise. + */ bool usrp_inband_usb_packet::cs_i2c_read_reply(long rid, long i2c_addr, uint8_t *i2c_data, long i2c_data_len) { if(!align32()) @@ -406,16 +446,16 @@ bool usrp_inband_usb_packet::cs_i2c_read_reply(long rid, long i2c_addr, uint8_t memcpy(payload, i2c_data, i2c_data_len); // Update payload length - int h_flags = flags(); - int h_chan = chan(); - int h_tag = tag(); - int h_payload_len = payload_len() + CS_FIXED_LEN + i2c_len; - - set_header(h_flags, h_chan, h_tag, h_payload_len); + incr_header_len(CS_FIXED_LEN + i2c_len); return true; } +/*! + * \brief Adds a SPI write command to the current packet. + * + * \returns true if the command was added to the packet, false otherwise. + */ bool usrp_inband_usb_packet::cs_spi_write(long enables, long format, long opt_header_bytes, uint8_t *spi_data, long spi_data_len) { if(!align32()) @@ -454,16 +494,16 @@ bool usrp_inband_usb_packet::cs_spi_write(long enables, long format, long opt_he memcpy(payload, spi_data, spi_data_len); // Update payload length - int h_flags = flags(); - int h_chan = chan(); - int h_tag = tag(); - int h_payload_len = payload_len() + CS_FIXED_LEN + spi_len; - - set_header(h_flags, h_chan, h_tag, h_payload_len); + incr_header_len(CS_FIXED_LEN + spi_len); return true; } +/*! + * \brief Adds a SPI bus read command to the packet. + * + * \returns true if the command was added to the packet, false otherwise. + */ bool usrp_inband_usb_packet::cs_spi_read(long rid, long enables, long format, long opt_header_bytes, long n_bytes) { if(!align32()) @@ -508,16 +548,17 @@ bool usrp_inband_usb_packet::cs_spi_read(long rid, long enables, long format, lo *payload = host_to_usrp_u32(word); // Update payload length - int h_flags = flags(); - int h_chan = chan(); - int h_tag = tag(); - int h_payload_len = payload_len() + CS_FIXED_LEN + CS_SPIREAD_LEN; + incr_header_len(CS_FIXED_LEN + CS_SPIREAD_LEN); - set_header(h_flags, h_chan, h_tag, h_payload_len); - return true; } +/*! + * \brief Adds an SPI read reply to the current packet. This is used by the + * fake USRP code to generate fake responses for SPI reads. + * + * \returns true if the command was added to the packet, false otherwise. + */ bool usrp_inband_usb_packet::cs_spi_read_reply(long rid, uint8_t *spi_data, long spi_data_len) { if(!align32()) @@ -546,30 +587,32 @@ bool usrp_inband_usb_packet::cs_spi_read_reply(long rid, uint8_t *spi_data, long memcpy(payload, spi_data, spi_data_len); // Update payload length - int h_flags = flags(); - int h_chan = chan(); - int h_tag = tag(); - int h_payload_len = payload_len() + CS_FIXED_LEN + spi_len; - - set_header(h_flags, h_chan, h_tag, h_payload_len); + incr_header_len(CS_FIXED_LEN + spi_len); return true; } -// Takes an offset to the beginning of a subpacket and extracts the -// length of the subpacket +/*! + * \brief Since all control packets contain subpackets which have the length of + * the subpacket at a uniform location in the subpacket, this will return the + * subpacket length given a byte offset of the start of the subpacket from the beginning of the packet. + * + * \returns the length of the subpacket + */ int usrp_inband_usb_packet::cs_len(int payload_offset) { uint32_t subpkt = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset))); return (subpkt >> CS_LEN_SHIFT) & CS_LEN_MASK; } -// The following method takes an offset within the packet payload to extract -// a control/status subpacket and construct a pmt response which includes the -// proper signal and arguments specified by usrp-low-level-cs. The USRP -// server could therefore use this to read subpackets and pass them responses -// back up to the application. It's arguable that only reply packets should -// be parsed here, however we parse others for use in debugging or failure -// reporting on the transmit side of packets. +/*! + * \brief The following method takes an offset within the packet payload to + * extract a control/status subpacket and constructs a pmt response which + * includes the proper signal and arguments specified by usrp-low-level-cs. The + * USRP server could therefore use this to read subpackets and pass them + * responses back up to the application. It's arguable that only reply packets + * should be parsed here, however we parse others for use in debugging or + * failure reporting on the transmit side of packets. + */ pmt_t usrp_inband_usb_packet::read_subpacket(int payload_offset) { uint32_t subpkt = usrp_to_host_u32(*((uint32_t *)(d_payload + payload_offset))); diff --git a/usrp/host/lib/inband/usrp_inband_usb_packet.h b/usrp/host/lib/inband/usrp_inband_usb_packet.h index 8c19b1aeb..8f59d1b65 100644 --- a/usrp/host/lib/inband/usrp_inband_usb_packet.h +++ b/usrp/host/lib/inband/usrp_inband_usb_packet.h @@ -31,6 +31,7 @@ static const int USB_PKT_SIZE = 512; // bytes static const int MAX_PAYLOAD = USB_PKT_SIZE-2*sizeof(uint32_t); +static const int CONTROL_CHAN = 0x1f; class usrp_inband_usb_packet { // @@ -150,6 +151,10 @@ public: | ((payload_len & PAYLOAD_LEN_MASK) << PAYLOAD_LEN_SHIFT)); d_word0 = host_to_usrp_u32(word0); } + + void incr_header_len(int val) { + set_header(flags(), chan(), tag(), payload_len() + val); + } uint32_t timestamp() const { return usrp_to_host_u32(d_timestamp); diff --git a/usrp/host/lib/inband/usrp_rx.cc b/usrp/host/lib/inband/usrp_rx.cc index caa2d7175..71c042a50 100644 --- a/usrp/host/lib/inband/usrp_rx.cc +++ b/usrp/host/lib/inband/usrp_rx.cc @@ -40,25 +40,30 @@ typedef usrp_inband_usb_packet transport_pkt; static const bool verbose = false; +bool usrp_rx_stop; + usrp_rx::usrp_rx(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg) : mb_mblock(rt, instance_name, user_arg), - d_disk_write(false) + d_disk_write(false), + d_disk_write_pkt(false) // if true, writes full packet, else just the payload { d_cs = define_port("cs", "usrp-rx-cs", true, mb_port::EXTERNAL); - //d_disk_write=true; - if(d_disk_write) { - d_ofile.open("rx_data.dat",std::ios::binary|std::ios::out); + d_ofile0.open("rx_data_chan0.dat",std::ios::binary|std::ios::out); + d_ofile1.open("rx_data_chan1.dat",std::ios::binary|std::ios::out); d_cs_ofile.open("rx_cs.dat",std::ios::binary|std::ios::out); } + + usrp_rx_stop = false; } usrp_rx::~usrp_rx() { if(d_disk_write) { - d_ofile.close(); + d_ofile0.close(); + d_ofile1.close(); d_cs_ofile.close(); } } @@ -69,6 +74,12 @@ usrp_rx::initial_transition() } +/*! + * \brief Handles incoming signals to to the m-block, wihch should only ever be + * a single message: cmd-usrrp-rx-start-reading. There is no signal to stop + * reading as the m-block goes in to a forever loop to read inband packets from + * the bus. + */ void usrp_rx::handle_message(mb_message_sptr msg) { @@ -85,6 +96,17 @@ usrp_rx::handle_message(mb_message_sptr msg) } } +/*! + * \brief Performs the actual reading of data from the USB bus, called by + * handle_message() when a cmd-usrp-rx-start-reading signal is received. + * + * The method enters a forever loop where it continues to read data from the bus + * and generate read responses to the higher layer. Currently, shared memory is + * used to exit this loop. + * + * The \p data parameter is a PMT list which contains only a single element, an + * invocation handle which will be returned with all read respones. + */ void usrp_rx::read_and_respond(pmt_t data) { @@ -104,7 +126,7 @@ usrp_rx::read_and_respond(pmt_t data) std::cout << "[usrp_rx] Waiting for packets..\n"; // Read by 512 which is packet size and send them back up - while(1) { + while(!usrp_rx_stop) { pmt_t v_pkt = pmt_make_u8vector(pkt_size, 0); transport_pkt *pkt = @@ -124,19 +146,38 @@ usrp_rx::read_and_respond(pmt_t data) d_cs->send(s_response_usrp_rx_read, pmt_list3(PMT_NIL, PMT_T, v_pkt)); - if(verbose) + if(verbose && 0) std::cout << "[usrp_rx] Read 1 packet\n"; if(d_disk_write) { - if(pkt->chan() == 0x1f) + if(pkt->chan() == CONTROL_CHAN) d_cs_ofile.write((const char *)pkt, transport_pkt::max_pkt_size()); - else - d_ofile.write((const char *)pkt, transport_pkt::max_pkt_size()); + else { + if(d_disk_write_pkt) { + if(pkt->chan() == 0) + d_ofile0.write((const char *)pkt, transport_pkt::max_pkt_size()); + else if(pkt->chan() == 1) + d_ofile1.write((const char *)pkt, transport_pkt::max_pkt_size()); + } else { + if(pkt->chan() == 0) + d_ofile0.write((const char *)pkt->payload(), transport_pkt::max_payload()); + else if(pkt->chan() == 1) + d_ofile1.write((const char *)pkt->payload(), transport_pkt::max_payload()); + } + } d_cs_ofile.flush(); - d_ofile.flush(); + d_ofile0.flush(); + d_ofile1.flush(); } } + + usrp_rx_stop = false; + + if(verbose) { + std::cout << "[USRP_RX] Stopping...\n"; + fflush(stdout); + } } REGISTER_MBLOCK_CLASS(usrp_rx); diff --git a/usrp/host/lib/inband/usrp_rx.h b/usrp/host/lib/inband/usrp_rx.h index e1a90a781..10104bd66 100644 --- a/usrp/host/lib/inband/usrp_rx.h +++ b/usrp/host/lib/inband/usrp_rx.h @@ -26,6 +26,8 @@ class usrp_standard_rx; +extern bool usrp_rx_stop; // used to communicate a 'stop' to the RX stub + /*! * \brief Implements the low level usb interface to the USRP */ @@ -35,7 +37,9 @@ class usrp_rx : public mb_mblock usrp_standard_rx *d_urx; bool d_disk_write; - std::ofstream d_ofile; + bool d_disk_write_pkt; + std::ofstream d_ofile0; + std::ofstream d_ofile1; std::ofstream d_cs_ofile; public: diff --git a/usrp/host/lib/inband/usrp_rx_stub.cc b/usrp/host/lib/inband/usrp_rx_stub.cc index 4bdb106b1..1c96b7a7a 100644 --- a/usrp/host/lib/inband/usrp_rx_stub.cc +++ b/usrp/host/lib/inband/usrp_rx_stub.cc @@ -43,7 +43,7 @@ typedef usrp_inband_usb_packet transport_pkt; static const bool verbose = false; -bool usrp_rx_stop; +bool usrp_rx_stop_stub; // Used for the fake control packet response code to send the responses back up // the RX. The TX stub dumps responses in to this queue. @@ -52,15 +52,32 @@ std::queue d_cs_queue; usrp_rx_stub::usrp_rx_stub(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg) : mb_mblock(rt, instance_name, user_arg), d_samples_per_frame((long)(126)), + d_decim_rx(128), d_amplitude(16384), d_disk_write(false) { + + // Information about the rates are passed all the way from the app in the form + // of a dictionary. We use this to read the RX decimation rate and compute + // the approximate number of MS/s as a form of flow control for the stub. + pmt_t usrp_dict = user_arg; + + if (pmt_is_dict(usrp_dict)) { + // Read the RX decimation rate + if(pmt_t decim_rx = pmt_dict_ref(usrp_dict, + pmt_intern("decim-rx"), + PMT_NIL)) { + if(!pmt_eqv(decim_rx, PMT_NIL)) + d_decim_rx = pmt_to_long(decim_rx); + } + } + d_cs = define_port("cs", "usrp-rx-cs", true, mb_port::EXTERNAL); // initialize NCO double freq = 100e3; int interp = 32; // 32 -> 4MS/s - double sample_rate = 128e6 / interp; + double sample_rate = 64e6 / interp; d_nco.set_freq(2*M_PI * freq/sample_rate); //d_disk_write = true; @@ -68,7 +85,7 @@ usrp_rx_stub::usrp_rx_stub(mb_runtime *rt, const std::string &instance_name, pmt if(d_disk_write) d_ofile.open("raw_rx.dat",std::ios::binary|std::ios::out); - usrp_rx_stop = false; + usrp_rx_stop_stub = false; } usrp_rx_stub::~usrp_rx_stub() @@ -80,7 +97,6 @@ usrp_rx_stub::~usrp_rx_stub() void usrp_rx_stub::initial_transition() { - } void @@ -90,94 +106,121 @@ usrp_rx_stub::handle_message(mb_message_sptr msg) pmt_t port_id = msg->port_id(); pmt_t data = msg->data(); + if (pmt_eq(msg->signal(), s_timeout) + && !pmt_eq(msg->data(), s_done)) { + + if(!usrp_rx_stop_stub) + read_and_respond(); + else { // requested to stop + cancel_timeout(msg->metadata()); + usrp_rx_stop_stub=false; + if(verbose) + std::cout << "[USRP_RX_STUB] Stopping RX stub\n"; + } + + } + // Theoretically only have 1 message to ever expect, but // want to make sure its at least what we want - if(pmt_eq(port_id, d_cs->port_symbol())) { + if(pmt_eq(port_id, d_cs->port_symbol()) + && pmt_eqv(event, s_cmd_usrp_rx_start_reading)) { if(verbose) - std::cout << "[USRP_RX_STUB] Starting...\n"; + std::cout << "[USRP_RX_STUB] Starting with decim @ " + << d_decim_rx << std::endl; - if(pmt_eqv(event, s_cmd_usrp_rx_start_reading)) - read_and_respond(data); + start_packet_timer(); } } +// Setup a periodic timer which will drive packet generation void -usrp_rx_stub::read_and_respond(pmt_t data) +usrp_rx_stub::start_packet_timer() { + d_t0 = mb_time::time(); // current time + + // Calculate the inter-packet arrival time. + double samples_per_sec = (64.0/(double)d_decim_rx)*1000000.0; + double frames_per_sec = samples_per_sec / (double)d_samples_per_frame; + double frame_rate = 1.0 / frames_per_sec; + + if(verbose) { + std::cout << "[USRP_RX_STUB] Scheduling periodic packet generator\n"; + std::cout << "\tsamples_per_sec: " << samples_per_sec << std::endl; + std::cout << "\tframes_per_sec: " << frames_per_sec << std::endl; + std::cout << "\tframe_rate: " << frame_rate << std::endl; + } - while(!usrp_rx_stop) { + schedule_periodic_timeout(d_t0 + frame_rate, mb_time(frame_rate), PMT_T); +} - long nsamples_this_frame = d_samples_per_frame; +void +usrp_rx_stub::read_and_respond() +{ - size_t nshorts = 2 * nsamples_this_frame; // 16-bit I & Q - long channel = 0; - long n_bytes = nshorts*2; - pmt_t uvec = pmt_make_s16vector(nshorts, 0); - size_t ignore; - int16_t *samples = pmt_s16vector_writeable_elements(uvec, ignore); + long nsamples_this_frame = d_samples_per_frame; - // fill in the complex sinusoid + size_t nshorts = 2 * nsamples_this_frame; // 16-bit I & Q + long channel = 0; + long n_bytes = nshorts*2; + pmt_t uvec = pmt_make_s16vector(nshorts, 0); + size_t ignore; + int16_t *samples = pmt_s16vector_writeable_elements(uvec, ignore); - for (int i = 0; i < nsamples_this_frame; i++){ + // fill in the complex sinusoid - if (1){ - gr_complex s; - d_nco.sincos(&s, 1, d_amplitude); - // write 16-bit i & q - samples[2*i] = (int16_t) s.real(); - samples[2*i+1] = (int16_t) s.imag(); - } - else { - gr_complex s(d_amplitude, d_amplitude); + for (int i = 0; i < nsamples_this_frame; i++){ - // write 16-bit i & q - samples[2*i] = (int16_t) s.real(); - samples[2*i+1] = (int16_t) s.imag(); - } + if (1){ + gr_complex s; + d_nco.sincos(&s, 1, d_amplitude); + // write 16-bit i & q + samples[2*i] = (int16_t) s.real(); + samples[2*i+1] = (int16_t) s.imag(); } - - if(d_disk_write) - d_ofile.write((const char *)samples, n_bytes); - - pmt_t v_pkt = pmt_make_u8vector(sizeof(transport_pkt), 0); - transport_pkt *pkt = - (transport_pkt *) pmt_u8vector_writeable_elements(v_pkt, ignore); + else { + gr_complex s(d_amplitude, d_amplitude); - pkt->set_header(0, channel, 0, n_bytes); - pkt->set_timestamp(0xffffffff); - memcpy(pkt->payload(), samples, n_bytes); - - d_cs->send(s_response_usrp_rx_read, pmt_list3(PMT_NIL, PMT_T, v_pkt)); - - // Now lets check the shared CS queue between the TX and RX stub. Each - // element in a queue is a list where the first element is an invocation - // handle and the second element is a PMT u8 vect representation of the - // CS packet response which can just be passed transparently. - while(!d_cs_queue.empty()) { - - pmt_t cs_pkt = d_cs_queue.front(); - d_cs_queue.pop(); - - pmt_t invocation_handle = pmt_nth(0, cs_pkt); - pmt_t v_pkt = pmt_nth(1, cs_pkt); - - d_cs->send(s_response_usrp_rx_read, - pmt_list3(invocation_handle, - PMT_T, - v_pkt)); // Take the front CS pkt - - - if(verbose) - std::cout << "[USRP_RX_STUB] Received CS response from TX stub\n"; + // write 16-bit i & q + samples[2*i] = (int16_t) s.real(); + samples[2*i+1] = (int16_t) s.imag(); } - } - usrp_rx_stop = false; + if(d_disk_write) + d_ofile.write((const char *)samples, n_bytes); + + pmt_t v_pkt = pmt_make_u8vector(sizeof(transport_pkt), 0); + transport_pkt *pkt = + (transport_pkt *) pmt_u8vector_writeable_elements(v_pkt, ignore); + + pkt->set_header(0, channel, 0, n_bytes); + pkt->set_timestamp(0xffffffff); + memcpy(pkt->payload(), samples, n_bytes); + + d_cs->send(s_response_usrp_rx_read, pmt_list3(PMT_NIL, PMT_T, v_pkt)); - if(verbose) - std::cout << "[USRP_RX_STUB] Got fake RX stop\n"; + // Now lets check the shared CS queue between the TX and RX stub. Each + // element in a queue is a list where the first element is an invocation + // handle and the second element is a PMT u8 vect representation of the + // CS packet response which can just be passed transparently. + while(!d_cs_queue.empty()) { + + pmt_t cs_pkt = d_cs_queue.front(); + d_cs_queue.pop(); + + pmt_t invocation_handle = pmt_nth(0, cs_pkt); + pmt_t v_pkt = pmt_nth(1, cs_pkt); + + d_cs->send(s_response_usrp_rx_read, + pmt_list3(invocation_handle, + PMT_T, + v_pkt)); // Take the front CS pkt + + + if(verbose) + std::cout << "[USRP_RX_STUB] Received CS response from TX stub\n"; + } } diff --git a/usrp/host/lib/inband/usrp_rx_stub.h b/usrp/host/lib/inband/usrp_rx_stub.h index 5a75bf00a..9cf308a99 100644 --- a/usrp/host/lib/inband/usrp_rx_stub.h +++ b/usrp/host/lib/inband/usrp_rx_stub.h @@ -31,9 +31,12 @@ typedef usrp_inband_usb_packet transport_pkt; -extern bool usrp_rx_stop; // used to communicate a 'stop' to the RX stub +extern bool usrp_rx_stop_stub; // used to communicate a 'stop' to the RX stub extern std::queue d_cs_queue; +static pmt_t s_timeout = pmt_intern("%timeout"); +static pmt_t s_done = pmt_intern("done"); + /*! * \brief Implements the low level usb interface to the USRP */ @@ -45,6 +48,10 @@ class usrp_rx_stub : public mb_mblock usrp_standard_rx* d_urx; long d_samples_per_frame; + long d_decim_rx; + + mb_time d_t0; + double d_delta_t; // for generating sine wave output ui_nco d_nco; @@ -61,8 +68,9 @@ class usrp_rx_stub : public mb_mblock void handle_message(mb_message_sptr msg); private: - void read_and_respond(pmt_t data); + void read_and_respond(); void read_data(); + void start_packet_timer(); }; diff --git a/usrp/host/lib/inband/usrp_server.cc b/usrp/host/lib/inband/usrp_server.cc index 1948a43b2..6a3643e56 100644 --- a/usrp/host/lib/inband/usrp_server.cc +++ b/usrp/host/lib/inband/usrp_server.cc @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -53,13 +55,42 @@ str(long x) usrp_server::usrp_server(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg) : mb_mblock(rt, instance_name, user_arg), + d_fpga_debug(false), + d_interp_tx(128), // these should match the lower level defaults (rx also) + d_decim_rx(128), d_fake_rx(false) { if(verbose) std::cout << "[USRP_SERVER] Initializing...\n"; // Dictionary for arguments to all of the components - pmt_t usrp_dict = user_arg; + d_usrp_dict = user_arg; + + if (pmt_is_dict(d_usrp_dict)) { + + if(pmt_t fpga_debug = pmt_dict_ref(d_usrp_dict, + pmt_intern("fpga-debug"), + PMT_NIL)) { + if(pmt_eqv(fpga_debug, PMT_T)) + d_fpga_debug=true; + } + + // Read the TX interpolations + if(pmt_t interp_tx = pmt_dict_ref(d_usrp_dict, + pmt_intern("interp-tx"), + PMT_NIL)) { + if(!pmt_eqv(interp_tx, PMT_NIL)) + d_interp_tx = pmt_to_long(interp_tx); + } + + // Read the RX decimation rate + if(pmt_t decim_rx = pmt_dict_ref(d_usrp_dict, + pmt_intern("decim-rx"), + PMT_NIL)) { + if(!pmt_eqv(decim_rx, PMT_NIL)) + d_decim_rx = pmt_to_long(decim_rx); + } + } // control & status port d_cs = define_port("cs", "usrp-server-cs", true, mb_port::EXTERNAL); @@ -82,7 +113,7 @@ usrp_server::usrp_server(mb_runtime *rt, const std::string &instance_name, pmt_t mb_port::EXTERNAL)); } - define_component("usrp", "usrp_usb_interface", usrp_dict); + define_component("usrp", "usrp_usb_interface", d_usrp_dict); connect("self", "cs_usrp", "usrp", "cs"); d_defer=false; @@ -108,6 +139,10 @@ usrp_server::usrp_server(mb_runtime *rt, const std::string &instance_name, pmt_t //d_fake_rx=true; } +/*! + * \brief resets the assigned capacity and owners of each RX and TX channel from + * allocations. + */ void usrp_server::reset_channels() { @@ -136,6 +171,11 @@ usrp_server::initial_transition() // the initial transition } +/*! + * \brief Reads all incoming messages to USRP server from the TX, RX, and the CS + * ports. This drives the state of USRP server and dispatches based on the + * message. + */ void usrp_server::handle_message(mb_message_sptr msg) { @@ -178,6 +218,9 @@ usrp_server::handle_message(mb_message_sptr msg) pmt_t status = pmt_nth(1, data); d_cs->send(s_response_open, pmt_list2(invocation_handle, status)); + //reset_all_registers(); + //initialize_registers(); + if(pmt_eqv(status,PMT_T)) { d_opened = true; d_defer = false; @@ -209,7 +252,7 @@ usrp_server::handle_message(mb_message_sptr msg) // Do not report back responses if they were generated from a // command packet - if(channel == 0x1f) + if(channel == CONTROL_CHAN) return; // Find the port through the owner of the channel @@ -470,7 +513,14 @@ usrp_server::handle_message(mb_message_sptr msg) std::cout << "[USRP_SERVER] unhandled msg: " << msg << std::endl; } -// Return -1 if it is not an RX port, or an index +/*! + * \brief Takes a port_symbol() as parameter \p port_id and is used to determine + * if the port is a TX port, or to find an index in the d_tx vector which stores + * the port. + * + * \returns -1 if \p port_id is not in the d_tx vector (i.e., it's not a TX + * port), otherwise returns an index in the d_tx vector which stores the port. + */ int usrp_server::tx_port_index(pmt_t port_id) { for(int i=0; i < (int) d_tx.size(); i++) @@ -480,7 +530,14 @@ int usrp_server::tx_port_index(pmt_t port_id) { return -1; } -// Return -1 if it is not an RX port, or an index +/*! + * \brief Takes a port_symbol() as parameter \p port_id and is used to determine + * if the port is an RX port, or to find an index in the d_rx vector which + * stores the port. + * + * \returns -1 if \p port_id is not in the d_rx vector (i.e., it's not an RX + * port), otherwise returns an index in the d_rx vector which stores the port. + */ int usrp_server::rx_port_index(pmt_t port_id) { for(int i=0; i < (int) d_rx.size(); i++) @@ -490,8 +547,12 @@ int usrp_server::rx_port_index(pmt_t port_id) { return -1; } -// Go through all TX and RX channels, sum up the assigned capacity -// and return it +/*! + * \brief Determines the current total capacity allocated by all RX and TX + * channels. + * + * \returns the total allocated capacity + */ long usrp_server::current_capacity_allocation() { long capacity = 0; @@ -504,6 +565,14 @@ long usrp_server::current_capacity_allocation() { return capacity; } + +/*! + * \brief Called by the handle_message() method if the incoming message to + * usrp_server is to allocate a channel (cmd-allocate-channel). The method + * checks if the requested capacity exists and if so it will reserve it for the + * caller on the channel that is returned via a response-allocate-channel + * signal. + */ void usrp_server::handle_cmd_allocate_channel( mb_port_sptr port, @@ -564,9 +633,13 @@ usrp_server::handle_cmd_allocate_channel( return; } -// Check the port type and deallocate assigned capacity based on this, ensuring -// that the owner of the method invocation is the owner of the port and that the -// channel number is valid. +/*! + * \brief Called by the handle_message() method if the incoming message to + * usrp_server is to deallocate a channel (cmd-deallocate-channel). The method + * ensures that the sender of the signal owns the channel and that the channel + * number is valid. A response-deallocate-channel signal is sent back with the + * result of the deallocation. + */ void usrp_server::handle_cmd_deallocate_channel( mb_port_sptr port, @@ -591,8 +664,26 @@ usrp_server::handle_cmd_deallocate_channel( return; } -void usrp_server::handle_cmd_xmit_raw_frame(mb_port_sptr port, std::vector &chan_info, pmt_t data) { - +/*! + * \brief Called by the handle_message() method if the incoming message to + * usrp_server is to transmit a frame (cmd-xmit-raw-frame). The method + * allocates enough memory to support a burst of packets which contain the frame + * over the bus of the frame, sets the packet headers, and sends a signal to the + * lower block for the data (packets) to be written to the bus. + * + * The \p port the command was sent on and the channel info (\p chan_info) of + * the channel the frame is to be transmitted on are passed to ensure that the + * caller owns the channel. + * + * The \p data parameter is in the format of a cmd-xmit-raw-frame signal. + * + * The properties + */ +void usrp_server::handle_cmd_xmit_raw_frame( + mb_port_sptr port, + std::vector &chan_info, + pmt_t data) +{ size_t n_bytes, psize; long max_payload_len = transport_pkt::max_payload(); @@ -667,7 +758,8 @@ void usrp_server::handle_cmd_xmit_raw_frame(mb_port_sptr port, std::vectorsend(s_cmd_usrp_write, pmt_list3(invocation_handle, pmt_from_long(channel), @@ -676,7 +768,29 @@ void usrp_server::handle_cmd_xmit_raw_frame(mb_port_sptr port, std::vector &chan_info, pmt_t data) +/*! + * \brief Called by the handle_message() method to parse incoming control/status + * signals (cmd-to-control-channel). + * + * The \p port the command was sent on and the channel info (\p chan_info) of + * the channel are passed to ensure that the caller owns the channel. + * + * The \p data parameter is in the format of a PMT list, where each element + * follows the format of a control/status signal (i.e. op-ping-fixed). + * + * The method will parse all of the C/S commands included in \p data and place + * the commands in to a lower level packet sent to the control channel. The + * method will pack as many commands as possible in t oa single packet, and once + * it is fill generate as many lower level packets as needed. + * + * Anything that needs to be returned to the sender of the signal (i.e. the + * value of a register) will be generated by the parse_control_pkt() method as + * the responses to the commands are read back from the USRP. + */ +void usrp_server::handle_cmd_to_control_channel( + mb_port_sptr port, + std::vector &chan_info, + pmt_t data) { pmt_t invocation_handle = pmt_nth(0, data); @@ -687,7 +801,10 @@ void usrp_server::handle_cmd_to_control_channel(mb_port_sptr port, std::vector &chan_info, pmt_t data) +usrp_server::handle_cmd_start_recv_raw_samples( + mb_port_sptr port, + std::vector &chan_info, + pmt_t data) { pmt_t invocation_handle = pmt_nth(0, data); long channel = pmt_to_long(pmt_nth(1, data)); @@ -1032,6 +1163,18 @@ usrp_server::handle_cmd_start_recv_raw_samples(mb_port_sptr port, std::vectorsend(s_response_recv_raw_samples, - pmt_list5(invocation_handle, + pmt_list6(invocation_handle, status, v_samples, pmt_from_long(pkt->timestamp()), + pmt_from_long(channel), properties)); return; } +/*! + * \brief Called by handle_response_usrp_read() when the incoming packet has a + * channel of CONTROL_CHAN. This means that the incoming packet contains a + * response for a command sent to the control channel, which this method will + * parse. + * + * The \p pkt parameter is a pointer to the full packet (transport_pkt) in + * memory. + * + * Given that all commands sent to the control channel that require responses + * will carry an RID (request ID), the method will use the RID passed back with + * the response to determine which port the response should be sent on. + */ void usrp_server::parse_control_pkt(pmt_t invocation_handle, transport_pkt *pkt) { @@ -1190,6 +1362,9 @@ usrp_server::parse_control_pkt(pmt_t invocation_handle, transport_pkt *pkt) return; pmt_t owner = d_rids[srid].owner; + + // Return the RID + d_rids[srid].owner = PMT_NIL; // FIXME: should be 1 response for all subpackets here ? if((port = tx_port_index(owner)) != -1) @@ -1225,6 +1400,9 @@ usrp_server::parse_control_pkt(pmt_t invocation_handle, transport_pkt *pkt) return; pmt_t owner = d_rids[srid].owner; + + // Return the RID + d_rids[srid].owner = PMT_NIL; // FIXME: should be 1 response for all subpackets here ? if((port = tx_port_index(owner)) != -1) @@ -1261,6 +1439,9 @@ usrp_server::parse_control_pkt(pmt_t invocation_handle, transport_pkt *pkt) return; pmt_t owner = d_rids[srid].owner; + + // Return the RID + d_rids[srid].owner = PMT_NIL; if((port = tx_port_index(owner)) != -1) d_tx[port]->send(s_response_from_control_channel, @@ -1294,6 +1475,9 @@ usrp_server::parse_control_pkt(pmt_t invocation_handle, transport_pkt *pkt) return; pmt_t owner = d_rids[srid].owner; + + // Return the RID + d_rids[srid].owner = PMT_NIL; if((port = tx_port_index(owner)) != -1) d_tx[port]->send(s_response_from_control_channel, @@ -1317,6 +1501,10 @@ usrp_server::parse_control_pkt(pmt_t invocation_handle, transport_pkt *pkt) } } +/*! + * \brief Used to recall all incoming signals that were deferred when USRP + * server was in the initialization state. + */ void usrp_server::recall_defer_queue() { @@ -1335,6 +1523,25 @@ usrp_server::recall_defer_queue() return; } +/*! + * \brief Commonly called by any method which handles outgoing frames or control + * packets to the USRP to check if the port on which the signal was sent owns + * the channel the outgoing packet will be associated with. This helps ensure + * that applications do not send data on other application's ports. + * + * The \p port parameter is the port symbol that the caller wishes to determine + * owns the channel specified by \p chan_info. + * + * The \p signal_info parameter is a PMT list containing two elements: the + * response signal to use if the permissions are invalid, and the invocation + * handle that was passed. This allows the method to generate detailed failure + * responses to signals without having to return some sort of structured + * information which the caller must then parse and interpret to determine the + * failure type. + * + * \returns true if \p port owns the channel specified by \p chan_info, false + * otherwise. + */ bool usrp_server::check_valid(mb_port_sptr port, long channel, @@ -1346,7 +1553,7 @@ usrp_server::check_valid(mb_port_sptr port, pmt_t invocation_handle = pmt_nth(1, signal_info); // not a valid channel number? - if(channel >= (long)chan_info.size() && channel != 0x1f) { + if(channel >= (long)chan_info.size() && channel != CONTROL_CHAN) { port->send(response_signal, pmt_list2(invocation_handle, s_err_channel_invalid)); @@ -1377,8 +1584,12 @@ usrp_server::check_valid(mb_port_sptr port, return true; } -// Goes through the vector of RIDs and retreieves an -// available one for use +/*! + * \brief Finds the next available RID for internal USRP server use with control + * and status packets. + * + * \returns the next valid RID or -1 if no more RIDs are available. + */ long usrp_server::next_rid() { @@ -1386,7 +1597,264 @@ usrp_server::next_rid() if(pmt_eqv(d_rids[i].owner, PMT_NIL)) return i; + if(verbose) + std::cout << "[USRP_SERVER] No RIDs left\n"; return -1; } +/*! + * \brief Called by handle_message() when USRP server gets a response that the + * USRP was opened successfully to initialize the registers using the new + * register read/write control packets. + */ +void +usrp_server::initialize_registers() +{ + // We use handle_cmd_to_control_channel() to create the register writes using + // PMT_NIL as the response port to tell usrp_server not to pass the response + // up to any application. + if(verbose) + std::cout << "[USRP_SERVER] Initializing registers...\n"; + + // RX mode to normal (0) + set_register(FR_MODE, 0); + + // FPGA debugging? + if(d_fpga_debug) { + set_register(FR_DEBUG_EN, 1); + // FIXME: need to figure out exact register writes to control daughterboard + // pins that need to be written to + } else { + set_register(FR_DEBUG_EN, 0); + } + + // Set the transmit sample rate divisor, which is 4-1 + set_register(FR_TX_SAMPLE_RATE_DIV, 3); + + // Dboard IO buffer and register settings + set_register(FR_OE_0, (0xffff << 16) | 0x0000); + set_register(FR_IO_0, (0xffff << 16) | 0x0000); + set_register(FR_OE_1, (0xffff << 16) | 0x0000); + set_register(FR_IO_1, (0xffff << 16) | 0x0000); + set_register(FR_OE_2, (0xffff << 16) | 0x0000); + set_register(FR_IO_2, (0xffff << 16) | 0x0000); + set_register(FR_OE_3, (0xffff << 16) | 0x0000); + set_register(FR_IO_3, (0xffff << 16) | 0x0000); + + // zero Tx side Auto Transmit/Receive regs + set_register(FR_ATR_MASK_0, 0); + set_register(FR_ATR_TXVAL_0, 0); + set_register(FR_ATR_RXVAL_0, 0); + set_register(FR_ATR_MASK_1, 0); + set_register(FR_ATR_TXVAL_1, 0); + set_register(FR_ATR_RXVAL_1, 0); + set_register(FR_ATR_MASK_2, 0); + set_register(FR_ATR_TXVAL_2, 0); + set_register(FR_ATR_RXVAL_2, 0); + set_register(FR_ATR_MASK_3, 0); + set_register(FR_ATR_TXVAL_3, 0); + set_register(FR_ATR_RXVAL_3, 0); + + // Configure TX mux, this is a hacked value + set_register(FR_TX_MUX, 0x00000081); + + // Set the interpolation rate, which is the rate divided by 4, minus 1 + set_register(FR_INTERP_RATE, (d_interp_tx/4)-1); + + // Apparently this register changes again + set_register(FR_TX_MUX, 0x00000981); + + // Set the receive sample rate divisor, which is 2-1 + set_register(FR_RX_SAMPLE_RATE_DIV, 1); + + // DC offset + set_register(FR_DC_OFFSET_CL_EN, 0x0000000f); + + // Reset the DC correction offsets + set_register(FR_ADC_OFFSET_0, 0); + set_register(FR_ADC_OFFSET_1, 0); + + // Some hard-coded RX configuration + set_register(FR_RX_FORMAT, 0x00000300); + set_register(FR_RX_MUX, 1); + + // RX decimation rate is divided by two, then subtract 1 + set_register(FR_DECIM_RATE, (d_decim_rx/2)-1); + + // More hard coding + set_register(FR_RX_MUX, 0x000e4e41); + + // Resetting RX registers + set_register(FR_RX_PHASE_0, 0); + set_register(FR_RX_PHASE_1, 0); + set_register(FR_RX_PHASE_2, 0); + set_register(FR_RX_PHASE_3, 0); + set_register(FR_RX_FREQ_0, 0x28000000); + set_register(FR_RX_FREQ_1, 0); + set_register(FR_RX_FREQ_2, 0); + set_register(FR_RX_FREQ_3, 0); + + // Enable debug bus + set_register(FR_DEBUG_EN, 0xf); + set_register(FR_OE_0, -1); + set_register(FR_OE_1, -1); + set_register(FR_OE_2, -1); + set_register(FR_OE_3, -1); + + // DEBUGGING + //check_register_initialization(); +} + +// FIXME: used for debugging to determine if all the registers are actually +// being set correctly +void +usrp_server::check_register_initialization() +{ + // RX mode to normal (0) + read_register(FR_MODE); + + // FPGA debugging? + if(d_fpga_debug) { + read_register(FR_DEBUG_EN); + // FIXME: need to figure out exact register writes to control daughterboard + // pins that need to be written to + } else { + read_register(FR_DEBUG_EN); + } + + // Set the transmit sample rate divisor, which is 4-1 + read_register(FR_TX_SAMPLE_RATE_DIV); + + // Dboard IO buffer and register settings + read_register(FR_OE_0); + read_register(FR_IO_0); + read_register(FR_OE_1); + read_register(FR_IO_1); + read_register(FR_OE_2); + read_register(FR_IO_2); + read_register(FR_OE_3); + read_register(FR_IO_3); + + // zero Tx side Auto Transmit/Receive regs + read_register(FR_ATR_MASK_0); + read_register(FR_ATR_TXVAL_0); + read_register(FR_ATR_RXVAL_0); + read_register(FR_ATR_MASK_1); + read_register(FR_ATR_TXVAL_1); + read_register(FR_ATR_RXVAL_1); + read_register(FR_ATR_MASK_2); + read_register(FR_ATR_TXVAL_2); + read_register(FR_ATR_RXVAL_2); + read_register(FR_ATR_MASK_3); + read_register(FR_ATR_TXVAL_3); + read_register(FR_ATR_RXVAL_3); + + // Configure TX mux, this is a hacked value + read_register(FR_TX_MUX); + + // Set the interpolation rate, which is the rate divided by 4, minus 1 + read_register(FR_INTERP_RATE); + + // Apparently this register changes again + read_register(FR_TX_MUX); + + // Set the receive sample rate divisor, which is 2-1 + read_register(FR_RX_SAMPLE_RATE_DIV); + + // DC offset + read_register(FR_DC_OFFSET_CL_EN); + + // Reset the DC correction offsets + read_register(FR_ADC_OFFSET_0); + read_register(FR_ADC_OFFSET_1); + + // Some hard-coded RX configuration + read_register(FR_RX_FORMAT); + read_register(FR_RX_MUX); + + // RX decimation rate is divided by two, then subtract 1 + read_register(FR_DECIM_RATE); + + // More hard coding + read_register(FR_RX_MUX); + + // Resetting RX registers + read_register(FR_RX_PHASE_0); + read_register(FR_RX_PHASE_1); + read_register(FR_RX_PHASE_2); + read_register(FR_RX_PHASE_3); + read_register(FR_RX_FREQ_0); + read_register(FR_RX_FREQ_1); + read_register(FR_RX_FREQ_2); + read_register(FR_RX_FREQ_3); +} + +/*! + * \brief Used to generate FPGA register write commands to reset all of the FPGA + * registers to a value of 0. + */ +void +usrp_server::reset_all_registers() +{ + for(int i=0; i<64; i++) + set_register(i, 0); +} + +/*! + * \brief Used internally by USRP server to generate a control/status packet + * which contains a register write. + * + * The \p reg parameter is the register number that the value \p val will be + * written to. + */ +void +usrp_server::set_register(long reg, long val) +{ + size_t psize; + long payload_len = 0; + + pmt_t v_packet = pmt_make_u8vector(sizeof(transport_pkt), 0); + transport_pkt *pkt = (transport_pkt *) pmt_u8vector_writeable_elements(v_packet, psize); + + pkt->set_header(0, CONTROL_CHAN, 0, payload_len); + pkt->set_timestamp(0xffffffff); + + pkt->cs_write_reg(reg, val); + + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(PMT_NIL, + pmt_from_long(CONTROL_CHAN), + v_packet)); +} + +/*! + * \brief Used internally by USRP server to generate a control/status packet + * which contains a register read. This is important to use internally so that + * USRP server can bypass the use of RIDs with register reads, as they are not + * needed and it would use up the finite number of RIDs available for use for + * applications to receive responses. + * + * The \p reg parameter is the register number that the value should be read + * from. + */ +void +usrp_server::read_register(long reg) +{ + size_t psize; + long payload_len = 0; + + pmt_t v_packet = pmt_make_u8vector(sizeof(transport_pkt), 0); + transport_pkt *pkt = (transport_pkt *) pmt_u8vector_writeable_elements(v_packet, psize); + + pkt->set_header(0, CONTROL_CHAN, 0, payload_len); + pkt->set_timestamp(0xffffffff); + + pkt->cs_read_reg(0, reg); + + d_cs_usrp->send(s_cmd_usrp_write, + pmt_list3(PMT_NIL, + pmt_from_long(CONTROL_CHAN), + v_packet)); +} + REGISTER_MBLOCK_CLASS(usrp_server); diff --git a/usrp/host/lib/inband/usrp_server.h b/usrp/host/lib/inband/usrp_server.h index 81dceb1f4..09c82faac 100644 --- a/usrp/host/lib/inband/usrp_server.h +++ b/usrp/host/lib/inband/usrp_server.h @@ -51,6 +51,13 @@ public: long d_ntx_chan; long d_nrx_chan; + pmt_t d_usrp_dict; + + bool d_fpga_debug; + + long d_interp_tx; + long d_decim_rx; + // Keep track of the request IDs struct rid_info { pmt_t owner; @@ -114,6 +121,11 @@ private: bool check_valid(mb_port_sptr port, long channel, std::vector &chan_info, pmt_t signal_info); void parse_control_pkt(pmt_t invocation_handle, transport_pkt *pkt); long next_rid(); + void initialize_registers(); + void set_register(long reg, long val); + void read_register(long reg); + void check_register_initialization(); + void reset_all_registers(); }; #endif /* INCLUDED_USRP_SERVER_H */ diff --git a/usrp/host/lib/inband/usrp_server.mbh b/usrp/host/lib/inband/usrp_server.mbh index 3fd0db139..ed7943fc3 100644 --- a/usrp/host/lib/inband/usrp_server.mbh +++ b/usrp/host/lib/inband/usrp_server.mbh @@ -202,7 +202,7 @@ (:incoming - (response-recv-raw-samples invocation-handle status samples timestamp properties) + (response-recv-raw-samples invocation-handle status samples timestamp channel properties) ;; samples is a uniform numeric vector. The contents of the sample ;; vector is treated as opaque and is passed from the FPGA diff --git a/usrp/host/lib/inband/usrp_tx.cc b/usrp/host/lib/inband/usrp_tx.cc index a7a5e4a89..da8777513 100644 --- a/usrp/host/lib/inband/usrp_tx.cc +++ b/usrp/host/lib/inband/usrp_tx.cc @@ -66,6 +66,10 @@ usrp_tx::initial_transition() } +/*! + * \brief Handles incoming signals to to the m-block, wihch should only ever be + * a single message: cmd-usrp-tx-write. + */ void usrp_tx::handle_message(mb_message_sptr msg) { @@ -82,6 +86,14 @@ usrp_tx::handle_message(mb_message_sptr msg) } } +/*! + * \brief Performs the actual writing of data to the USB bus, called by + * handle_message() when a cmd-usrp-tx-write signal is received. + * + * The \p data parameter is a PMT list which contains three mandatory elements, + * in the following order: an invocation handle, a channel, and a uniform vector + * of memory which contains the packets to be written to the bus. + */ void usrp_tx::write(pmt_t data) { @@ -121,7 +133,7 @@ usrp_tx::write(pmt_t data) for(int i=0; i < n_packets; i++) { if(d_disk_write) { - if(pkts[i].chan() == 0x1f) + if(pkts[i].chan() == CONTROL_CHAN) d_cs_ofile.write((const char *)&pkts[i], transport_pkt::max_pkt_size()); else d_ofile.write((const char *)&pkts[i], transport_pkt::max_pkt_size()); diff --git a/usrp/host/lib/inband/usrp_tx_stub.cc b/usrp/host/lib/inband/usrp_tx_stub.cc index 7a9876322..6cff3b4ee 100644 --- a/usrp/host/lib/inband/usrp_tx_stub.cc +++ b/usrp/host/lib/inband/usrp_tx_stub.cc @@ -103,7 +103,7 @@ usrp_tx_stub::write(pmt_t data) for(long i=0; iset_header(0, 0x1f, 0, 0); + q_pkt->set_header(0, CONTROL_CHAN, 0, 0); q_pkt->set_timestamp(0xffffffff); // We dispatch based on the control packet type, however we can extract the diff --git a/usrp/host/lib/inband/usrp_usb_interface.cc b/usrp/host/lib/inband/usrp_usb_interface.cc index 269ed2706..51b6d4646 100644 --- a/usrp/host/lib/inband/usrp_usb_interface.cc +++ b/usrp/host/lib/inband/usrp_usb_interface.cc @@ -47,12 +47,16 @@ static pmt_t s_shutdown = pmt_intern("%shutdown"); static const bool verbose = false; - -// need to take number of TX and RX channels as parameter +/*! + * \brief Initializes the USB interface m-block. + * + * The \p user_arg should be a PMT dictionary which can contain optional + * arguments for the block, such as the decimatoin and interpolation rate. + */ usrp_usb_interface::usrp_usb_interface(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg) : mb_mblock(rt, instance_name, user_arg), - d_fpga_debug(false), d_fake_usrp(false), + d_rx_reading(false), d_interp_tx(128), d_decim_rx(128), d_rf_freq(10e6), @@ -86,7 +90,7 @@ usrp_usb_interface::usrp_usb_interface(mb_runtime *rt, const std::string &instan d_interp_tx = pmt_to_long(interp_tx); } - // Read the RX interpolations + // Read the RX decimation rate if(pmt_t decim_rx = pmt_dict_ref(usrp_dict, pmt_intern("decim-rx"), PMT_NIL)) { @@ -134,8 +138,8 @@ usrp_usb_interface::usrp_usb_interface(mb_runtime *rt, const std::string &instan d_tx_cs = define_port("tx_cs", "usrp-tx-cs", false, mb_port::INTERNAL); // Connect to TX and RX - define_component("tx", tx_interface, PMT_F); - define_component("rx", rx_interface, PMT_F); + define_component("tx", tx_interface, usrp_dict); + define_component("rx", rx_interface, usrp_dict); connect("self", "rx_cs", "rx", "cs"); connect("self", "tx_cs", "tx", "cs"); @@ -146,8 +150,6 @@ usrp_usb_interface::usrp_usb_interface(mb_runtime *rt, const std::string &instan d_utx = NULL; d_urx = NULL; - d_fpga_debug=true; // WARNING: DO NOT ENABLE WITH D'BOARDS OTHER THAN BASIC TX/RX - } usrp_usb_interface::~usrp_usb_interface() @@ -161,6 +163,10 @@ usrp_usb_interface::initial_transition() } +/*! + * \brief Handles all incoming signals to the block from the lowest m-blocks + * which read/write to the bus, or the higher m-block which is the USRP server. + */ void usrp_usb_interface::handle_message(mb_message_sptr msg) { @@ -256,6 +262,13 @@ usrp_usb_interface::handle_message(mb_message_sptr msg) std::cout << "[USRP_USB_INTERFACE] unhandled msg: " << msg << std::endl; } +/*! + * \brief Called by the handle_message() method when the incoming signal is to + * open a USB connection to the USRP (cmd-usrp-open). + * + * The \p data parameter is a PMT list, where the elements are an invocation + * handle and the USRP number. + */ void usrp_usb_interface::handle_cmd_open(pmt_t data) { @@ -290,7 +303,7 @@ usrp_usb_interface::handle_cmd_open(pmt_t data) return; } - if(!d_utx->set_tx_freq (0,d_rf_freq)) { // try setting center freq to 0 + if(!d_utx->set_tx_freq (0,d_rf_freq) || !d_utx->set_tx_freq(1,d_rf_freq)) { // try setting center freq to 0 if (verbose) std::cout << "[USRP_USB_INTERFACE] Failed to set center frequency on TX\n"; reply_data = pmt_list2(invocation_handle, PMT_F); @@ -298,6 +311,14 @@ usrp_usb_interface::handle_cmd_open(pmt_t data) return; } + if(!d_utx->set_mux(0xBA98)) { + if (verbose) + std::cout << "[USRP_USB_INTERFACE] Failed to set TX mux\n"; + reply_data = pmt_list2(invocation_handle, PMT_F); + d_cs->send(s_response_usrp_open, reply_data); + return; + } + d_utx->start(); if (verbose) @@ -321,33 +342,44 @@ usrp_usb_interface::handle_cmd_open(pmt_t data) return; } - if(!d_urx->set_rx_freq (0, d_rf_freq)) { + if(!d_urx->set_rx_freq (0, -d_rf_freq) || !d_urx->set_rx_freq(1, -d_rf_freq)) { if (verbose) std::cout << "[usrp_server] Failed to set center frequency on RX\n"; reply_data = pmt_list2(invocation_handle, PMT_F); d_cs->send(s_response_usrp_open, reply_data); return; } - - if(d_fpga_debug) { - d_utx->_write_fpga_reg(FR_DEBUG_EN,0xf); - d_utx->_write_oe(0, 0xffff, 0xffff); - d_urx->_write_oe(0, 0xffff, 0xffff); - d_utx->_write_oe(1, 0xffff, 0xffff); - d_urx->_write_oe(1, 0xffff, 0xffff); - -// while(1){ -// for(int i=0; i<0xffff; i++) -// d_urx->write_io(0, i, 0xffff); -// } - } + // Two channels ... this really needs to end up being set correctly by + // querying for what dboards are connected + if(!d_urx->set_mux(0x32103210)) { + if (verbose) + std::cout << "[USRP_USB_INTERFACE] Failed to set RX mux\n"; + reply_data = pmt_list2(invocation_handle, PMT_F); + d_cs->send(s_response_usrp_open, reply_data); + return; + } + if (verbose) std::cout << "[USRP_USB_INTERFACE] Setup RX channel\n"; + +// d_utx->_write_fpga_reg(FR_DEBUG_EN,0xf); +// d_utx->_write_oe(0, 0xffff, 0xffff); +// d_urx->_write_oe(0, 0xffff, 0xffff); +// d_utx->_write_oe(1, 0xffff, 0xffff); +// d_urx->_write_oe(1, 0xffff, 0xffff); d_cs->send(s_response_usrp_open, pmt_list2(invocation_handle, PMT_T)); } +/*! + * \brief Called by the handle_message() method when the incoming signal is to + * write data to the USB bus (cmd-usrp-write). + * + * The \p data parameter is a PMT list containing 3 mandatory elements in the + * following order: an invocation handle, channel, and a uniform vector + * representation of the packets. + */ void usrp_usb_interface::handle_cmd_write(pmt_t data) { @@ -366,6 +398,13 @@ usrp_usb_interface::handle_cmd_write(pmt_t data) return; } +/*! + * \brief Called by the handle_message() method when the incoming signal is to + * start reading data from the USB bus (cmd-usrp-start-reading). + * + * The \p data parameter is a PMT list with a single element: an invocation + * handle which can be returned with the response. + */ void usrp_usb_interface::handle_cmd_start_reading(pmt_t data) { @@ -381,9 +420,18 @@ usrp_usb_interface::handle_cmd_start_reading(pmt_t data) d_rx_cs->send(s_cmd_usrp_rx_start_reading, pmt_list2(PMT_NIL, rx_handle)); + d_rx_reading = true; + return; } +/*! + * \brief Called by the handle_message() method when the incoming signal is to + * stop reading data from the USB bus (cmd-usrp-stop-reading). + * + * The \p data parameter is a PMT list with a single element: an invocation + * handle which can be returned with the response. + */ void usrp_usb_interface::handle_cmd_stop_reading(pmt_t data) { @@ -392,22 +440,40 @@ usrp_usb_interface::handle_cmd_stop_reading(pmt_t data) if(!d_fake_usrp) { if(verbose) std::cout << "[USRP_USB_INTERFACE] Stopping RX...\n"; + usrp_rx_stop = true; + + // Used to allow a read() being called by a lower layer to complete before + // stopping, else there can be partial data left on the bus and can generate + // errors. + while(usrp_rx_stop) {usleep(1);} d_urx->stop(); } else { if(verbose) std::cout << "[USRP_USB_INTERFACE] Stopping fake RX...\n"; - usrp_rx_stop = true; // extern to communicate with stub to wait + usrp_rx_stop_stub = true; // extern to communicate with stub to wait } + d_rx_reading = false; + return; } +/*! + * \brief Called by the handle_message() method when the incoming signal is to + * close the USB connection to the USRP. + * + * The \p data parameter is a PMT list with a single element: an invocation + * handle which can be returned with the response. + */ void usrp_usb_interface::handle_cmd_close(pmt_t data) { pmt_t invocation_handle = pmt_nth(0, data); + if(d_rx_reading) + handle_cmd_stop_reading(PMT_NIL); + if(d_fake_usrp) { d_cs->send(s_response_usrp_close, pmt_list2(invocation_handle, PMT_T)); return; diff --git a/usrp/host/lib/inband/usrp_usb_interface.h b/usrp/host/lib/inband/usrp_usb_interface.h index 8efce2ff6..6c2c15768 100644 --- a/usrp/host/lib/inband/usrp_usb_interface.h +++ b/usrp/host/lib/inband/usrp_usb_interface.h @@ -42,10 +42,10 @@ class usrp_usb_interface : public mb_mblock long d_ntx_chan; long d_nrx_chan; - long d_fpga_debug; - bool d_fake_usrp; + bool d_rx_reading; + long d_interp_tx; long d_decim_rx; -- cgit