diff options
Diffstat (limited to 'host/utils')
-rw-r--r-- | host/utils/CMakeLists.txt | 77 | ||||
-rw-r--r-- | host/utils/clkgen-config.cpp | 296 | ||||
-rw-r--r-- | host/utils/fpga-downloader.cpp | 267 | ||||
-rw-r--r-- | host/utils/uhd_find_devices.cpp | 60 | ||||
-rw-r--r-- | host/utils/uhd_usrp_probe.cpp | 179 | ||||
-rw-r--r-- | host/utils/usrp-e-debug-pins.c | 77 | ||||
-rw-r--r-- | host/utils/usrp-e-i2c.c | 87 | ||||
-rw-r--r-- | host/utils/usrp-e-loopback.c | 231 | ||||
-rw-r--r-- | host/utils/usrp-e-spi.c | 54 | ||||
-rw-r--r-- | host/utils/usrp1_init_eeprom.cpp | 69 | ||||
-rwxr-xr-x | host/utils/usrp2_card_burner.py | 246 | ||||
-rwxr-xr-x | host/utils/usrp2_card_burner_gui.py | 169 | ||||
-rwxr-xr-x | host/utils/usrp2_recovery.py | 68 | ||||
-rw-r--r-- | host/utils/usrp_burn_db_eeprom.cpp | 100 | ||||
-rw-r--r-- | host/utils/usrp_burn_mb_eeprom.cpp | 81 | ||||
-rw-r--r-- | host/utils/usrp_e_regs.hpp | 196 | ||||
-rwxr-xr-x | host/utils/usrp_n2xx_net_burner.py | 288 |
17 files changed, 2545 insertions, 0 deletions
diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt new file mode 100644 index 000000000..a8e50f72b --- /dev/null +++ b/host/utils/CMakeLists.txt @@ -0,0 +1,77 @@ +# +# Copyright 2010 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# Utilities that get installed into the runtime path +######################################################################## +SET(util_runtime_sources + uhd_find_devices.cpp + uhd_usrp_probe.cpp +) + +#for each source: build an executable and install +FOREACH(util_source ${util_runtime_sources}) + GET_FILENAME_COMPONENT(util_name ${util_source} NAME_WE) + ADD_EXECUTABLE(${util_name} ${util_source}) + TARGET_LINK_LIBRARIES(${util_name} uhd) + INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${RUNTIME_DIR}) +ENDFOREACH(util_source) + +######################################################################## +# Utilities that get installed into the share path +######################################################################## +SET(util_share_sources + usrp_burn_db_eeprom.cpp + usrp_burn_mb_eeprom.cpp +) + +IF(ENABLE_USRP1) + LIST(APPEND util_share_sources + usrp1_init_eeprom.cpp + ) +ENDIF(ENABLE_USRP1) + +IF(ENABLE_USRP_E100) + ENABLE_LANGUAGE(C) + INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e100/include) + LIST(APPEND util_share_sources + fpga-downloader.cpp + clkgen-config.cpp + usrp-e-loopback.c + usrp-e-debug-pins.c + usrp-e-i2c.c + usrp-e-spi.c + ) +ENDIF(ENABLE_USRP_E100) + +#for each source: build an executable and install +FOREACH(util_source ${util_share_sources}) + GET_FILENAME_COMPONENT(util_name ${util_source} NAME_WE) + ADD_EXECUTABLE(${util_name} ${util_source}) + TARGET_LINK_LIBRARIES(${util_name} uhd) + INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${PKG_DATA_DIR}/utils) +ENDFOREACH(util_source) + +IF(ENABLE_USRP2) + INSTALL(PROGRAMS + usrp2_recovery.py + usrp2_card_burner.py + usrp2_card_burner_gui.py + usrp_n2xx_net_burner.py + DESTINATION ${PKG_DATA_DIR}/utils + ) +ENDIF(ENABLE_USRP2) diff --git a/host/utils/clkgen-config.cpp b/host/utils/clkgen-config.cpp new file mode 100644 index 000000000..e8279b4ae --- /dev/null +++ b/host/utils/clkgen-config.cpp @@ -0,0 +1,296 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2004,2008,2009 Free Software Foundation, Inc. + * + * This file is part of UHD + * + * 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. +*/ + +#include <iostream> +#include <sstream> +#include <fstream> +#include <string> +#include <cstdlib> + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +#include <linux/spi/spidev.h> + + +// Programming data for clock gen chip +static const unsigned int config_data[] = { + 0x000024, + 0x023201, + 0x000081, + 0x000400, + 0x00104c, + 0x001101, + 0x001200, + 0x001300, + 0x001414, + 0x001500, + 0x001604, + 0x001704, + 0x001807, + 0x001900, + //0x001a00,//for debug + 0x001a32, + 0x001b12, + 0x001c44, + 0x001d00, + 0x001e00, + 0x00f062, + 0x00f162, + 0x00f262, + 0x00f362, + 0x00f462, + 0x00f562, + 0x00f662, + 0x00f762, + 0x00f862, + 0x00f962, + 0x00fa62, + 0x00fb62, + 0x00fc00, + 0x00fd00, + 0x019021, + 0x019100, + 0x019200, + 0x019333, + 0x019400, + 0x019500, + 0x019611, + 0x019700, + 0x019800, + 0x019900, + 0x019a00, + 0x019b00, + 0x01e003, + 0x01e102, + 0x023000, + 0x023201, + 0x0b0201, + 0x0b0300, + 0x001fff, + 0x0a0000, + 0x0a0100, + 0x0a0200, + 0x0a0302, + 0x0a0400, + 0x0a0504, + 0x0a060e, + 0x0a0700, + 0x0a0810, + 0x0a090e, + 0x0a0a00, + 0x0a0bf0, + 0x0a0c0b, + 0x0a0d01, + 0x0a0e90, + 0x0a0f01, + 0x0a1001, + 0x0a11e0, + 0x0a1201, + 0x0a1302, + 0x0a1430, + 0x0a1580, + 0x0a16ff, + 0x023201, + 0x0b0301, + 0x023201, +}; + + +const unsigned int CLKGEN_SELECT = 145; + + +enum gpio_direction {IN, OUT}; + +class gpio { + public: + + gpio(unsigned int gpio_num, gpio_direction pin_direction, bool close_action); + ~gpio(); + + bool get_value(); + void set_value(bool state); + + private: + + unsigned int gpio_num; + + std::stringstream base_path; + std::fstream value_file; + std::fstream direction_file; + bool close_action; // True set to input and release, false do nothing +}; + +class spidev { + public: + + spidev(std::string dev_name); + ~spidev(); + + void send(char *wbuf, char *rbuf, unsigned int nbytes); + + private: + + int fd; + +}; + +gpio::gpio(unsigned int _gpio_num, gpio_direction pin_direction, bool close_action) +{ + std::fstream export_file; + + gpio_num = _gpio_num; + + export_file.open("/sys/class/gpio/export", std::ios::out); + if (!export_file.is_open()) ///\todo Poor error handling + std::cout << "Failed to open gpio export file." << std::endl; + + export_file << gpio_num << std::endl; + + base_path << "/sys/class/gpio/gpio" << gpio_num << std::flush; + + std::string direction_file_name; + + direction_file_name = base_path.str() + "/direction"; + + direction_file.open(direction_file_name.c_str()); + if (!direction_file.is_open()) + std::cout << "Failed to open direction file." << std::endl; + if (pin_direction == OUT) + direction_file << "out" << std::endl; + else + direction_file << "in" << std::endl; + + std::string value_file_name; + + value_file_name = base_path.str() + "/value"; + + value_file.open(value_file_name.c_str(), std::ios_base::in | std::ios_base::out); + if (!value_file.is_open()) + std::cout << "Failed to open value file." << std::endl; +} + +bool gpio::get_value() +{ + + std::string val; + + std::getline(value_file, val); + value_file.seekg(0); + + if (val == "0") + return false; + else if (val == "1") + return true; + else + std::cout << "Data read from value file|" << val << "|" << std::endl; + + return false; +} + +void gpio::set_value(bool state) +{ + + if (state) + value_file << "1" << std::endl; + else + value_file << "0" << std::endl; +} + +gpio::~gpio() +{ + if (close_action) { + std::fstream unexport_file; + + direction_file << "in" << std::endl; + + unexport_file.open("/sys/class/gpio/unexport", std::ios::out); + if (!unexport_file.is_open()) ///\todo Poor error handling + std::cout << "Failed to open gpio export file." << std::endl; + + unexport_file << gpio_num << std::endl; + + } + +} + +spidev::spidev(std::string fname) +{ + int ret; + int mode = 0; + int speed = 12000; + int bits = 24; + + fd = open(fname.c_str(), O_RDWR); + + ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); + ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); + ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); +} + + +spidev::~spidev() +{ + close(fd); +} + +void spidev::send(char *buf, char *rbuf, unsigned int nbytes) +{ + int ret; + + struct spi_ioc_transfer tr; + tr.tx_buf = (unsigned long) buf; + tr.rx_buf = (unsigned long) rbuf; + tr.len = nbytes; + tr.delay_usecs = 0; + tr.speed_hz = 12000000; + tr.bits_per_word = 24; + + ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); + +} + +static void send_config_to_clkgen(gpio &chip_select, const unsigned int data[], unsigned int data_size) +{ + spidev spi("/dev/spidev1.0"); + unsigned int rbuf; + + for (unsigned int i = 0; i < data_size; i++) { + + std::cout << "sending " << std::hex << data[i] << std::endl; + chip_select.set_value(0); + spi.send((char *)&data[i], (char *)&rbuf, 4); + chip_select.set_value(1); + + }; +} + +int main(int argc, char *argv[]) +{ + + gpio clkgen_select(CLKGEN_SELECT, OUT, true); + + send_config_to_clkgen(clkgen_select, config_data, sizeof(config_data)/sizeof(unsigned int)); +} + diff --git a/host/utils/fpga-downloader.cpp b/host/utils/fpga-downloader.cpp new file mode 100644 index 000000000..80ee71600 --- /dev/null +++ b/host/utils/fpga-downloader.cpp @@ -0,0 +1,267 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2004,2008,2009 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. +*/ + +#include <iostream> +#include <sstream> +#include <fstream> +#include <string> +#include <cstdlib> + +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +#include <linux/spi/spidev.h> + +/* + * Configuration connections + * + * CCK - MCSPI1_CLK + * DIN - MCSPI1_MOSI + * PROG_B - GPIO_175 - output (change mux) + * DONE - GPIO_173 - input (change mux) + * INIT_B - GPIO_114 - input (change mux) + * +*/ + +const unsigned int PROG_B = 175; +const unsigned int DONE = 173; +const unsigned int INIT_B = 114; + +static std::string bit_file = "safe_u1e.bin"; + +const int BUF_SIZE = 4096; + +enum gpio_direction {IN, OUT}; + +class gpio { + public: + + gpio(unsigned int gpio_num, gpio_direction pin_direction); + + bool get_value(); + void set_value(bool state); + + private: + + std::stringstream base_path; + std::fstream value_file; +}; + +class spidev { + public: + + spidev(std::string dev_name); + ~spidev(); + + void send(char *wbuf, char *rbuf, unsigned int nbytes); + + private: + + int fd; + +}; + +gpio::gpio(unsigned int gpio_num, gpio_direction pin_direction) +{ + std::fstream export_file; + + export_file.open("/sys/class/gpio/export", std::ios::out); + if (!export_file.is_open()) ///\todo Poor error handling + std::cout << "Failed to open gpio export file." << std::endl; + + export_file << gpio_num << std::endl; + + base_path << "/sys/class/gpio/gpio" << gpio_num << std::flush; + + std::fstream direction_file; + std::string direction_file_name; + + direction_file_name = base_path.str() + "/direction"; + + direction_file.open(direction_file_name.c_str()); + if (!direction_file.is_open()) + std::cout << "Failed to open direction file." << std::endl; + if (pin_direction == OUT) + direction_file << "out" << std::endl; + else + direction_file << "in" << std::endl; + + std::string value_file_name; + + value_file_name = base_path.str() + "/value"; + + value_file.open(value_file_name.c_str(), std::ios_base::in | std::ios_base::out); + if (!value_file.is_open()) + std::cout << "Failed to open value file." << std::endl; +} + +bool gpio::get_value() +{ + + std::string val; + + std::getline(value_file, val); + value_file.seekg(0); + + if (val == "0") + return false; + else if (val == "1") + return true; + else + std::cout << "Data read from value file|" << val << "|" << std::endl; + + return false; +} + +void gpio::set_value(bool state) +{ + + if (state) + value_file << "1" << std::endl; + else + value_file << "0" << std::endl; +} + +static void prepare_fpga_for_configuration(gpio &prog, gpio &init) +{ + + prog.set_value(true); + prog.set_value(false); + prog.set_value(true); + +#if 0 + bool ready_to_program(false); + unsigned int count(0); + do { + ready_to_program = init.get_value(); + count++; + + sleep(1); + } while (count < 10 && !ready_to_program); + + if (count == 10) { + std::cout << "FPGA not ready for programming." << std::endl; + exit(-1); + } +#endif +} + +spidev::spidev(std::string fname) +{ + int ret; + int mode = 0; + int speed = 12000000; + int bits = 8; + + fd = open(fname.c_str(), O_RDWR); + + ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); + ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); + ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); +} + + +spidev::~spidev() +{ + close(fd); +} + +void spidev::send(char *buf, char *rbuf, unsigned int nbytes) +{ + int ret; + + struct spi_ioc_transfer tr; + tr.tx_buf = (unsigned long) buf; + tr.rx_buf = (unsigned long) rbuf; + tr.len = nbytes; + tr.delay_usecs = 0; + tr.speed_hz = 48000000; + tr.bits_per_word = 8; + + ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); + +} + +static void send_file_to_fpga(std::string &file_name, gpio &error, gpio &done) +{ + std::ifstream bitstream; + + std::cout << "File name - " << file_name.c_str() << std::endl; + + bitstream.open(file_name.c_str(), std::ios::binary); + if (!bitstream.is_open()) + std::cout << "File " << file_name << " not opened succesfully." << std::endl; + + spidev spi("/dev/spidev1.0"); + char buf[BUF_SIZE]; + char rbuf[BUF_SIZE]; + + do { + bitstream.read(buf, BUF_SIZE); + spi.send(buf, rbuf, bitstream.gcount()); + + if (error.get_value()) + std::cout << "INIT_B went high, error occured." << std::endl; + + if (!done.get_value()) + std::cout << "Configuration complete." << std::endl; + + } while (bitstream.gcount() == BUF_SIZE); +} + +int main(int argc, char *argv[]) +{ + + gpio gpio_prog_b(PROG_B, OUT); + gpio gpio_init_b(INIT_B, IN); + gpio gpio_done (DONE, IN); + + if (argc == 2) + bit_file = argv[1]; + + bool module_found(false); + std::ifstream mod_file("/proc/modules"); + while (!mod_file.eof()) { + std::string line; + getline(mod_file, line); + if (line.find("usrp_e") != std::string::npos) + module_found = true; + } + mod_file.close(); + + if (module_found) { + std::cout << "USRP Embedded kernel module loaded, not loading FPGA." << std::endl; + return -1; + } + + std::cout << "FPGA config file: " << bit_file << std::endl; + + prepare_fpga_for_configuration(gpio_prog_b, gpio_init_b); + + std::cout << "Done = " << gpio_done.get_value() << std::endl; + + send_file_to_fpga(bit_file, gpio_init_b, gpio_done); +} + diff --git a/host/utils/uhd_find_devices.cpp b/host/utils/uhd_find_devices.cpp new file mode 100644 index 000000000..b778eeb68 --- /dev/null +++ b/host/utils/uhd_find_devices.cpp @@ -0,0 +1,60 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils/safe_main.hpp> +#include <uhd/device.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>()->default_value(""), "device address args") + ; + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //print the help message + if (vm.count("help")){ + std::cout << boost::format("UHD Find Devices %s") % desc << std::endl; + return ~0; + } + + //discover the usrps and print the results + uhd::device_addrs_t device_addrs = uhd::device::find(vm["args"].as<std::string>()); + + if (device_addrs.size() == 0){ + std::cerr << "No UHD Devices Found" << std::endl; + return ~0; + } + + for (size_t i = 0; i < device_addrs.size(); i++){ + std::cout << "--------------------------------------------------" << std::endl; + std::cout << "-- UHD Device " << i << std::endl; + std::cout << "--------------------------------------------------" << std::endl; + std::cout << device_addrs[i].to_pp_string() << std::endl << std::endl; + //uhd::device::make(device_addrs[i]); //test make + } + + return 0; +} diff --git a/host/utils/uhd_usrp_probe.cpp b/host/utils/uhd_usrp_probe.cpp new file mode 100644 index 000000000..5cba7c362 --- /dev/null +++ b/host/utils/uhd_usrp_probe.cpp @@ -0,0 +1,179 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils/safe_main.hpp> +#include <uhd/device.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/usrp/device_props.hpp> +#include <uhd/usrp/mboard_props.hpp> +#include <uhd/usrp/dboard_props.hpp> +#include <uhd/usrp/codec_props.hpp> +#include <uhd/usrp/dsp_props.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <uhd/usrp/dboard_id.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/foreach.hpp> +#include <iostream> +#include <sstream> + +namespace po = boost::program_options; +using namespace uhd; + +static std::string indent(size_t level){ + return (level)? (indent(level-1) + " ") : ""; +} + +static std::string make_border(const std::string &text){ + std::stringstream ss; + ss << boost::format(" _____________________________________________________") << std::endl; + ss << boost::format(" /") << std::endl; + std::vector<std::string> lines = std::split_string(text, "\n"); + while (lines.back() == "") lines.pop_back(); //strip trailing newlines + if (lines.size()) lines[0] = " " + lines[0]; //indent the title line + BOOST_FOREACH(const std::string &line, lines){ + ss << boost::format("| %s") % line << std::endl; + } + //ss << boost::format(" \\____________________________________________________") << std::endl; + return ss.str(); +} + +static std::string get_dsp_pp_string(const std::string &type, wax::obj dsp){ + std::stringstream ss; + ss << boost::format("%s DSP: %s") % type % dsp[usrp::DSP_PROP_NAME].as<std::string>() << std::endl; + //ss << std::endl; + ss << boost::format("Codec Rate: %f Msps") % (dsp[usrp::DSP_PROP_CODEC_RATE].as<double>()/1e6) << std::endl; + //ss << boost::format("Host Rate: %f Msps") % (dsp[usrp::DSP_PROP_HOST_RATE].as<double>()/1e6) << std::endl; + //ss << boost::format("Freq Shift: %f Mhz") % (dsp[usrp::DSP_PROP_FREQ_SHIFT].as<double>()/1e6) << std::endl; + return ss.str(); +} + +static std::string prop_names_to_pp_string(const prop_names_t &prop_names){ + std::stringstream ss; size_t count = 0; + BOOST_FOREACH(const std::string &prop_name, prop_names){ + ss << ((count++)? ", " : "") << prop_name; + } + return ss.str(); +} + +static std::string get_subdev_pp_string(const std::string &type, wax::obj subdev){ + std::stringstream ss; + ss << boost::format("%s Subdev: %s") % type % subdev[usrp::SUBDEV_PROP_NAME].as<std::string>() << std::endl; + //ss << std::endl; + + prop_names_t ant_names(subdev[usrp::SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>()); + ss << boost::format("Antennas: %s") % prop_names_to_pp_string(ant_names) << std::endl; + + freq_range_t freq_range(subdev[usrp::SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>()); + ss << boost::format("Freq range: %.3f to %.3f Mhz") % (freq_range.start()/1e6) % (freq_range.stop()/1e6) << std::endl; + + prop_names_t gain_names(subdev[usrp::SUBDEV_PROP_GAIN_NAMES].as<prop_names_t>()); + if (gain_names.size() == 0) ss << "Gain Elements: None" << std::endl; + BOOST_FOREACH(const std::string &gain_name, gain_names){ + gain_range_t gain_range(subdev[named_prop_t(usrp::SUBDEV_PROP_GAIN_RANGE, gain_name)].as<gain_range_t>()); + ss << boost::format("Gain range %s: %.1f to %.1f step %.1f dB") % gain_name % gain_range.start() % gain_range.stop() % gain_range.step() << std::endl; + } + + ss << boost::format("Connection Type: %c") % char(subdev[usrp::SUBDEV_PROP_CONNECTION].as<usrp::subdev_conn_t>()) << std::endl; + ss << boost::format("Uses LO offset: %s") % (subdev[usrp::SUBDEV_PROP_USE_LO_OFFSET].as<bool>()? "Yes" : "No") << std::endl; + + return ss.str(); +} + +static std::string get_codec_pp_string(const std::string &type, wax::obj codec){ + std::stringstream ss; + ss << boost::format("%s Codec: %s") % type % codec[usrp::CODEC_PROP_NAME].as<std::string>() << std::endl; + //ss << std::endl; + prop_names_t gain_names(codec[usrp::CODEC_PROP_GAIN_NAMES].as<prop_names_t>()); + if (gain_names.size() == 0) ss << "Gain Elements: None" << std::endl; + BOOST_FOREACH(const std::string &gain_name, gain_names){ + gain_range_t gain_range(codec[named_prop_t(usrp::CODEC_PROP_GAIN_RANGE, gain_name)].as<gain_range_t>()); + ss << boost::format("Gain range %s: %.1f to %.1f step %.1f dB") % gain_name % gain_range.start() % gain_range.stop() % gain_range.step() << std::endl; + } + return ss.str(); +} + +static std::string get_dboard_pp_string(const std::string &type, wax::obj dboard){ + std::stringstream ss; + ss << boost::format("%s Dboard: %s") % type % dboard[usrp::DBOARD_PROP_NAME].as<std::string>() << std::endl; + //ss << std::endl; + BOOST_FOREACH(const std::string &subdev_name, dboard[usrp::DBOARD_PROP_SUBDEV_NAMES].as<prop_names_t>()){ + ss << make_border(get_subdev_pp_string(type, dboard[named_prop_t(usrp::DBOARD_PROP_SUBDEV, subdev_name)])); + } + ss << make_border(get_codec_pp_string(type, dboard[usrp::DBOARD_PROP_CODEC])); + return ss.str(); +} + +static std::string get_mboard_pp_string(wax::obj mboard){ + std::stringstream ss; + ss << boost::format("Mboard: %s") % mboard[usrp::MBOARD_PROP_NAME].as<std::string>() << std::endl; + //ss << std::endl; + usrp::mboard_eeprom_t mb_eeprom = mboard[usrp::MBOARD_PROP_EEPROM_MAP].as<usrp::mboard_eeprom_t>(); + BOOST_FOREACH(const std::string &key, mb_eeprom.keys()){ + if (not mb_eeprom[key].empty()) ss << boost::format("%s: %s") % key % mb_eeprom[key] << std::endl; + } + BOOST_FOREACH(const std::string &dsp_name, mboard[usrp::MBOARD_PROP_RX_DSP_NAMES].as<prop_names_t>()){ + ss << make_border(get_dsp_pp_string("RX", mboard[named_prop_t(usrp::MBOARD_PROP_RX_DSP, dsp_name)])); + } + BOOST_FOREACH(const std::string &dsp_name, mboard[usrp::MBOARD_PROP_TX_DSP_NAMES].as<prop_names_t>()){ + ss << make_border(get_dsp_pp_string("TX", mboard[named_prop_t(usrp::MBOARD_PROP_TX_DSP, dsp_name)])); + } + BOOST_FOREACH(const std::string &dsp_name, mboard[usrp::MBOARD_PROP_RX_DBOARD_NAMES].as<prop_names_t>()){ + ss << make_border(get_dboard_pp_string("RX", mboard[named_prop_t(usrp::MBOARD_PROP_RX_DBOARD, dsp_name)])); + } + BOOST_FOREACH(const std::string &dsp_name, mboard[usrp::MBOARD_PROP_TX_DBOARD_NAMES].as<prop_names_t>()){ + ss << make_border(get_dboard_pp_string("TX", mboard[named_prop_t(usrp::MBOARD_PROP_TX_DBOARD, dsp_name)])); + } + return ss.str(); +} + + +static std::string get_device_pp_string(device::sptr dev){ + std::stringstream ss; + ss << boost::format("Device: %s") % (*dev)[usrp::DEVICE_PROP_NAME].as<std::string>() << std::endl; + //ss << std::endl; + BOOST_FOREACH(const std::string &mboard_name, (*dev)[usrp::DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>()){ + ss << make_border(get_mboard_pp_string((*dev)[named_prop_t(usrp::DEVICE_PROP_MBOARD, mboard_name)])); + } + return ss.str(); +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>()->default_value(""), "device address args") + ; + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //print the help message + if (vm.count("help")){ + std::cout << boost::format("UHD USRP Probe %s") % desc << std::endl; + return ~0; + } + + device::sptr dev = device::make(vm["args"].as<std::string>()); + + std::cout << make_border(get_device_pp_string(dev)) << std::endl; + + return 0; +} diff --git a/host/utils/usrp-e-debug-pins.c b/host/utils/usrp-e-debug-pins.c new file mode 100644 index 000000000..1ed2c8983 --- /dev/null +++ b/host/utils/usrp-e-debug-pins.c @@ -0,0 +1,77 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <fcntl.h> +#include <string.h> +#include <sys/ioctl.h> + +#include <linux/usrp_e.h> +#include "usrp_e_regs.hpp" + +// Usage: usrp_e_gpio <string> + +static int fp; + +static int read_reg(__u16 reg) +{ + int ret; + struct usrp_e_ctl16 d; + + d.offset = reg; + d.count = 1; + ret = ioctl(fp, USRP_E_READ_CTL16, &d); + return d.buf[0]; +} + +static void write_reg(__u16 reg, __u16 val) +{ + int ret; + struct usrp_e_ctl16 d; + + d.offset = reg; + d.count = 1; + d.buf[0] = val; + ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); +} + +int main(int argc, char *argv[]) +{ + int test; + + test = 0; + if (argc < 2) { + printf("%s 0|1|off\n", argv[0]); + } + + fp = open("/dev/usrp_e0", O_RDWR); + printf("fp = %d\n", fp); + if (fp < 0) { + perror("Open failed"); + return -1; + } + + if (strcmp(argv[1], "0") == 0) { + printf("Selected 0 based on %s\n", argv[1]); + write_reg(UE_REG_GPIO_TX_DDR, 0xFFFF); + write_reg(UE_REG_GPIO_RX_DDR, 0xFFFF); + write_reg(UE_REG_GPIO_TX_SEL, 0x0); + write_reg(UE_REG_GPIO_RX_SEL, 0x0); + write_reg(UE_REG_GPIO_TX_DBG, 0xFFFF); + write_reg(UE_REG_GPIO_RX_DBG, 0xFFFF); + } else if (strcmp(argv[1], "1") == 0) { + printf("Selected 1 based on %s\n", argv[1]); + write_reg(UE_REG_GPIO_TX_DDR, 0xFFFF); + write_reg(UE_REG_GPIO_RX_DDR, 0xFFFF); + write_reg(UE_REG_GPIO_TX_SEL, 0xFFFF); + write_reg(UE_REG_GPIO_RX_SEL, 0xFFFF); + write_reg(UE_REG_GPIO_TX_DBG, 0xFFFF); + write_reg(UE_REG_GPIO_RX_DBG, 0xFFFF); + } else { + printf("Selected off based on %s\n", argv[1]); + write_reg(UE_REG_GPIO_TX_DDR, 0x0); + write_reg(UE_REG_GPIO_RX_DDR, 0x0); + } + + return 0; +} diff --git a/host/utils/usrp-e-i2c.c b/host/utils/usrp-e-i2c.c new file mode 100644 index 000000000..c6fd4c632 --- /dev/null +++ b/host/utils/usrp-e-i2c.c @@ -0,0 +1,87 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#include <linux/usrp_e.h> + +// Usage: usrp_e_i2c w address data0 data1 data 2 .... +// Usage: usrp_e_i2c r address count + +int main(int argc, char *argv[]) +{ + int fp, ret, i, tmp; + struct usrp_e_i2c *i2c_msg; + int direction, address, count; + + if (argc < 3) { + printf("Usage: usrp-e-i2c w address data0 data1 data2 ...\n"); + printf("Usage: usrp-e-i2c r address count\n"); + printf("All addresses and data in hex.\n"); + exit(-1); + } + + if (strcmp(argv[1], "r") == 0) { + direction = 0; + } else if (strcmp(argv[1], "w") == 0) { + direction = 1; + } else { + return -1; + } + + sscanf(argv[2], "%X", &address); + printf("Address = %X\n", address); + + fp = open("/dev/usrp_e0", O_RDWR); + printf("fp = %d\n", fp); + if (fp < 0) { + perror("Open failed"); + return -1; + } + +// sleep(1); + + if (direction) { + count = argc - 3; + } else { + sscanf(argv[3], "%X", &count); + } + printf("Count = %X\n", count); + + i2c_msg = malloc(sizeof(i2c_msg) + count * sizeof(char)); + + i2c_msg->addr = address; + i2c_msg->len = count; + + for (i = 0; i < count; i++) { + i2c_msg->data[i] = i; + } + + if (direction) { + // Write + + for (i=0; i<count; i++) { + sscanf(argv[3+i], "%X", &tmp); + i2c_msg->data[i] = tmp; + } + + ret = ioctl(fp, USRP_E_I2C_WRITE, i2c_msg); + printf("Return value from i2c_write ioctl: %d\n", ret); + } else { + // Read + + ret = ioctl(fp, USRP_E_I2C_READ, i2c_msg); + printf("Return value from i2c_read ioctl: %d\n", ret); + + printf("Ioctl: %d Data read :", ret); + for (i=0; i<count; i++) { + printf(" %X", i2c_msg->data[i]); + } + printf("\n"); + + } + return 0; +} diff --git a/host/utils/usrp-e-loopback.c b/host/utils/usrp-e-loopback.c new file mode 100644 index 000000000..454d81ba7 --- /dev/null +++ b/host/utils/usrp-e-loopback.c @@ -0,0 +1,231 @@ +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <sys/mman.h> +#include <linux/usrp_e.h> + +#define MAX_PACKET_SIZE 1016 +static int packet_data_length; +static int error; + +struct pkt { + int len; + int checksum; + int seq_num; + short data[]; +}; + +static int length_array[2048]; +static int length_array_tail = 0; +static int length_array_head = 0; + +pthread_mutex_t length_array_mutex; //gotta lock the index to keep it from getting hosed + +//yes this is a circular buffer that does not check empty +//no i don't want to hear about it +void push_length_array(int length) { + pthread_mutex_lock(&length_array_mutex); + if(length_array_tail > 2047) length_array_tail = 0; + length_array[length_array_tail++] = length; + pthread_mutex_unlock(&length_array_mutex); +} + +int pop_length_array(void) { + int retval; + pthread_mutex_lock(&length_array_mutex); + if(length_array_head > 2047) length_array_head = 0; + retval = length_array[length_array_head++]; + pthread_mutex_unlock(&length_array_mutex); + return retval; +} + +static int fp; + +static int calc_checksum(struct pkt *p) +{ + int i, sum; + + i = 0; + sum = 0; + + for (i=0; i < p->len; i++) + sum ^= p->data[i]; + + sum ^= p->seq_num; + sum ^= p->len; + + return sum; +} + +static void *read_thread(void *threadid) +{ + char *rx_data; + int cnt, prev_seq_num, pkt_count, seq_num_failure; + struct pkt *p; + unsigned long bytes_transfered, elapsed_seconds; + struct timeval start_time, finish_time; + int expected_count; + + printf("Greetings from the reading thread!\n"); + + bytes_transfered = 0; + gettimeofday(&start_time, NULL); + + // IMPORTANT: must assume max length packet from fpga + rx_data = malloc(2048); + p = (struct pkt *) ((void *)rx_data); + + prev_seq_num = 0; + pkt_count = 0; + seq_num_failure = 0; + + while (1) { + + cnt = read(fp, rx_data, 2048); + if (cnt < 0) + printf("Error returned from read: %d, sequence number = %d\n", cnt, p->seq_num); + +// printf("p->seq_num = %d\n", p->seq_num); + + + pkt_count++; + + if (p->seq_num != prev_seq_num + 1) { + printf("Sequence number fail, current = %d, previous = %d, pkt_count = %d\n", + p->seq_num, prev_seq_num, pkt_count); + + seq_num_failure ++; + if (seq_num_failure > 2) + error = 1; + } + + expected_count = pop_length_array()*2+12; + if(cnt != expected_count) { + printf("Received %d bytes, expected %d\n", cnt, expected_count); + } + + prev_seq_num = p->seq_num; + + if (calc_checksum(p) != p->checksum) { + printf("Checksum fail packet = %X, expected = %X, pkt_count = %d\n", + calc_checksum(p), p->checksum, pkt_count); + error = 1; + } + + bytes_transfered += cnt; + + if (bytes_transfered > (100 * 1000000)) { + gettimeofday(&finish_time, NULL); + elapsed_seconds = finish_time.tv_sec - start_time.tv_sec; + + printf("RX data transfer rate = %f K Samples/second\n", + (float) bytes_transfered / (float) elapsed_seconds / 4000); + + + start_time = finish_time; + bytes_transfered = 0; + } + + +// printf("."); +// fflush(stdout); +// printf("\n"); + } + +} + +static void *write_thread(void *threadid) +{ + int seq_number, i, cnt; + void *tx_data; + struct pkt *p; + + printf("Greetings from the write thread!\n"); + + tx_data = malloc(2048); + p = (struct pkt *) ((void *)tx_data); + + for (i=0; i < packet_data_length; i++) +// p->data[i] = random() >> 16; + p->data[i] = i; + + seq_number = 1; + + while (1) { + p->seq_num = seq_number++; + + if (packet_data_length > 0) + p->len = packet_data_length; + else + p->len = (random()<<1 & 0x1ff) + (1004 - 512); + + push_length_array(p->len); + + p->checksum = calc_checksum(p); + + cnt = write(fp, tx_data, p->len * 2 + 12); + if (cnt < 0) + printf("Error returned from write: %d\n", cnt); +// sleep(1); + } +} + + +int main(int argc, char *argv[]) +{ + pthread_t tx, rx; + pthread_mutex_init(&length_array_mutex, 0); + long int t; + struct sched_param s = { + .sched_priority = 1 + }; + void *rb; + struct usrp_transfer_frame *tx_rb, *rx_rb; + + if (argc < 2) { + printf("%s data_size\n", argv[0]); + return -1; + } + + packet_data_length = atoi(argv[1]); + if(packet_data_length > MAX_PACKET_SIZE) { + printf("Packet size must be smaller than %i\n", MAX_PACKET_SIZE); + exit(-1); + } + + fp = open("/dev/usrp_e0", O_RDWR); + printf("fp = %d\n", fp); + + rb = mmap(0, 202 * 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fp, 0); + if (!rb) { + printf("mmap failed\n"); + exit; + } + + + sched_setscheduler(0, SCHED_RR, &s); + error = 0; + +#if 1 + if (pthread_create(&rx, NULL, read_thread, (void *) t)) { + printf("Failed to create rx thread\n"); + exit(-1); + } + + sleep(1); +#endif + + if (pthread_create(&tx, NULL, write_thread, (void *) t)) { + printf("Failed to create tx thread\n"); + exit(-1); + } + +// while (!error) + sleep(1000000000); + + printf("Done sleeping\n"); +} diff --git a/host/utils/usrp-e-spi.c b/host/utils/usrp-e-spi.c new file mode 100644 index 000000000..5203f56a8 --- /dev/null +++ b/host/utils/usrp-e-spi.c @@ -0,0 +1,54 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#include <linux/usrp_e.h> + +// Usage: usrp_e_spi w|rb slave data + +int main(int argc, char *argv[]) +{ + int fp, slave, length, ret; + unsigned int data; + struct usrp_e_spi spi_dat; + + if (argc < 5) { + printf("Usage: usrp_e_spi w|rb slave transfer_length data\n"); + exit(-1); + } + + slave = atoi(argv[2]); + length = atoi(argv[3]); + data = atoll(argv[4]); + + printf("Data = %X\n", data); + + fp = open("/dev/usrp_e0", O_RDWR); + printf("fp = %d\n", fp); + if (fp < 0) { + perror("Open failed"); + return -1; + } + +// sleep(1); + + + spi_dat.slave = slave; + spi_dat.data = data; + spi_dat.length = length; + spi_dat.flags = UE_SPI_PUSH_FALL | UE_SPI_LATCH_RISE; + + if (*argv[1] == 'r') { + spi_dat.readback = 1; + ret = ioctl(fp, USRP_E_SPI, &spi_dat); + printf("Ioctl returns: %d, Data returned = %d\n", ret, spi_dat.data); + } else { + spi_dat.readback = 0; + ioctl(fp, USRP_E_SPI, &spi_dat); + } + + return 0; +} diff --git a/host/utils/usrp1_init_eeprom.cpp b/host/utils/usrp1_init_eeprom.cpp new file mode 100644 index 000000000..b05e400b1 --- /dev/null +++ b/host/utils/usrp1_init_eeprom.cpp @@ -0,0 +1,69 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils/safe_main.hpp> +#include <uhd/device.hpp> +#include <uhd/usrp/device_props.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("image", po::value<std::string>(), "BIN image file") + ; + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //print the help message + if (vm.count("help")){ + std::cout << boost::format("USRP EEPROM initialization %s") % desc << std::endl; + return ~0; + } + + //load the options into the address + uhd::device_addr_t device_addr; + device_addr["type"] = "usrp1"; + device_addr["uninit"] = "yeah"; //tell find to look for an uninitialized FX2 + + //find and create a control transport to do the writing. + + uhd::device_addrs_t found_addrs = uhd::device::find(device_addr); + + if (found_addrs.size() == 0){ + std::cerr << "No uninitialized USRP devices found" << std::endl; + return ~0; + } + + for (size_t i = 0; i < found_addrs.size(); i++){ + std::cout << "Writing EEPROM data..." << std::endl; + //uhd::device_addrs_t devs = uhd::device::find(found_addrs[i]); + uhd::device::sptr dev = uhd::device::make(found_addrs[i]); + wax::obj mb = (*dev)[uhd::usrp::DEVICE_PROP_MBOARD]; + mb[std::string("load_eeprom")] = vm["image"].as<std::string>(); + } + + + std::cout << "Power-cycle the usrp for the changes to take effect." << std::endl; + return 0; +} diff --git a/host/utils/usrp2_card_burner.py b/host/utils/usrp2_card_burner.py new file mode 100755 index 000000000..1db5e59ce --- /dev/null +++ b/host/utils/usrp2_card_burner.py @@ -0,0 +1,246 @@ +#!/usr/bin/env python +# +# Copyright 2010 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import platform +import tempfile +import subprocess +import urllib +import optparse +import math +import os +import re + +######################################################################## +# constants +######################################################################## +SECTOR_SIZE = 512 # bytes +MAX_FILE_SIZE = 1 * (2**20) # maximum number of bytes we'll burn to a slot + +FPGA_OFFSET = 0 # offset in flash to fpga image +FIRMWARE_OFFSET = 1 * (2**20) # offset in flash to firmware image + +MAX_SD_CARD_SIZE = 2048e6 # bytes (any bigger is sdhc) + +######################################################################## +# helper functions +######################################################################## +def command(*args): + p = subprocess.Popen( + args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + ret = p.wait() + verbose = p.stdout.read() + if ret != 0: raise Exception, verbose + return verbose + +def get_dd_path(): + if platform.system() == 'Windows': + dd_path = os.path.join(tempfile.gettempdir(), 'dd.exe') + if not os.path.exists(dd_path): + print 'Downloading dd.exe to %s'%dd_path + dd_bin = urllib.urlopen('http://www.ettus.com/downloads/dd.exe').read() + open(dd_path, 'wb').write(dd_bin) + return dd_path + return 'dd' + +def int_ceil_div(num, den): + return int(math.ceil(float(num)/float(den))) + +def get_tmp_file(): + tmp = tempfile.mkstemp() + os.close(tmp[0]) + return tmp[1] + +######################################################################## +# list possible devices +######################################################################## +def get_raw_device_hints(): + #################################################################### + # Platform Windows: parse the output of dd.exe --list + #################################################################### + if platform.system() == 'Windows': + def extract_info_value(info, key): + return info.split(key)[-1].split()[0] + def get_info_list(output): + in_info = False + for line in output.splitlines(): + if line.startswith('\\\\'): in_info = True; info = '' + elif in_info and not line.strip(): in_info = False; yield info + if in_info: info += '\n'+line.strip() + def is_info_valid(info): + try: + assert 'link to' in info + #handles two spellings of remov(e)able: + assert 'remov' in info.lower() + if 'size is' in info: assert int(extract_info_value(info, 'size is')) <= MAX_SD_CARD_SIZE + return True + except: return False + def extract_info_name(info): + for key in ('Mounted on', 'link to'): + if key in info: return extract_info_value(info, key) + return info.splitlines()[0].strip() + + return sorted(set(map(extract_info_name, filter(is_info_valid, get_info_list(command(get_dd_path(), '--list')))))) + + #################################################################### + # Platform Linux: parse procfs /proc/partitions + #################################################################### + if platform.system() == 'Linux': + devs = list() + try: output = open('/proc/partitions', 'r').read() + except: return devs + for line in output.splitlines(): + try: + major, minor, blocks, name = line.split() + assert not name[-1].isdigit() or int(minor) == 0 + assert int(blocks)*1024 <= MAX_SD_CARD_SIZE + except: continue + devs.append(os.path.join('/dev', name)) + + return sorted(set(devs)) + + #################################################################### + # Platform Mac OS X: parse diskutil list and info commands + #################################################################### + if platform.system() == 'Darwin': + devs = map(lambda d: d.split()[0], filter(lambda l: l.startswith('/dev'), command('diskutil', 'list').splitlines())) + def output_to_info(output): + return dict([map(str.strip, pair.lower().split(':')) for pair in filter(lambda l: ':' in l, output.splitlines())]) + def is_dev_valid(dev): + info = output_to_info(command('diskutil', 'info', dev)) + try: + if info.has_key('internal'): assert info['internal'] == 'no' + if info.has_key('ejectable'): assert info['ejectable'] == 'yes' + if info.has_key('total size'): + size_match = re.match('^.*\((\d+)\s*bytes\).*$', info['total size']) + if size_match: assert int(size_match.groups()[0]) <= MAX_SD_CARD_SIZE + return True + except: return False + + return sorted(set(filter(is_dev_valid, devs))) + + #################################################################### + # Platform Others: + #################################################################### + return () + +######################################################################## +# write and verify with dd +######################################################################## +def verify_image(image_file, device_file, offset): + #create a temporary file to store the readback image + tmp_file = get_tmp_file() + + #read the image data + img_data = open(image_file, 'rb').read() + count = int_ceil_div(len(img_data), SECTOR_SIZE) + + #execute a dd subprocess + verbose = command( + get_dd_path(), + "of=%s"%tmp_file, + "if=%s"%device_file, + "skip=%d"%(offset/SECTOR_SIZE), + "bs=%d"%SECTOR_SIZE, + "count=%d"%count, + ) + + #verfy the data + tmp_data = open(tmp_file, 'rb').read(len(img_data)) + if img_data != tmp_data: return 'Verification Failed:\n%s'%verbose + return 'Verification Passed:\n%s'%verbose + +def write_image(image_file, device_file, offset): + #create a temporary file to store the padded image + tmp_file = get_tmp_file() + + #write the padded image data + img_data = open(image_file, 'rb').read() + count = int_ceil_div(len(img_data), SECTOR_SIZE) + pad_len = SECTOR_SIZE*count - len(img_data) + pad_str = ''.join([chr(0)]*pad_len) #zero-padding + open(tmp_file, 'wb').write(img_data + pad_str) + + #execute a dd subprocess + verbose = command( + get_dd_path(), + "if=%s"%tmp_file, + "of=%s"%device_file, + "seek=%d"%(offset/SECTOR_SIZE), + "bs=%d"%SECTOR_SIZE, + "count=%d"%count, + ) + + try: #exec the sync command (only works on linux) + if platform.system() == 'Linux': command('sync') + except: pass + + return verbose + +def write_and_verify(image_file, device_file, offset): + if os.path.getsize(image_file) > MAX_FILE_SIZE: + raise Exception, 'Image file larger than %d bytes!'%MAX_FILE_SIZE + return '%s\n%s'%( + write_image( + image_file=image_file, + device_file=device_file, + offset=offset, + ), verify_image( + image_file=image_file, + device_file=device_file, + offset=offset, + ), + ) + +def burn_sd_card(dev, fw, fpga): + verbose = '' + if fw: verbose += 'Burn firmware image:\n%s\n'%write_and_verify( + image_file=fw, device_file=dev, offset=FIRMWARE_OFFSET + ) + if fpga: verbose += 'Burn fpga image:\n%s\n'%write_and_verify( + image_file=fpga, device_file=dev, offset=FPGA_OFFSET + ) + return verbose + +######################################################################## +# command line options +######################################################################## +def get_options(): + parser = optparse.OptionParser() + parser.add_option("--dev", type="string", help="raw device path", default='') + parser.add_option("--fw", type="string", help="firmware image path (optional)", default='') + parser.add_option("--fpga", type="string", help="fpga image path (optional)", default='') + parser.add_option("--list", action="store_true", help="list possible raw devices", default=False) + (options, args) = parser.parse_args() + + if options.list: + print 'Possible raw devices:' + print ' ' + '\n '.join(get_raw_device_hints()) + exit() + + return options + +######################################################################## +# main +######################################################################## +if __name__=='__main__': + options = get_options() + if not options.dev: raise Exception, 'no raw device path specified' + print burn_sd_card(dev=options.dev, fw=options.fw, fpga=options.fpga) diff --git a/host/utils/usrp2_card_burner_gui.py b/host/utils/usrp2_card_burner_gui.py new file mode 100755 index 000000000..58b7a514a --- /dev/null +++ b/host/utils/usrp2_card_burner_gui.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python +# +# Copyright 2010 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import usrp2_card_burner #import implementation +import Tkinter, tkFileDialog, tkFont, tkMessageBox +import os + +class BinFileEntry(Tkinter.Frame): + """ + Simple file entry widget for getting the file path of bin files. + Combines a label, entry, and button with file dialog callback. + """ + + def __init__(self, root, what, def_path=''): + self._what = what + Tkinter.Frame.__init__(self, root) + Tkinter.Label(self, text=what+":").pack(side=Tkinter.LEFT) + self._entry = Tkinter.Entry(self, width=50) + self._entry.insert(Tkinter.END, def_path) + self._entry.pack(side=Tkinter.LEFT) + Tkinter.Button(self, text="...", command=self._button_cb).pack(side=Tkinter.LEFT) + + def _button_cb(self): + filename = tkFileDialog.askopenfilename( + parent=self, + filetypes=[('bin files', '*.bin'), ('all files', '*.*')], + title="Select bin file for %s"%self._what, + initialdir=os.path.dirname(self.get_filename()), + ) + + # open file on your own + if filename: + self._entry.delete(0, Tkinter.END) + self._entry.insert(0, filename) + + def get_filename(self): + return self._entry.get() + +class DeviceEntryWidget(Tkinter.Frame): + """ + Simple entry widget for getting the raw device name. + Combines a label, entry, and helpful text box with hints. + """ + + def __init__(self, root, text=''): + Tkinter.Frame.__init__(self, root) + + Tkinter.Button(self, text="Rescan for Devices", command=self._reload_cb).pack() + + self._hints = Tkinter.Listbox(self) + self._hints.bind("<<ListboxSelect>>", self._listbox_cb) + self._reload_cb() + self._hints.pack(expand=Tkinter.YES, fill=Tkinter.X) + + frame = Tkinter.Frame(self) + frame.pack() + + Tkinter.Label(frame, text="Raw Device:").pack(side=Tkinter.LEFT) + self._entry = Tkinter.Entry(frame, width=50) + self._entry.insert(Tkinter.END, text) + self._entry.pack(side=Tkinter.LEFT) + + def _reload_cb(self): + self._hints.delete(0, Tkinter.END) + for hint in usrp2_card_burner.get_raw_device_hints(): + self._hints.insert(Tkinter.END, hint) + + def _listbox_cb(self, event): + try: + sel = self._hints.get(self._hints.curselection()[0]) + self._entry.delete(0, Tkinter.END) + self._entry.insert(0, sel) + except Exception, e: print e + + def get_devname(self): + return self._entry.get() + +class SectionLabel(Tkinter.Label): + """ + Make a text label with bold font. + """ + + def __init__(self, root, text): + Tkinter.Label.__init__(self, root, text=text) + + #set the font bold + f = tkFont.Font(font=self['font']) + f['weight'] = 'bold' + self['font'] = f.name + +class USRP2CardBurnerApp(Tkinter.Frame): + """ + The top level gui application for the usrp2 sd card burner. + Creates entry widgets and button with callback to write images. + """ + + def __init__(self, root, dev, fw, fpga): + + Tkinter.Frame.__init__(self, root) + + #pack the file entry widgets + SectionLabel(self, text="Select Images").pack(pady=5) + self._fw_img_entry = BinFileEntry(self, "Firmware Image", def_path=fw) + self._fw_img_entry.pack() + self._fpga_img_entry = BinFileEntry(self, "FPGA Image", def_path=fpga) + self._fpga_img_entry.pack() + + #pack the destination entry widget + SectionLabel(self, text="Select Device").pack(pady=5) + self._raw_dev_entry = DeviceEntryWidget(self, text=dev) + self._raw_dev_entry.pack() + + #the do it button + SectionLabel(self, text="").pack(pady=5) + Tkinter.Label(self, text="Warning! This tool can overwrite your hard drive. Use with caution.").pack() + Tkinter.Button(self, text="Burn SD Card", command=self._burn).pack() + + def _burn(self): + #grab strings from the gui + fw = self._fw_img_entry.get_filename() + fpga = self._fpga_img_entry.get_filename() + dev = self._raw_dev_entry.get_devname() + + #check input + if not dev: + tkMessageBox.showerror('Error:', 'No device specified!') + return + if not fw and not fpga: + tkMessageBox.showerror('Error:', 'No images specified!') + return + if fw and not os.path.exists(fw): + tkMessageBox.showerror('Error:', 'Firmware image not found!') + return + if fpga and not os.path.exists(fpga): + tkMessageBox.showerror('Error:', 'FPGA image not found!') + return + + #burn the sd card + try: + verbose = usrp2_card_burner.burn_sd_card(dev=dev, fw=fw, fpga=fpga) + tkMessageBox.showinfo('Verbose:', verbose) + except Exception, e: + tkMessageBox.showerror('Verbose:', 'Error: %s'%str(e)) + +######################################################################## +# main +######################################################################## +if __name__=='__main__': + options = usrp2_card_burner.get_options() + root = Tkinter.Tk() + root.title('USRP2 SD Card Burner') + USRP2CardBurnerApp(root, dev=options.dev, fw=options.fw, fpga=options.fpga).pack() + root.mainloop() + exit() diff --git a/host/utils/usrp2_recovery.py b/host/utils/usrp2_recovery.py new file mode 100755 index 000000000..5654e93d3 --- /dev/null +++ b/host/utils/usrp2_recovery.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# +# Copyright 2010 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +""" +The usrp2 recovery app: + +When the usrp2 has an unknown or bad ip address in its eeprom, +it may not be possible to communicate with the usrp2 over ip/udp. + +This app will send a raw ethernet packet to bypass the ip layer. +The packet will contain a known ip address to burn into eeprom. +Because the recovery packet is sent with a broadcast mac address, +only one usrp2 should be present on the interface upon execution. + +This app requires super-user privileges and only works on linux. +""" + +import socket +import struct +import optparse + +BCAST_MAC_ADDR = 'ff:ff:ff:ff:ff:ff' +RECOVERY_ETHERTYPE = 0xbeee +IP_RECOVERY_CODE = 'addr' + +def mac_addr_repr_to_binary_string(mac_addr): + return ''.join(map(lambda x: chr(int(x, 16)), mac_addr.split(':'))) + +if __name__ == '__main__': + parser = optparse.OptionParser(usage='usage: %prog [options]\n'+__doc__) + parser.add_option('--ifc', type='string', help='ethernet interface name [default=%default]', default='eth0') + parser.add_option('--new-ip', type='string', help='ip address to set [default=%default]', default='192.168.10.2') + (options, args) = parser.parse_args() + + #create the raw socket + print "Opening raw socket on interface:", options.ifc + soc = socket.socket(socket.PF_PACKET, socket.SOCK_RAW) + soc.bind((options.ifc, RECOVERY_ETHERTYPE)) + + #create the recovery packet + print "Loading packet with ip address:", options.new_ip + packet = struct.pack( + '!6s6sH4s4s', + mac_addr_repr_to_binary_string(BCAST_MAC_ADDR), + mac_addr_repr_to_binary_string(BCAST_MAC_ADDR), + RECOVERY_ETHERTYPE, + IP_RECOVERY_CODE, + socket.inet_aton(options.new_ip), + ) + + print "Sending packet (%d bytes)"%len(packet) + soc.send(packet) + print "Done" diff --git a/host/utils/usrp_burn_db_eeprom.cpp b/host/utils/usrp_burn_db_eeprom.cpp new file mode 100644 index 000000000..9afd71a22 --- /dev/null +++ b/host/utils/usrp_burn_db_eeprom.cpp @@ -0,0 +1,100 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +#include <uhd/utils/safe_main.hpp> +#include <uhd/device.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/usrp/dboard_id.hpp> +#include <uhd/usrp/device_props.hpp> +#include <uhd/usrp/mboard_props.hpp> +#include <uhd/usrp/dboard_props.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/assign.hpp> +#include <iostream> + +using namespace uhd; +using namespace uhd::usrp; +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + //command line variables + std::string args, slot, unit; + static const uhd::dict<std::string, mboard_prop_t> unit_to_db_prop = boost::assign::map_list_of + ("RX", MBOARD_PROP_RX_DBOARD) ("TX", MBOARD_PROP_TX_DBOARD) + ; + static const uhd::dict<std::string, mboard_prop_t> unit_to_db_names_prop = boost::assign::map_list_of + ("RX", MBOARD_PROP_RX_DBOARD_NAMES) ("TX", MBOARD_PROP_TX_DBOARD_NAMES) + ; + + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "device address args [default = \"\"]") + ("slot", po::value<std::string>(&slot)->default_value(""), "dboard slot name [default is blank for automatic]") + ("unit", po::value<std::string>(&unit)->default_value(""), "which unit [RX or TX]") + ("id", po::value<std::string>(), "dboard id to burn, omit for readback") + ; + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //print the help message + if (vm.count("help")){ + std::cout << boost::format("USRP Burn Daughterboard EEPROM %s") % desc << std::endl; + std::cout << boost::format( + "Omit the ID argument to perform readback,\n" + "Or specify a new ID to burn into the EEPROM.\n" + ) << std::endl; + return ~0; + } + + //check inputs + if (not unit_to_db_prop.has_key(unit)){ + std::cout << "Error: specify RX or TX for unit" << std::endl; + return ~0; + } + + //make the device and extract the dboard w/ property + device::sptr dev = device::make(args); + uhd::prop_names_t dboard_names = (*dev)[DEVICE_PROP_MBOARD][unit_to_db_names_prop[unit]].as<uhd::prop_names_t>(); + if (dboard_names.size() == 1 and slot.empty()) slot = dboard_names.front(); + uhd::assert_has(dboard_names, slot, "dboard slot name"); + wax::obj dboard = (*dev)[DEVICE_PROP_MBOARD][named_prop_t(unit_to_db_prop[unit], slot)]; + std::string prefix = unit + ":" + slot; + + //read the current dboard id from eeprom + if (vm.count("id") == 0){ + std::cout << boost::format("Getting dbid on %s dboard...") % prefix << std::endl; + dboard_id_t id = dboard[DBOARD_PROP_DBOARD_ID].as<dboard_id_t>(); + std::cout << boost::format(" Current dbid: %s") % id.to_pp_string() << std::endl; + } + + //write a new dboard id to eeprom + else{ + dboard_id_t id = dboard_id_t::from_string(vm["id"].as<std::string>()); + std::cout << boost::format("Setting dbid on %s dboard...") % prefix << std::endl; + std::cout << boost::format(" New dbid: %s") % id.to_pp_string() << std::endl; + dboard[DBOARD_PROP_DBOARD_ID] = id; + } + + std::cout << " Done" << std::endl << std::endl; + return 0; +} diff --git a/host/utils/usrp_burn_mb_eeprom.cpp b/host/utils/usrp_burn_mb_eeprom.cpp new file mode 100644 index 000000000..20e1b58b1 --- /dev/null +++ b/host/utils/usrp_burn_mb_eeprom.cpp @@ -0,0 +1,81 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils/safe_main.hpp> +#include <uhd/device.hpp> +#include <uhd/usrp/device_props.hpp> +#include <uhd/usrp/mboard_props.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + std::string args, key, val; + + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "device address args [default = \"\"]") + ("key", po::value<std::string>(&key), "the indentifier for a value in EEPROM") + ("val", po::value<std::string>(&val), "the new value to set, omit for readback") + ; + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //print the help message + if (vm.count("help") or not vm.count("key")){ + std::cout << boost::format("USRP Burn Motherboard EEPROM %s") % desc << std::endl; + std::cout << boost::format( + "Omit the value argument to perform a readback,\n" + "Or specify a new value to burn into the EEPROM.\n" + ) << std::endl; + return ~0; + } + + std::cout << "Creating USRP device from address: " + args << std::endl; + uhd::device::sptr dev = uhd::device::make(args); + //FIXME the default mboard for now (may be others) + wax::obj mboard = (*dev)[uhd::usrp::DEVICE_PROP_MBOARD]; + std::cout << std::endl; + + if (true /*always readback*/){ + std::cout << "Fetching current settings from EEPROM..." << std::endl; + uhd::usrp::mboard_eeprom_t mb_eeprom = \ + mboard[uhd::usrp::MBOARD_PROP_EEPROM_MAP].as<uhd::usrp::mboard_eeprom_t>(); + if (not mb_eeprom.has_key(key)){ + std::cerr << boost::format("Cannot find value for EEPROM[%s]") % key << std::endl; + return ~0; + } + std::cout << boost::format(" EEPROM [\"%s\"] is \"%s\"") % key % mb_eeprom[key] << std::endl; + std::cout << std::endl; + } + if (vm.count("val")){ + uhd::usrp::mboard_eeprom_t mb_eeprom; mb_eeprom[key] = val; + std::cout << boost::format("Setting EEPROM [\"%s\"] to \"%s\"...") % key % val << std::endl; + mboard[uhd::usrp::MBOARD_PROP_EEPROM_MAP] = mb_eeprom; + std::cout << "Power-cycle the USRP device for the changes to take effect." << std::endl; + std::cout << std::endl; + } + + std::cout << "Done" << std::endl; + return 0; +} diff --git a/host/utils/usrp_e_regs.hpp b/host/utils/usrp_e_regs.hpp new file mode 100644 index 000000000..a4f42093e --- /dev/null +++ b/host/utils/usrp_e_regs.hpp @@ -0,0 +1,196 @@ + + +//////////////////////////////////////////////////////////////// +// +// Memory map for embedded wishbone bus +// +//////////////////////////////////////////////////////////////// + +// All addresses are byte addresses. All accesses are word (16-bit) accesses. +// This means that address bit 0 is usually 0. +// There are 11 bits of address for the control. + +#ifndef __USRP_E_REGS_H +#define __USRP_E_REGS_H + +///////////////////////////////////////////////////// +// Slave pointers + +#define UE_REG_SLAVE(n) ((n)<<7) +#define UE_REG_SR_ADDR(n) ((UE_REG_SLAVE(5)) + (4*(n))) + +///////////////////////////////////////////////////// +// Slave 0 -- Misc Regs + +#define UE_REG_MISC_BASE UE_REG_SLAVE(0) + +#define UE_REG_MISC_LED UE_REG_MISC_BASE + 0 +#define UE_REG_MISC_SW UE_REG_MISC_BASE + 2 +#define UE_REG_MISC_CGEN_CTRL UE_REG_MISC_BASE + 4 +#define UE_REG_MISC_CGEN_ST UE_REG_MISC_BASE + 6 +#define UE_REG_MISC_TEST UE_REG_MISC_BASE + 8 +#define UE_REG_MISC_RX_LEN UE_REG_MISC_BASE + 10 +#define UE_REG_MISC_TX_LEN UE_REG_MISC_BASE + 12 + +///////////////////////////////////////////////////// +// Slave 1 -- UART +// CLKDIV is 16 bits, others are only 8 + +#define UE_REG_UART_BASE UE_REG_SLAVE(1) + +#define UE_REG_UART_CLKDIV UE_REG_UART_BASE + 0 +#define UE_REG_UART_TXLEVEL UE_REG_UART_BASE + 2 +#define UE_REG_UART_RXLEVEL UE_REG_UART_BASE + 4 +#define UE_REG_UART_TXCHAR UE_REG_UART_BASE + 6 +#define UE_REG_UART_RXCHAR UE_REG_UART_BASE + 8 + +///////////////////////////////////////////////////// +// Slave 2 -- SPI Core +// This should be accessed through the IOCTL +// Users should not touch directly + +#define UE_REG_SPI_BASE UE_REG_SLAVE(2) + +//spi slave constants +#define UE_SPI_SS_AD9522 (1 << 3) +#define UE_SPI_SS_AD9862 (1 << 2) +#define UE_SPI_SS_TX_DB (1 << 1) +#define UE_SPI_SS_RX_DB (1 << 0) + +//////////////////////////////////////////////// +// Slave 3 -- I2C Core +// This should be accessed through the IOCTL +// Users should not touch directly + +#define UE_REG_I2C_BASE UE_REG_SLAVE(3) + + +//////////////////////////////////////////////// +// Slave 4 -- GPIO + +#define UE_REG_GPIO_BASE UE_REG_SLAVE(4) + +#define UE_REG_GPIO_RX_IO UE_REG_GPIO_BASE + 0 +#define UE_REG_GPIO_TX_IO UE_REG_GPIO_BASE + 2 +#define UE_REG_GPIO_RX_DDR UE_REG_GPIO_BASE + 4 +#define UE_REG_GPIO_TX_DDR UE_REG_GPIO_BASE + 6 +#define UE_REG_GPIO_RX_SEL UE_REG_GPIO_BASE + 8 +#define UE_REG_GPIO_TX_SEL UE_REG_GPIO_BASE + 10 +#define UE_REG_GPIO_RX_DBG UE_REG_GPIO_BASE + 12 +#define UE_REG_GPIO_TX_DBG UE_REG_GPIO_BASE + 14 + +//possible bit values for sel when dbg is 0: +#define GPIO_SEL_SW 0 // if pin is an output, set by software in the io reg +#define GPIO_SEL_ATR 1 // if pin is an output, set by ATR logic + +//possible bit values for sel when dbg is 1: +#define GPIO_SEL_DEBUG_0 0 // if pin is an output, debug lines from FPGA fabric +#define GPIO_SEL_DEBUG_1 1 // if pin is an output, debug lines from FPGA fabric + + +//////////////////////////////////////////////////// +// Slave 5 -- Settings Bus +// +// Output-only, no readback, 32 registers total +// Each register must be written 32 bits at a time +// First the address xxx_xx00 and then xxx_xx10 + +#define UE_REG_SETTINGS_BASE UE_REG_SLAVE(5) + +/////////////////////////////////////////////////// +// Slave 6 -- ATR Controller +// 16 regs + +#define UE_REG_ATR_BASE UE_REG_SLAVE(6) + +#define UE_REG_ATR_IDLE_RXSIDE UE_REG_ATR_BASE + 0 +#define UE_REG_ATR_IDLE_TXSIDE UE_REG_ATR_BASE + 2 +#define UE_REG_ATR_INTX_RXSIDE UE_REG_ATR_BASE + 4 +#define UE_REG_ATR_INTX_TXSIDE UE_REG_ATR_BASE + 6 +#define UE_REG_ATR_INRX_RXSIDE UE_REG_ATR_BASE + 8 +#define UE_REG_ATR_INRX_TXSIDE UE_REG_ATR_BASE + 10 +#define UE_REG_ATR_FULL_RXSIDE UE_REG_ATR_BASE + 12 +#define UE_REG_ATR_FULL_TXSIDE UE_REG_ATR_BASE + 14 + +///////////////////////////////////////////////// +// DSP RX Regs +//////////////////////////////////////////////// +#define UE_REG_DSP_RX_FREQ UE_REG_SR_ADDR(0) +#define UE_REG_DSP_RX_SCALE_IQ UE_REG_SR_ADDR(1) // {scale_i,scale_q} +#define UE_REG_DSP_RX_DECIM_RATE UE_REG_SR_ADDR(2) // hb and decim rate +#define UE_REG_DSP_RX_DCOFFSET_I UE_REG_SR_ADDR(3) // Bit 31 high sets fixed offset mode, using lower 14 bits, // otherwise it is automatic +#define UE_REG_DSP_RX_DCOFFSET_Q UE_REG_SR_ADDR(4) // Bit 31 high sets fixed offset mode, using lower 14 bits +#define UE_REG_DSP_RX_MUX UE_REG_SR_ADDR(5) + +/////////////////////////////////////////////////// +// VITA RX CTRL regs +/////////////////////////////////////////////////// +// The following 3 are logically a single command register. +// They are clocked into the underlying fifo when time_ticks is written. +#define UE_REG_CTRL_RX_STREAM_CMD UE_REG_SR_ADDR(8) // {now, chain, num_samples(30) +#define UE_REG_CTRL_RX_TIME_SECS UE_REG_SR_ADDR(9) +#define UE_REG_CTRL_RX_TIME_TICKS UE_REG_SR_ADDR(10) +#define UE_REG_CTRL_RX_CLEAR_OVERRUN UE_REG_SR_ADDR(11) // write anything to clear overrun +#define UE_REG_CTRL_RX_VRT_HEADER UE_REG_SR_ADDR(12) // word 0 of packet. FPGA fills in packet counter +#define UE_REG_CTRL_RX_VRT_STREAM_ID UE_REG_SR_ADDR(13) // word 1 of packet. +#define UE_REG_CTRL_RX_VRT_TRAILER UE_REG_SR_ADDR(14) +#define UE_REG_CTRL_RX_NSAMPS_PER_PKT UE_REG_SR_ADDR(15) +#define UE_REG_CTRL_RX_NCHANNELS UE_REG_SR_ADDR(16) // 1 in basic case, up to 4 for vector sources + +///////////////////////////////////////////////// +// DSP TX Regs +//////////////////////////////////////////////// +#define UE_REG_DSP_TX_FREQ UE_REG_SR_ADDR(17) +#define UE_REG_DSP_TX_SCALE_IQ UE_REG_SR_ADDR(18) // {scale_i,scale_q} +#define UE_REG_DSP_TX_INTERP_RATE UE_REG_SR_ADDR(19) +#define UE_REG_DSP_TX_UNUSED UE_REG_SR_ADDR(20) +#define UE_REG_DSP_TX_MUX UE_REG_SR_ADDR(21) + +///////////////////////////////////////////////// +// VITA TX CTRL regs +//////////////////////////////////////////////// +#define UE_REG_CTRL_TX_NCHANNELS UE_REG_SR_ADDR(24) +#define UE_REG_CTRL_TX_CLEAR_UNDERRUN UE_REG_SR_ADDR(25) +#define UE_REG_CTRL_TX_REPORT_SID UE_REG_SR_ADDR(26) +#define UE_REG_CTRL_TX_POLICY UE_REG_SR_ADDR(27) + +#define UE_FLAG_CTRL_TX_POLICY_WAIT (0x1 << 0) +#define UE_FLAG_CTRL_TX_POLICY_NEXT_PACKET (0x1 << 1) +#define UE_FLAG_CTRL_TX_POLICY_NEXT_BURST (0x1 << 2) + +///////////////////////////////////////////////// +// VITA49 64 bit time (write only) +//////////////////////////////////////////////// + /*! + * \brief Time 64 flags + * + * <pre> + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------------------------------------------------------+-+-+ + * | |S|P| + * +-----------------------------------------------------------+-+-+ + * + * P - PPS edge selection (0=negedge, 1=posedge, default=0) + * S - Source (0=sma, 1=mimo, 0=default) + * + * </pre> + */ +#define UE_REG_TIME64_SECS UE_REG_SR_ADDR(28) // value to set absolute secs to on next PPS +#define UE_REG_TIME64_TICKS UE_REG_SR_ADDR(29) // value to set absolute ticks to on next PPS +#define UE_REG_TIME64_FLAGS UE_REG_SR_ADDR(30) // flags - see chart above +#define UE_REG_TIME64_IMM UE_REG_SR_ADDR(31) // set immediate (0=latch on next pps, 1=latch immediate, default=0) +#define UE_REG_TIME64_TPS UE_REG_SR_ADDR(31) // clock ticks per second (counter rollover) + +//pps flags (see above) +#define UE_FLAG_TIME64_PPS_NEGEDGE (0 << 0) +#define UE_FLAG_TIME64_PPS_POSEDGE (1 << 0) +#define UE_FLAG_TIME64_PPS_SMA (0 << 1) +#define UE_FLAG_TIME64_PPS_MIMO (1 << 1) + +#define UE_FLAG_TIME64_LATCH_NOW 1 +#define UE_FLAG_TIME64_LATCH_NEXT_PPS 0 + +#endif + diff --git a/host/utils/usrp_n2xx_net_burner.py b/host/utils/usrp_n2xx_net_burner.py new file mode 100755 index 000000000..21327e0af --- /dev/null +++ b/host/utils/usrp_n2xx_net_burner.py @@ -0,0 +1,288 @@ +#!/usr/bin/env python +# +# Copyright 2010 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# TODO: make it autodetect UHD devices +# TODO: you should probably watch sequence numbers + +import optparse +import math +import os +import re +import struct +import socket +import sys + +######################################################################## +# constants +######################################################################## +UDP_FW_UPDATE_PORT = 49154 +UDP_MAX_XFER_BYTES = 1024 +UDP_TIMEOUT = 3 +UDP_POLL_INTERVAL = 0.10 #in seconds + +USRP2_FW_PROTO_VERSION = 7 + +#from bootloader_utils.h + +FPGA_IMAGE_SIZE_BYTES = 1572864 +FW_IMAGE_SIZE_BYTES = 31744 +SAFE_FPGA_IMAGE_LOCATION_ADDR = 0x00000000 +SAFE_FW_IMAGE_LOCATION_ADDR = 0x003F0000 +PROD_FPGA_IMAGE_LOCATION_ADDR = 0x00180000 +PROD_FW_IMAGE_LOCATION_ADDR = 0x00300000 + +FLASH_DATA_PACKET_SIZE = 256 + +#see fw_common.h +FLASH_ARGS_FMT = '!LLLLL256s' +FLASH_INFO_FMT = '!LLLLL256x' +FLASH_IP_FMT = '!LLLL260x' + +class update_id_t: + USRP2_FW_UPDATE_ID_WAT = ord(' ') + USRP2_FW_UPDATE_ID_OHAI_LOL = ord('a') + USRP2_FW_UPDATE_ID_OHAI_OMG = ord('A') + USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = ord('f') + USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = ord('F') + USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = ord('e') + USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = ord('E') + USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = ord('d') + USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = ord('D') + USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = ord('B') + USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = ord('w') + USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = ord('W') + USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = ord('r') + USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = ord('R') + USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = ord('s') + USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = ord('S') + USRP2_FW_UPDATE_ID_KTHXBAI = ord('~') + +_seq = -1 +def seq(): + global _seq + _seq = _seq+1 + return _seq + +######################################################################## +# helper functions +######################################################################## +def unpack_flash_args_fmt(s): + return struct.unpack(FLASH_ARGS_FMT, s) #(proto_ver, pktid, seq, flash_addr, length, data) + +def unpack_flash_info_fmt(s): + return struct.unpack(FLASH_INFO_FMT, s) #(proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes) + +def unpack_flash_ip_fmt(s): + return struct.unpack(FLASH_IP_FMT, s) #(proto_ver, pktid, seq, ip_addr) + +def pack_flash_args_fmt(proto_ver, pktid, seq, flash_addr, length, data): + return struct.pack(FLASH_ARGS_FMT, proto_ver, pktid, seq, flash_addr, length, data) + +def pack_flash_info_fmt(proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes): + return struct.pack(FLASH_INFO_FMT, proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes) + +def send_and_recv(pkt, ip): + update_socket = create_socket() + + try: + update_socket.sendto(pkt, (ip, UDP_FW_UPDATE_PORT)) + except Exception, e: + print e + sys.exit(1) + + try: + (recv_pkt, recv_addr) = update_socket.recvfrom(UDP_MAX_XFER_BYTES) + except Exception, e: + print e + sys.exit(1) + + if recv_addr != (options.ip, UDP_FW_UPDATE_PORT): + raise Exception, "Packet received from invalid IP %s, expected %s" % (recv_addr, options.ip) + + return recv_pkt + +def create_socket(): + socket.setdefaulttimeout(UDP_TIMEOUT) + update_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + return update_socket + +#just here to validate comms +def init_update(ip): + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_OHAI_LOL, seq(), 0, 0, "") + in_pkt = send_and_recv(out_pkt, ip) + (proto_ver, pktid, rxseq, ip_addr) = unpack_flash_ip_fmt(in_pkt) + if pktid == update_id_t.USRP2_FW_UPDATE_ID_OHAI_OMG: + print "USRP2P found." + else: + raise Exception, "Invalid reply received from device." + +# print "Incoming:\n\tVer: %i\n\tID: %c\n\tSeq: %i\n\tIP: %i\n" % (proto_ver, chr(pktid), rxseq, ip_addr) + +def get_flash_info(ip): + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL, seq(), 0, 0, "") + in_pkt = send_and_recv(out_pkt, ip) + + (proto_ver, pktid, rxseq, sector_size_bytes, memory_size_bytes) = unpack_flash_info_fmt(in_pkt) + + if pktid != update_id_t.USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG: + raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + + return (memory_size_bytes, sector_size_bytes) + +def burn_fw(ip, fw, fpga, reset, safe): + init_update(ip) + (flash_size, sector_size) = get_flash_info(ip) + + print "Flash size: %i\nSector size: %i" % (flash_size, sector_size) + + if fpga: + if safe: + image_location = SAFE_FPGA_IMAGE_LOCATION_ADDR + else: + image_location = PROD_FPGA_IMAGE_LOCATION_ADDR + + fpga_file = open(fpga, 'rb') + fpga_image = fpga_file.read() + erase_image(ip, image_location, FPGA_IMAGE_SIZE_BYTES) + write_image(ip, fpga_image, image_location) + verify_image(ip, fpga_image, image_location) + + if fw: + if safe: + image_location = SAFE_FW_IMAGE_LOCATION_ADDR + else: + image_location = PROD_FW_IMAGE_LOCATION_ADDR + + fw_file = open(fw, 'rb') + fw_image = fw_file.read() + erase_image(ip, image_location, FW_IMAGE_SIZE_BYTES) + write_image(ip, fw_image, image_location) + verify_image(ip, fw_image, image_location) + + if reset: + reset_usrp(ip) + +def write_image(ip, image, addr): + print "Writing image" +#we split the image into smaller (256B) bits and send them down the wire + while image: + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL, seq(), addr, FLASH_DATA_PACKET_SIZE, image[:FLASH_DATA_PACKET_SIZE]) + in_pkt = send_and_recv(out_pkt, ip) + + (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + + if pktid != update_id_t.USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG: + raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + image = image[FLASH_DATA_PACKET_SIZE:] + addr += FLASH_DATA_PACKET_SIZE + +def verify_image(ip, image, addr): + print "Verifying data" + readsize = len(image) + readdata = str() + while readsize > 0: + if readsize < FLASH_DATA_PACKET_SIZE: thisreadsize = readsize + else: thisreadsize = FLASH_DATA_PACKET_SIZE + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL, seq(), addr, thisreadsize, "") + in_pkt = send_and_recv(out_pkt, ip) + + (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + + if pktid != update_id_t.USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG: + raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + readdata += data[:thisreadsize] + readsize -= FLASH_DATA_PACKET_SIZE + addr += FLASH_DATA_PACKET_SIZE + + print "Read back %i bytes" % len(readdata) +# print readdata + +# for i in range(256, 512): +# print "out: %i in: %i" % (ord(image[i]), ord(readdata[i])) + + if readdata != image: + print "Verify failed. Image did not write correctly." + else: + print "Success." + +def reset_usrp(ip): + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL, seq(), 0, 0, "") + in_pkt = send_and_recv(out_pkt, ip) + + (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + if pktid == update_id_t.USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG: + raise Exception, "Device failed to reset." + +def erase_image(ip, addr, length): + #get flash info first + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL, seq(), addr, length, "") + in_pkt = send_and_recv(out_pkt, ip) + + (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + + if pktid != update_id_t.USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG: + raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + print "Erasing %i bytes at %i" % (length, addr) + + #now wait for it to finish + while(1): + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL, seq(), 0, 0, "") + in_pkt = send_and_recv(out_pkt, ip) + + (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + + if pktid == update_id_t.USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG: break + elif pktid != update_id_t.USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG: + raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + +######################################################################## +# command line options +######################################################################## +def get_options(): + parser = optparse.OptionParser() + parser.add_option("--ip", type="string", help="USRP2P firmware address", default='') + parser.add_option("--fw", type="string", help="firmware image path (optional)", default='') + parser.add_option("--fpga", type="string", help="fpga image path (optional)", default='') + parser.add_option("--reset", action="store_true", help="reset the device after writing", default=False) + parser.add_option("--overwrite-safe", action="store_true", help="never ever use this option", default=False) + (options, args) = parser.parse_args() + + return options + +######################################################################## +# main +######################################################################## +if __name__=='__main__': + options = get_options() + if not options.ip: raise Exception, 'no ip address specified' + + if not options.fpga and not options.fw and not options.reset: raise Exception, 'Must specify either a firmware image or FPGA image, and/or reset.' + + if options.overwrite_safe: + print("Are you REALLY, REALLY sure you want to overwrite the safe image? This is ALMOST ALWAYS a terrible idea.") + print("If your image is faulty, your USRP2+ will become a brick until reprogrammed via JTAG.") + response = raw_input("""Type "yes" to continue, or anything else to quit: """) + if response != "yes": + sys.exit(0) + + burn_fw(ip=options.ip, fw=options.fw, fpga=options.fpga, reset=options.reset, safe=options.overwrite_safe) |