diff options
author | Martin Braun <martin.braun@ettus.com> | 2022-04-14 16:28:27 +0200 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2022-06-10 13:24:05 -0500 |
commit | 76156b15b7fa5636281a4139c2c856da0a7b05c5 (patch) | |
tree | 28a697cc97a5b9570e926e80facc0a513705e557 /host | |
parent | f2043bc60bfe841bbb73b5f5dd38813592c9536b (diff) | |
download | uhd-76156b15b7fa5636281a4139c2c856da0a7b05c5.tar.gz uhd-76156b15b7fa5636281a4139c2c856da0a7b05c5.tar.bz2 uhd-76156b15b7fa5636281a4139c2c856da0a7b05c5.zip |
examples: gpio: Refactor example
The example had organically grown and was getting hard to read, and also
had some known issues. Summary of fixes:
- Default GPIO bank and connector are now derived from the device. This
allows this example to pass without throwing an exception on E3xx and
X4xx series when using default arguments.
- The bitbang test is moved into its own code section, to make the rest
more readable.
- We move all the streamer-related code into a helper struct
- Some repetitive parts of the code are moved into their own functions
- The argument --require-loopback is added, which will fail tests if
GPIO pins are not correctly looped back externally
- --list-banks is renamed to --list_banks for consistency
Diffstat (limited to 'host')
-rw-r--r-- | host/examples/gpio.cpp | 654 |
1 files changed, 368 insertions, 286 deletions
diff --git a/host/examples/gpio.cpp b/host/examples/gpio.cpp index 417234881..730cf905f 100644 --- a/host/examples/gpio.cpp +++ b/host/examples/gpio.cpp @@ -8,12 +8,12 @@ // Example for GPIO testing and bit banging. // -// This example was originally designed to test the 11 bit wide front panel +// This example was originally designed to test the 12 bit wide front panel // GPIO on the X300 series and has since been adapted to work with any GPIO // bank on any USRP and provide optional bit banging. Please excuse the // clutter. Also, there is no current way to detect the width of the // specified GPIO bank, so the user must specify the width with the --bits -// flag if more than 11 bits. +// flag if more than 12 bits. // // GPIO Testing: // For testing, GPIO bits are set as follows: @@ -72,13 +72,13 @@ #include <uhd/usrp/multi_usrp.hpp> #include <uhd/utils/safe_main.hpp> #include <uhd/utils/thread.hpp> -#include <stdint.h> -#include <stdlib.h> #include <boost/format.hpp> #include <boost/program_options.hpp> #include <boost/tokenizer.hpp> #include <chrono> #include <csignal> +#include <cstdint> +#include <cstdlib> #include <iostream> #include <thread> @@ -87,11 +87,11 @@ static const std::string GPIO_DEFAULT_OTW_FORMAT = "sc16"; static const double GPIO_DEFAULT_RX_RATE = 500e3; static const double GPIO_DEFAULT_TX_RATE = 500e3; static const double GPIO_DEFAULT_DWELL_TIME = 2.0; -static const std::string GPIO_DEFAULT_GPIO = "FP0"; -static const size_t GPIO_DEFAULT_NUM_BITS = 11; +static const size_t GPIO_DEFAULT_NUM_BITS = 12; static const std::string GPIO_DEFAULT_CTRL = "0x0"; // all as user controlled static const std::string GPIO_DEFAULT_DDR = "0x0"; // all as inputs static const std::string GPIO_DEFAULT_OUT = "0x0"; +constexpr size_t GPIO_MIN_NUM_BITS = 5; static inline uint32_t GPIO_BIT(const size_t x) { @@ -131,7 +131,7 @@ void output_reg_values(const std::string& bank, for (const auto& attr : attrs) { const uint32_t gpio_bits = uint32_t(usrp->get_gpio_attr(bank, attr)); std::cout << (boost::format("%10s:%s") % attr - % to_bit_string(gpio_bits, num_bits)) + % to_bit_string(gpio_bits, num_bits)) << std::endl; } @@ -150,15 +150,207 @@ void output_reg_values(const std::string& bank, } } + +bool check_rb_values(const uint32_t rb, + uint32_t expected, + const uint32_t num_bits, + const uint32_t loopback_num_bits) +{ + if (loopback_num_bits) { + const uint32_t lb_mask = (1 << loopback_num_bits) - 1; + expected |= ((expected & lb_mask) << GPIO_MIN_NUM_BITS); + } + if ((rb & expected) != expected) { + std::cout << "fail:" << std::endl; + for (size_t bit = 0; bit < num_bits; bit++) { + if ((expected & GPIO_BIT(bit)) && ((rb & GPIO_BIT(bit)) == 0)) { + std::cout << "Bit " << bit << " should be set, but is not. "; + if (loopback_num_bits && bit >= GPIO_MIN_NUM_BITS) { + std::cout << "Are GPIO pins correctly looped back?"; + } + std::cout << std::endl; + } + } + return false; + } + std::cout << "pass:" << std::endl; + return true; +} + + +void run_bitbang_test(uhd::usrp::multi_usrp::sptr usrp, + const std::string gpio_bank, + const std::string port, + const uint32_t ddr, + const uint32_t out, + const uint32_t mask, + const uint32_t num_bits, + const std::chrono::milliseconds dwell_time) +{ + // Set all pins to "GPIO", and DDR/OUT to whatever the user requested + usrp->set_gpio_attr(gpio_bank, "CTRL", 0, mask); + usrp->set_gpio_attr(gpio_bank, "DDR", ddr, mask); + usrp->set_gpio_attr(gpio_bank, "OUT", out, mask); + + // print out initial state of GPIO + std::cout << "\nConfigured GPIO values:" << std::endl; + output_reg_values(gpio_bank, port, usrp, num_bits); + std::cout << std::endl; + std::signal(SIGINT, &sig_int_handler); + + while (not stop_signal_called) { + // dwell and continuously read back GPIO values + auto stop_time = std::chrono::steady_clock::now() + dwell_time; + while (not stop_signal_called and std::chrono::steady_clock::now() < stop_time) { + std::cout << "\rREADBACK: " + << to_bit_string( + usrp->get_gpio_attr(gpio_bank, "READBACK"), num_bits); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + std::cout << std::endl; + } +} + + +struct stream_helper_type +{ + stream_helper_type(uhd::usrp::multi_usrp::sptr usrp, + double rx_rate, + double tx_rate, + const std::string& cpu, + const std::string& otw, + const std::chrono::milliseconds dwell_time) + : rx_args(cpu, otw), tx_args(cpu, otw), dwell_time(dwell_time) + { + rx_cmd.stream_now = true; + + if (usrp->get_rx_num_channels()) { + rx_stream = usrp->get_rx_stream(rx_args); + usrp->set_rx_rate(rx_rate); + } + if (usrp->get_tx_num_channels()) { + tx_stream = usrp->get_tx_stream(tx_args); + usrp->set_tx_rate(tx_rate); + } + + const size_t rx_spp = rx_stream ? rx_stream->get_max_num_samps() : 0; + const size_t tx_spp = tx_stream ? tx_stream->get_max_num_samps() : 0; + nsamps_per_buff = std::max(rx_spp, tx_spp); + + if (rx_stream) { + rx_buff.resize(nsamps_per_buff * uhd::convert::get_bytes_per_item(cpu)); + for (size_t ch = 0; ch < rx_stream->get_num_channels(); ch++) { + rx_buffs.push_back(&rx_buff.front()); // same buffer for each channel + } + } + if (tx_stream) { + tx_buff.resize(nsamps_per_buff * uhd::convert::get_bytes_per_item(cpu)); + for (size_t ch = 0; ch < tx_stream->get_num_channels(); ch++) + tx_buffs.push_back(&tx_buff.front()); // same buffer for each channel + } + + tx_md.has_time_spec = false; + tx_md.start_of_burst = true; + } + + void start_stream(bool tx, bool rx) + { + if (tx && rx) { + rx_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; + rx_stream->issue_stream_cmd(rx_cmd); + tx_md.start_of_burst = true; + tx_md.end_of_burst = false; + auto stop_time = std::chrono::steady_clock::now() + dwell_time; + while ( + not stop_signal_called and std::chrono::steady_clock::now() < stop_time) { + try { + tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); + tx_md.start_of_burst = false; + rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); + } catch (...) { + } + } + return; + } + + if (tx) { + auto stop_time = std::chrono::steady_clock::now() + dwell_time; + tx_md.start_of_burst = true; + tx_md.end_of_burst = false; + while ( + not stop_signal_called and std::chrono::steady_clock::now() < stop_time) { + try { + tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); + tx_md.start_of_burst = false; + } catch (...) { + } + } + } + + if (rx) { + rx_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; + rx_stream->issue_stream_cmd(rx_cmd); + auto stop_time = std::chrono::steady_clock::now() + dwell_time; + while ( + not stop_signal_called and std::chrono::steady_clock::now() < stop_time) { + try { + rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); + } catch (...) { + } + } + } + } + + void stop_stream(bool tx, bool rx) + { + if (tx) { + tx_md.end_of_burst = true; + try { + tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); + } catch (...) { + } + } + if (rx) { + rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + // clear out any data left in the rx stream + try { + rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); + } catch (...) { + } + } + } + + uhd::stream_args_t rx_args; + uhd::stream_args_t tx_args; + uhd::rx_streamer::sptr rx_stream; + uhd::tx_streamer::sptr tx_stream; + uhd::stream_cmd_t rx_cmd{uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS}; + + size_t nsamps_per_buff; + std::vector<char> rx_buff; + std::vector<char> tx_buff; + std::vector<void*> rx_buffs, tx_buffs; + + uhd::rx_metadata_t rx_md; + uhd::tx_metadata_t tx_md; + + double timeout = 0.01; + + const std::chrono::milliseconds dwell_time; +}; + + int UHD_SAFE_MAIN(int argc, char* argv[]) { // variables to be set by po std::string args; std::string cpu, otw; double rx_rate, tx_rate, dwell; - std::string gpio; + // This is the argument for set_gpio_attr(), not the connector name: + std::string gpio_bank; std::string port; size_t num_bits; + uint32_t loopback_num_bits = 0; std::string src_str; std::string ctrl_str; std::string ddr_str; @@ -175,16 +367,17 @@ int UHD_SAFE_MAIN(int argc, char* argv[]) ("tx_subdev_spec", po::value<std::string>(&tx_subdev_spec)->default_value(""), "A:0, B:0, or A:0 B:0") ("rx_subdev_spec", po::value<std::string>(&rx_subdev_spec)->default_value(""), "A:0, B:0, or A:0 B:0") ("repeat", "repeat loop until Ctrl-C is pressed") - ("list-banks", "print list of banks before running tests") + ("list_banks", "print list of banks before running tests") ("cpu", po::value<std::string>(&cpu)->default_value(GPIO_DEFAULT_CPU_FORMAT), "cpu data format") ("otw", po::value<std::string>(&otw)->default_value(GPIO_DEFAULT_OTW_FORMAT), "over the wire data format") ("rx_rate", po::value<double>(&rx_rate)->default_value(GPIO_DEFAULT_RX_RATE), "rx sample rate") ("tx_rate", po::value<double>(&tx_rate)->default_value(GPIO_DEFAULT_TX_RATE), "tx sample rate") ("dwell", po::value<double>(&dwell)->default_value(GPIO_DEFAULT_DWELL_TIME), "dwell time in seconds for each test case") - ("bank", po::value<std::string>(&gpio)->default_value(GPIO_DEFAULT_GPIO), "name of gpio bank") - ("port", po::value<std::string>(&port)->default_value(""), "name of gpio port. If not specified, defaults to the GPIO bank") + ("bank", po::value<std::string>(&gpio_bank)->default_value(""), "name of gpio bank (defaults to first bank in list)") + ("port", po::value<std::string>(&port)->default_value(""), "name of gpio port (source bank). If not specified, defaults to the first bank") ("bits", po::value<size_t>(&num_bits)->default_value(GPIO_DEFAULT_NUM_BITS), "number of bits in gpio bank") ("bitbang", "single test case where user sets values for CTRL, DDR, and OUT registers") + ("check_loopback", "check that lower half of pins is looped back onto upper half") ("src", po::value<std::string>(&src_str), "GPIO SRC reg value") ("ddr", po::value<std::string>(&ddr_str)->default_value(GPIO_DEFAULT_DDR), "GPIO DDR reg value") ("out", po::value<std::string>(&out_str)->default_value(GPIO_DEFAULT_OUT), "GPIO OUT reg value") @@ -194,48 +387,74 @@ int UHD_SAFE_MAIN(int argc, char* argv[]) po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); + /*** Sanity-check arguments **********************************************/ // print the help message if (vm.count("help")) { - std::cout << boost::format("gpio %s") % desc << std::endl; - return ~0; + std::cout << "gpio " << desc << std::endl; + return EXIT_SUCCESS; + } + if (vm.count("check_loopback")) { + // For a proper test, we need at least 5 pins (4xATR + 1xGPIO). That + // means we also want at *most* 5 pins to be looped back for this test. + if (num_bits <= GPIO_MIN_NUM_BITS) { + loopback_num_bits = 0; + } else { + loopback_num_bits = std::min(GPIO_MIN_NUM_BITS, num_bits - GPIO_MIN_NUM_BITS); + } + std::cout << "Checking external GPIO loopback! Expecting the following external " + "connections: " + << std::endl; + for (size_t gpio = 0; gpio + loopback_num_bits < num_bits; ++gpio) { + std::cout << "GPIO " << gpio << " --> " << gpio + loopback_num_bits + << std::endl; + } } + const auto dwell_time = std::chrono::milliseconds(static_cast<int64_t>(dwell * 1000)); + + /*** Set up USRP device and GPIO banks ************************************/ + std::cout << std::endl; + std::cout << "Creating the usrp device with: " << args << "..." << std::endl; + auto usrp = uhd::usrp::multi_usrp::make(args); + std::cout << "Using Device: " << usrp->get_pp_string() << std::endl; // Handle if the port is unspecified if (port.empty()) { - port = gpio; + port = usrp->get_gpio_src_banks(0).front(); } + if (gpio_bank.empty()) { + gpio_bank = usrp->get_gpio_banks(0).front(); + } + std::cout << "Using GPIO connector: " << port << std::endl; - // create a usrp device - std::cout << std::endl; - std::cout << boost::format("Creating the usrp device with: %s...") % args - << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; - - if (vm.count("list-banks")) { + if (vm.count("list_banks")) { std::cout << "Available GPIO banks: " << std::endl; auto banks = usrp->get_gpio_banks(0); for (auto& bank : banks) { std::cout << "* " << bank << std::endl; } } - std::cout << "Using GPIO bank: " << gpio << std::endl; - + std::cout << "Using GPIO bank: " << gpio_bank << std::endl; // subdev spec if (!tx_subdev_spec.empty()) usrp->set_tx_subdev_spec(tx_subdev_spec); if (!rx_subdev_spec.empty()) usrp->set_rx_subdev_spec(rx_subdev_spec); - std::cout << boost::format(" rx_subdev_spec: %s") - % usrp->get_rx_subdev_spec(0).to_string() + std::cout << " rx_subdev_spec: " << usrp->get_rx_subdev_spec(0).to_string() << std::endl; - std::cout << boost::format(" tx_subdev_spec: %s") - % usrp->get_tx_subdev_spec(0).to_string() + std::cout << " tx_subdev_spec: " << usrp->get_tx_subdev_spec(0).to_string() << std::endl; + // set GPIO driver source + if (vm.count("src")) { + std::vector<std::string> gpio_src; + typedef boost::char_separator<char> separator; + boost::tokenizer<separator> tokens(src_str, separator(" ")); + std::copy(tokens.begin(), tokens.end(), std::back_inserter(gpio_src)); + usrp->set_gpio_src(port, gpio_src); + } // print out initial unconfigured state of GPIO std::cout << "Initial GPIO values:" << std::endl; - output_reg_values(gpio, port, usrp, num_bits); + output_reg_values(gpio_bank, port, usrp, num_bits); // configure GPIO registers uint32_t ddr = strtoul(ddr_str.c_str(), NULL, 0); @@ -247,299 +466,162 @@ int UHD_SAFE_MAIN(int argc, char* argv[]) uint32_t atr_duplex = 0; uint32_t mask = (1 << num_bits) - 1; - if (!vm.count("bitbang")) { - // set up GPIO outputs: - // GPIO[0] = ATR output 1 at idle - ctrl |= GPIO_BIT(0); - atr_idle |= GPIO_BIT(0); - ddr |= GPIO_BIT(0); - - // GPIO[1] = ATR output 1 during RX - ctrl |= GPIO_BIT(1); - ddr |= GPIO_BIT(1); - atr_rx |= GPIO_BIT(1); - - // GPIO[2] = ATR output 1 during TX - ctrl |= GPIO_BIT(2); - ddr |= GPIO_BIT(2); - atr_tx |= GPIO_BIT(2); - - // GPIO[3] = ATR output 1 during full duplex - ctrl |= GPIO_BIT(3); - ddr |= GPIO_BIT(3); - atr_duplex |= GPIO_BIT(3); - - // GPIO[4] = output - ddr |= GPIO_BIT(4); + // The bitbang test is its own thing + if (vm.count("bitbang")) { + run_bitbang_test(usrp, gpio_bank, port, ddr, out, mask, num_bits, dwell_time); + return EXIT_SUCCESS; } - // set GPIO driver source - if (vm.count("src")) { - std::vector<std::string> gpio_src; - typedef boost::char_separator<char> separator; - boost::tokenizer<separator> tokens(src_str, separator(" ")); - std::copy(tokens.begin(), tokens.end(), std::back_inserter(gpio_src)); - usrp->set_gpio_src(port, gpio_src); - } + // set up GPIO outputs: + // GPIO[0] = ATR output 1 at idle + ctrl |= GPIO_BIT(0); + atr_idle |= GPIO_BIT(0); + ddr |= GPIO_BIT(0); + + // GPIO[1] = ATR output 1 during RX + ctrl |= GPIO_BIT(1); + ddr |= GPIO_BIT(1); + atr_rx |= GPIO_BIT(1); + + // GPIO[2] = ATR output 1 during TX + ctrl |= GPIO_BIT(2); + ddr |= GPIO_BIT(2); + atr_tx |= GPIO_BIT(2); + + // GPIO[3] = ATR output 1 during full duplex + ctrl |= GPIO_BIT(3); + ddr |= GPIO_BIT(3); + atr_duplex |= GPIO_BIT(3); + + // GPIO[4] = output + ddr |= GPIO_BIT(4); // set data direction register (DDR) - usrp->set_gpio_attr(gpio, "DDR", ddr, mask); + usrp->set_gpio_attr(gpio_bank, "DDR", ddr, mask); // set control register - usrp->set_gpio_attr(gpio, "CTRL", ctrl, mask); + usrp->set_gpio_attr(gpio_bank, "CTRL", ctrl, mask); // set output values (OUT) - usrp->set_gpio_attr(gpio, "OUT", out, mask); + usrp->set_gpio_attr(gpio_bank, "OUT", out, mask); // set ATR registers - usrp->set_gpio_attr(gpio, "ATR_0X", atr_idle, mask); - usrp->set_gpio_attr(gpio, "ATR_RX", atr_rx, mask); - usrp->set_gpio_attr(gpio, "ATR_TX", atr_tx, mask); - usrp->set_gpio_attr(gpio, "ATR_XX", atr_duplex, mask); + usrp->set_gpio_attr(gpio_bank, "ATR_0X", atr_idle, mask); + usrp->set_gpio_attr(gpio_bank, "ATR_RX", atr_rx, mask); + usrp->set_gpio_attr(gpio_bank, "ATR_TX", atr_tx, mask); + usrp->set_gpio_attr(gpio_bank, "ATR_XX", atr_duplex, mask); // print out initial state of FP GPIO std::cout << "\nConfigured GPIO values:" << std::endl; - output_reg_values(gpio, port, usrp, num_bits); + output_reg_values(gpio_bank, port, usrp, num_bits); std::cout << std::endl; // set up streams - uhd::stream_args_t rx_args(cpu, otw); - uhd::stream_args_t tx_args(cpu, otw); - uhd::rx_streamer::sptr rx_stream; - uhd::tx_streamer::sptr tx_stream; - uhd::stream_cmd_t rx_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); - rx_cmd.stream_now = true; - if (usrp->get_rx_num_channels()) { - rx_stream = usrp->get_rx_stream(rx_args); - usrp->set_rx_rate(rx_rate); - } - if (usrp->get_tx_num_channels()) { - tx_stream = usrp->get_tx_stream(tx_args); - usrp->set_tx_rate(tx_rate); - } - - // set up buffers for tx and rx - const size_t rx_spp = rx_stream ? rx_stream->get_max_num_samps() : 0; - const size_t tx_spp = tx_stream ? tx_stream->get_max_num_samps() : 0; - const size_t nsamps_per_buff = std::max(rx_spp, tx_spp); - std::vector<char> rx_buff(nsamps_per_buff * uhd::convert::get_bytes_per_item(cpu)); - std::vector<char> tx_buff(nsamps_per_buff * uhd::convert::get_bytes_per_item(cpu)); - std::vector<void*> rx_buffs, tx_buffs; - if (rx_stream) { - for (size_t ch = 0; ch < rx_stream->get_num_channels(); ch++) { - rx_buffs.push_back(&rx_buff.front()); // same buffer for each channel - } - } - if (tx_stream) { - for (size_t ch = 0; ch < tx_stream->get_num_channels(); ch++) - tx_buffs.push_back(&tx_buff.front()); // same buffer for each channel - } - - uhd::rx_metadata_t rx_md; - uhd::tx_metadata_t tx_md; - tx_md.has_time_spec = false; - tx_md.start_of_burst = true; - double timeout = 0.01; - auto dwell_time = std::chrono::milliseconds(static_cast<int64_t>(dwell * 1000)); + stream_helper_type stream_helper(usrp, rx_rate, tx_rate, cpu, otw, dwell_time); - int loop = 0; - uint32_t rb, expected; + int loop = 0; + int failures = 0; + bool tests_failed = false; // register signal handler std::signal(SIGINT, &sig_int_handler); - if (!vm.count("bitbang")) { - // Test the mask parameter of the multi_usrp::set_gpio_attr API - // We only need to test once with no dwell time - std::cout << "\nTesting mask..." << std::flush; - // send a value of all 1's to the DDR with a mask for only upper most bit - usrp->set_gpio_attr(gpio, "DDR", ~0, GPIO_BIT(num_bits - 1)); - // upper most bit should now be 1, but all the other bits should be unchanged - rb = usrp->get_gpio_attr(gpio, "DDR") & mask; - expected = ddr | GPIO_BIT(num_bits - 1); - if (rb == expected) - std::cout << "pass:" << std::endl; - else - std::cout << "fail:" << std::endl; - output_reg_values(gpio, port, usrp, num_bits); - // restore DDR value - usrp->set_gpio_attr(gpio, "DDR", ddr, mask); - } - + // Test the mask parameter of the multi_usrp::set_gpio_attr API + // We only need to test once with no dwell time + std::cout << "\nTesting mask..." << std::flush; + // send a value of all 1's to the DDR with a mask for only upper most bit + usrp->set_gpio_attr(gpio_bank, "DDR", ~0, GPIO_BIT(num_bits - 1)); + // upper most bit should now be 1, but all the other bits should be unchanged + failures += int(!check_rb_values(usrp->get_gpio_attr(gpio_bank, "DDR") & mask, + ddr | GPIO_BIT(num_bits - 1), + num_bits, 0)); + output_reg_values(gpio_bank, port, usrp, num_bits); + // restore DDR value + usrp->set_gpio_attr(gpio_bank, "DDR", ddr, mask); + + /*************************************************************************/ + /* Setup complete, start running test */ + /*************************************************************************/ while (not stop_signal_called) { - int failures = 0; - if (vm.count("repeat")) std::cout << "Press Ctrl + C to quit..." << std::endl; - if (vm.count("bitbang")) { - // dwell and continuously read back GPIO values - auto stop_time = std::chrono::steady_clock::now() + dwell_time; - while ( - not stop_signal_called and std::chrono::steady_clock::now() < stop_time) { - rb = usrp->get_gpio_attr(gpio, "READBACK"); - std::cout << "\rREADBACK: " << to_bit_string(rb, num_bits); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - std::cout << std::endl; - } else { - // test user controlled GPIO and ATR idle by setting bit 4 high for 1 second - std::cout << "\nTesting user controlled GPIO and ATR idle output..." - << std::flush; - usrp->set_gpio_attr(gpio, "OUT", GPIO_BIT(4), GPIO_BIT(4)); - auto stop_time = std::chrono::steady_clock::now() + dwell_time; - while ( - not stop_signal_called and std::chrono::steady_clock::now() < stop_time) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - rb = usrp->get_gpio_attr(gpio, "READBACK"); - expected = GPIO_BIT(4) | GPIO_BIT(0); - if ((rb & expected) != expected) { - ++failures; - std::cout << "fail:" << std::endl; - if ((rb & GPIO_BIT(0)) == 0) - std::cout << "Bit 0 should be set, but is not" << std::endl; - if ((rb & GPIO_BIT(4)) == 0) - std::cout << "Bit 4 should be set, but is not" << std::endl; - } else { - std::cout << "pass:" << std::endl; - } - output_reg_values(gpio, port, usrp, num_bits); - usrp->set_gpio_attr(gpio, "OUT", 0, GPIO_BIT(4)); - if (stop_signal_called) - break; - - if (rx_stream) { - // test ATR RX by receiving for 1 second - std::cout << "\nTesting ATR RX output..." << std::flush; - rx_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; - rx_stream->issue_stream_cmd(rx_cmd); - stop_time = std::chrono::steady_clock::now() + dwell_time; - while (not stop_signal_called - and std::chrono::steady_clock::now() < stop_time) { - try { - rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); - } catch (...) { - } - } - rb = usrp->get_gpio_attr(gpio, "READBACK"); - expected = GPIO_BIT(1); - if ((rb & expected) != expected) { - ++failures; - std::cout << "fail:" << std::endl; - std::cout << "Bit 1 should be set, but is not" << std::endl; - } else { - std::cout << "pass:" << std::endl; - } - output_reg_values(gpio, port, usrp, num_bits); - rx_stream->issue_stream_cmd( - uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); - // clear out any data left in the rx stream - try { - rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); - } catch (...) { - } - } - if (stop_signal_called) - break; - - if (tx_stream) { - // test ATR TX by transmitting for 1 second - std::cout << "\nTesting ATR TX output..." << std::flush; - stop_time = std::chrono::steady_clock::now() + dwell_time; - tx_md.start_of_burst = true; - tx_md.end_of_burst = false; - while (not stop_signal_called - and std::chrono::steady_clock::now() < stop_time) { - try { - tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); - tx_md.start_of_burst = false; - } catch (...) { - } - } - rb = usrp->get_gpio_attr(gpio, "READBACK"); - expected = GPIO_BIT(2); - if ((rb & expected) != expected) { - ++failures; - std::cout << "fail:" << std::endl; - std::cout << "Bit 2 should be set, but is not" << std::endl; - } else { - std::cout << "pass:" << std::endl; - } - output_reg_values(gpio, port, usrp, num_bits); - tx_md.end_of_burst = true; - try { - tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); - } catch (...) { - } - } - if (stop_signal_called) - break; - - if (rx_stream and tx_stream) { - // test ATR full duplex by transmitting and receiving - std::cout << "\nTesting ATR full duplex output..." << std::flush; - rx_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; - rx_stream->issue_stream_cmd(rx_cmd); - tx_md.start_of_burst = true; - tx_md.end_of_burst = false; - stop_time = std::chrono::steady_clock::now() + dwell_time; - while (not stop_signal_called - and std::chrono::steady_clock::now() < stop_time) { - try { - tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); - tx_md.start_of_burst = false; - rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); - } catch (...) { - } - } - - // Read GPIO - rb = usrp->get_gpio_attr(gpio, "READBACK"); + /*** Test 1: User-controlled GPIO + ATR idle *************************/ + std::cout << "\nTesting user controlled GPIO and ATR idle output..." + << std::flush; + usrp->set_gpio_attr(gpio_bank, "OUT", GPIO_BIT(4), GPIO_BIT(4)); + auto stop_time = std::chrono::steady_clock::now() + dwell_time; + while (not stop_signal_called and std::chrono::steady_clock::now() < stop_time) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + failures += int(!check_rb_values(usrp->get_gpio_attr(gpio_bank, "READBACK"), + GPIO_BIT(4) | GPIO_BIT(0), + num_bits, loopback_num_bits)); + output_reg_values(gpio_bank, port, usrp, num_bits); + usrp->set_gpio_attr(gpio_bank, "OUT", 0, GPIO_BIT(4)); + if (stop_signal_called) + break; - // Stop streaming - rx_stream->issue_stream_cmd( - uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); - tx_md.end_of_burst = true; - try { - tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); - } catch (...) { - } + /*** Test 2: ATR RX **************************************************/ + if (stream_helper.rx_stream) { + // test ATR RX by receiving for 1 second + std::cout << "\nTesting ATR RX output..." << std::flush; + stream_helper.start_stream(false, true); + failures += int(!check_rb_values(usrp->get_gpio_attr(gpio_bank, "READBACK"), + GPIO_BIT(1), + num_bits, loopback_num_bits)); + output_reg_values(gpio_bank, port, usrp, num_bits); + stream_helper.stop_stream(false, true); + } + if (stop_signal_called) + break; - // clear out any data left in the rx stream - try { - rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); - } catch (...) { - } + /*** Test 3: ATR TX **************************************************/ + if (stream_helper.tx_stream) { + // test ATR TX by transmitting for 1 second + std::cout << "\nTesting ATR TX output..." << std::flush; + stream_helper.start_stream(true, false); + failures += int(!check_rb_values(usrp->get_gpio_attr(gpio_bank, "READBACK"), + GPIO_BIT(2), + num_bits, loopback_num_bits)); + output_reg_values(gpio_bank, port, usrp, num_bits); + stream_helper.stop_stream(true, false); + } + if (stop_signal_called) + break; - // Analyze results - expected = GPIO_BIT(3); - if ((rb & expected) != expected) { - ++failures; - std::cout << "fail:" << std::endl; - std::cout << "Bit 3 should be set, but is not" << std::endl; - } else { - std::cout << "pass:" << std::endl; - } - output_reg_values(gpio, port, usrp, num_bits); - } + /*** Test 4: ATR FDX *************************************************/ + if (stream_helper.rx_stream and stream_helper.tx_stream) { + // test ATR full duplex by transmitting and receiving + std::cout << "\nTesting ATR full duplex output..." << std::flush; + stream_helper.start_stream(true, true); + failures += int(!check_rb_values(usrp->get_gpio_attr(gpio_bank, "READBACK"), + GPIO_BIT(3), + num_bits, loopback_num_bits)); + stream_helper.stop_stream(true, true); + output_reg_values(gpio_bank, port, usrp, num_bits); + } - std::cout << std::endl; - if (failures) - std::cout << failures << " tests failed" << std::endl; - else - std::cout << "All tests passed!" << std::endl; + std::cout << std::endl; + if (failures) { + tests_failed = true; + std::cout << failures << " tests failed" << std::endl; + } else { + std::cout << "All tests passed!" << std::endl; } - if (!vm.count("repeat")) + if (!vm.count("repeat")) { break; + } + failures = 0; - if (not stop_signal_called) - std::cout << (boost::format("\nLoop %d completed") % ++loop) << std::endl; + if (not stop_signal_called) { + std::cout << "\nLoop " << ++loop << " completed" << std::endl; + } } // finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return EXIT_SUCCESS; + return int(tests_failed); } |