diff options
-rw-r--r-- | firmware/fx3/README.md | 10 | ||||
-rw-r--r-- | firmware/fx3/b200/b200_main.c | 35 | ||||
-rw-r--r-- | firmware/fx3/b200/b200_main.h | 4 | ||||
-rw-r--r-- | firmware/fx3/b200/b200_usb_descriptors.c | 84 | ||||
-rw-r--r-- | host/lib/transport/libusb1_base.cpp | 1 | ||||
-rw-r--r-- | host/lib/usrp/b200/b200_iface.hpp | 45 | ||||
-rw-r--r-- | host/lib/usrp/b200/b200_impl.cpp | 111 | ||||
-rw-r--r-- | host/lib/usrp/b200/b200_impl.hpp | 14 |
8 files changed, 239 insertions, 65 deletions
diff --git a/firmware/fx3/README.md b/firmware/fx3/README.md index 84a895059..29d72a9ad 100644 --- a/firmware/fx3/README.md +++ b/firmware/fx3/README.md @@ -19,16 +19,16 @@ distributed by the FX3 manufacturer, Cypress Semiconductor. You can download the [FX3 SDK from here](http://www.cypress.com/?rID=57990). *Note*: You *must* use SDK version 1.2.3! -Once you have downloaded it, extract the ARM cross-compiler sub-directory from -the zip file and put it somewhere useful. The highest level directory you need -is `arm-2011.03/`. +Once you have downloaded it, extract the ARM cross-compiler from the tarball +`ARM_GCC.tar.gz` and put it somewhere useful. The highest level directory you +need is `arm-2013.03/`. Now that you have extracted the cross compilation toolchain, you need to set up some environment variables to tell the B2xx `makefile` where to look for the tools. These variables are: ``` - $ export ARMGCC_INSTALL_PATH=<your path>/arm-2011.03 + $ export ARMGCC_INSTALL_PATH=<your path>/arm-2013.03 $ export ARMGCC_VERSION=4.5.2 ``` @@ -61,7 +61,7 @@ into the `common/` directory you just copied from the Cypress SDK, and apply the patch `b200/fx3_mem_map.patch`. ``` - # cd uhd.git/firmware/common/ + # cd uhd.git/firmware/fx3/common/ $ patch -p2 < ../b200/fx3_mem_map.patch ``` diff --git a/firmware/fx3/b200/b200_main.c b/firmware/fx3/b200/b200_main.c index 077ee251b..cb0172af3 100644 --- a/firmware/fx3/b200/b200_main.c +++ b/firmware/fx3/b200/b200_main.c @@ -2034,6 +2034,7 @@ void b200_usb_init(void) { /* Check to see if a VID/PID is in the EEPROM that we should use. */ uint8_t valid[4]; + uint8_t vidpid[4]; CyU3PMemSet(valid, 0, 4); CyFxUsbI2cTransfer(0x0, 0xA0, 4, valid, CyTrue); if(*((uint32_t *) &(valid[0])) == 0xB2145943) { @@ -2041,7 +2042,6 @@ void b200_usb_init(void) { /* Pull the programmed device serial out of the i2c EEPROM, and copy the * characters into the device serial string, which is then advertised as * part of the USB descriptors. */ - uint8_t vidpid[4]; CyU3PMemSet(vidpid, 0, 4); CyFxUsbI2cTransfer(0x4, 0xA0, 4, vidpid, CyTrue); b200_usb2_dev_desc[8] = vidpid[2]; @@ -2055,6 +2055,35 @@ void b200_usb_init(void) { b200_usb3_dev_desc[11] = vidpid[1]; } + /* We support two VIDs: + * Ettus Research: 0x2500 + * National Instruments: 0x3923 + * + * We support three PIDs: + * Ettus B200/B210: 0x0020 + * NI USRP-2900: 0x7813 + * NI USRP-2901: 0x7814 + */ + uint8_t *mfr_string = NULL; + uint8_t *product_string = NULL; + if((vidpid[3] == 0x25) && (vidpid[2] == 0x00)) { + mfr_string = b200_usb_manufacture_desc; + product_string = b200_usb_product_desc; + } else if((vidpid[3] == 0x39) && (vidpid[2] == 0x23)) { + mfr_string = niusrp_usb_manufacture_desc; + + if((vidpid[1] == 0x78) && (vidpid[0] == 0x13)) { + product_string = niusrp_2900_usb_product_desc; + } else if((vidpid[1] == 0x78) && (vidpid[0] == 0x14)) { + product_string = niusrp_2901_usb_product_desc; + } else { + product_string = unknown_desc; + } + } else { + mfr_string = unknown_desc; + product_string = unknown_desc; + } + uint8_t ascii_serial[9]; CyU3PMemSet(ascii_serial, 0, 9); CyFxUsbI2cTransfer(0x4f7, 0xA0, 9, ascii_serial, CyTrue); @@ -2101,10 +2130,10 @@ void b200_usb_init(void) { (uint8_t *) b200_string_lang_id_desc); CyU3PUsbSetDesc(CY_U3P_USB_SET_STRING_DESCR, 1, - (uint8_t *) b200_usb_manufacture_desc); + (uint8_t *) mfr_string); CyU3PUsbSetDesc(CY_U3P_USB_SET_STRING_DESCR, 2, - (uint8_t *) b200_usb_product_desc); + (uint8_t *) product_string); CyU3PUsbSetDesc(CY_U3P_USB_SET_STRING_DESCR, 3, (uint8_t *) dev_serial); diff --git a/firmware/fx3/b200/b200_main.h b/firmware/fx3/b200/b200_main.h index 2c6f178ec..61048abc9 100644 --- a/firmware/fx3/b200/b200_main.h +++ b/firmware/fx3/b200/b200_main.h @@ -128,6 +128,10 @@ extern const uint8_t b200_usb_ss_config_desc[]; extern const uint8_t b200_string_lang_id_desc[]; extern const uint8_t b200_usb_manufacture_desc[]; extern const uint8_t b200_usb_product_desc[]; +extern const uint8_t niusrp_usb_manufacture_desc[]; +extern const uint8_t niusrp_2900_usb_product_desc[]; +extern const uint8_t niusrp_2901_usb_product_desc[]; +extern const uint8_t unknown_desc[]; extern uint8_t dev_serial[]; diff --git a/firmware/fx3/b200/b200_usb_descriptors.c b/firmware/fx3/b200/b200_usb_descriptors.c index e8a765b24..03dbce74a 100644 --- a/firmware/fx3/b200/b200_usb_descriptors.c +++ b/firmware/fx3/b200/b200_usb_descriptors.c @@ -458,6 +458,39 @@ const uint8_t b200_usb_manufacture_desc[] __attribute__ ((aligned (32))) = 'C',0x00 }; +/* NI Manufacturer String Descriptor */ +const uint8_t niusrp_usb_manufacture_desc[] __attribute__ ((aligned (32))) = + { + 0x36, /* Descriptor Size */ + CY_U3P_USB_STRING_DESCR, /* Device Descriptor Type */ + 'N',0x00, + 'a',0x00, + 't',0x00, + 'i',0x00, + 'o',0x00, + 'n',0x00, + 'a',0x00, + 'l',0x00, + ' ',0x00, + 'I',0x00, + 'n',0x00, + 's',0x00, + 't',0x00, + 'r',0x00, + 'u',0x00, + 'm',0x00, + 'e',0x00, + 'n',0x00, + 't',0x00, + 's',0x00, + ' ',0x00, + 'C',0x00, + 'o',0x00, + 'r',0x00, + 'p',0x00, + '.',0x00 + }; + /* Standard Product String Descriptor */ const uint8_t b200_usb_product_desc[] __attribute__ ((aligned (32))) = @@ -475,6 +508,57 @@ const uint8_t b200_usb_product_desc[] __attribute__ ((aligned (32))) = '0',0x00 }; +/* NI-USRP 2900 Product String Descriptor */ +const uint8_t niusrp_2900_usb_product_desc[] __attribute__ ((aligned (32))) = + { + 0x1A, /* Descriptor Size */ + CY_U3P_USB_STRING_DESCR, /* Device Descriptor Type */ + 'N',0x00, + 'I',0x00, + ' ',0x00, + 'U',0x00, + 'S',0x00, + 'R',0x00, + 'P',0x00, + '-',0x00, + '2',0x00, + '9',0x00, + '0',0x00, + '0',0x00 + }; + +/* NI-USRP 2901 Product String Descriptor */ +const uint8_t niusrp_2901_usb_product_desc[] __attribute__ ((aligned (32))) = + { + 0x1A, /* Descriptor Size */ + CY_U3P_USB_STRING_DESCR, /* Device Descriptor Type */ + 'N',0x00, + 'I',0x00, + ' ',0x00, + 'U',0x00, + 'S',0x00, + 'R',0x00, + 'P',0x00, + '-',0x00, + '2',0x00, + '9',0x00, + '0',0x00, + '1',0x00 + }; + +const uint8_t unknown_desc[] __attribute__ ((aligned (32))) = + { + 0x10, /* Descriptor Size */ + CY_U3P_USB_STRING_DESCR, /* Device Descriptor Type */ + 'U',0x00, + 'n',0x00, + 'k',0x00, + 'n',0x00, + 'o',0x00, + 'w',0x00, + 'n',0x00 + }; + /* Microsoft OS Descriptor. */ const uint8_t CyFxUsbOSDscr[] __attribute__ ((aligned (32))) = { diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index 375cfe92d..ae33cc036 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -327,6 +327,7 @@ public: bool firmware_loaded() { return (get_manufacturer() == "Ettus Research LLC") or + (get_manufacturer() == "National Instruments Corp.") or (get_manufacturer() == "Free Software Folks"); } diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp index 83adfdd64..1d123439a 100644 --- a/host/lib/usrp/b200/b200_iface.hpp +++ b/host/lib/usrp/b200/b200_iface.hpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2013 Ettus Research LLC +// Copyright 2012-2013,2015 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 @@ -21,19 +21,48 @@ #include <stdint.h> #include <uhd/transport/usb_control.hpp> #include <uhd/types/serial.hpp> //i2c iface +#include <uhd/types/dict.hpp> +#include <boost/assign/list_of.hpp> #include <boost/shared_ptr.hpp> #include <boost/utility.hpp> #include "ad9361_ctrl.hpp" -const static boost::uint16_t B200_VENDOR_ID = 0x2500; -const static boost::uint16_t B200_PRODUCT_ID = 0x0020; -const static boost::uint16_t FX3_VID = 0x04b4; -const static boost::uint16_t FX3_DEFAULT_PID = 0x00f3; -const static boost::uint16_t FX3_REENUM_PID = 0x00f0; +enum b200_type_t { + B200, + B210 +}; + +const static boost::uint16_t B200_VENDOR_ID = 0x2500; +const static boost::uint16_t B200_VENDOR_NI_ID = 0x3923; +const static boost::uint16_t B200_PRODUCT_ID = 0x0020; +const static boost::uint16_t B200_PRODUCT_NI_ID = 0x7813; +const static boost::uint16_t B210_PRODUCT_NI_ID = 0x7814; +const static boost::uint16_t FX3_VID = 0x04b4; +const static boost::uint16_t FX3_DEFAULT_PID = 0x00f3; +const static boost::uint16_t FX3_REENUM_PID = 0x00f0; static const std::string B200_FW_FILE_NAME = "usrp_b200_fw.hex"; -static const std::string B200_FPGA_FILE_NAME = "usrp_b200_fpga.bin"; -static const std::string B210_FPGA_FILE_NAME = "usrp_b210_fpga.bin"; + +//! Map the product ID (in the EEPROM) to a device type +static const uhd::dict<boost::uint16_t, b200_type_t> B2X0_PRODUCT_ID = boost::assign::map_list_of + (0x0001, B200) + (0x7737, B200) + (B200_PRODUCT_NI_ID, B200) + (0x0002, B210) + (0x7738, B210) + (B210_PRODUCT_NI_ID, B210) +; + +static const uhd::dict<b200_type_t, std::string> B2X0_STR_NAMES = boost::assign::map_list_of + (B200, "B200") + (B210, "B210") +; + +static const uhd::dict<b200_type_t, std::string> B2X0_FPGA_FILE_NAME = boost::assign::map_list_of + (B200, "usrp_b200_fpga.bin") + (B210, "usrp_b210_fpga.bin") +; + class UHD_API b200_iface: boost::noncopyable, public virtual uhd::i2c_iface { public: diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index e567ff434..273153c16 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -43,10 +43,6 @@ using namespace uhd::transport; static const boost::posix_time::milliseconds REENUMERATION_TIMEOUT_MS(3000); -//! mapping of frontend to radio perif index -static const size_t FE1 = 1; -static const size_t FE2 = 0; - class b200_ad9361_client_t : public ad9361_params { public: ~b200_ad9361_client_t() {} @@ -77,6 +73,24 @@ public: /*********************************************************************** * Discovery **********************************************************************/ +//! Look up the type of B-Series device we're currently running. +// If the product ID stored in mb_eeprom is invalid, throws a +// uhd::runtime_error. +static b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom) +{ + if (mb_eeprom["product"].empty()) { + throw uhd::runtime_error("B200: Missing product ID on EEPROM."); + } + boost::uint16_t product_id = boost::lexical_cast<boost::uint16_t>(mb_eeprom["product"]); + if (not B2X0_PRODUCT_ID.has_key(product_id)) { + throw uhd::runtime_error(str( + boost::format("B200 unknown product code: 0x%04x") + % product_id + )); + } + return B2X0_PRODUCT_ID[product_id]; +} + static device_addrs_t b200_find(const device_addr_t &hint) { device_addrs_t b200_addrs; @@ -152,20 +166,14 @@ static device_addrs_t b200_find(const device_addr_t &hint) new_addr["type"] = "b200"; new_addr["name"] = mb_eeprom["name"]; new_addr["serial"] = handle->get_serial(); - if (not mb_eeprom["product"].empty()) - { - switch (boost::lexical_cast<boost::uint16_t>(mb_eeprom["product"])) - { - case 0x0001: - case 0x7737: - new_addr["product"] = "B200"; - break; - case 0x7738: - case 0x0002: - new_addr["product"] = "B210"; - break; - default: UHD_MSG(error) << "B200 unknown product code: " << mb_eeprom["product"] << std::endl; - } + try { + // Turn the 16-Bit product ID into a string representation + new_addr["product"] = B2X0_STR_NAMES[get_b200_type(mb_eeprom)]; + } catch (const uhd::runtime_error &e) { + // No problem if this fails -- this is just device discovery, after all. + UHD_MSG(error) << e.what() << std::endl; + // Skip this loop. + continue; } //this is a found b200 when the hint serial and name match or blank if ( @@ -238,32 +246,41 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : .subscribe(boost::bind(&b200_impl::set_mb_eeprom, this, _1)); //////////////////////////////////////////////////////////////////// - // Load the FPGA image, then reset GPIF + // Identify the device type //////////////////////////////////////////////////////////////////// std::string default_file_name; - std::string product_name = "B200?"; - if (not mb_eeprom["product"].empty()) - { - switch (boost::lexical_cast<boost::uint16_t>(mb_eeprom["product"])) - { - case 0x0001: - case 0x7737: - product_name = "B200"; - default_file_name = B200_FPGA_FILE_NAME; - break; - case 0x7738: - case 0x0002: - product_name = "B210"; - default_file_name = B210_FPGA_FILE_NAME; - break; - default: UHD_MSG(error) << "B200 unknown product code: " << mb_eeprom["product"] << std::endl; + std::string product_name; + try { + // This will throw if the product ID is invalid: + _b200_type = get_b200_type(mb_eeprom); + default_file_name = B2X0_FPGA_FILE_NAME.get(_b200_type); + product_name = B2X0_STR_NAMES.get(_b200_type); + } catch (const uhd::runtime_error &e) { + // The only reason we may let this pass is if the user specified + // the FPGA file name: + if (not device_addr.has_key("fpga")) { + throw e; } + // In this case, we must provide a default product name: + product_name = "B200?"; + _b200_type = B200; } - if (default_file_name.empty()) + + //set up frontend mapping + _fe1 = 1; + _fe2 = 0; + if (_b200_type == B200 and + not mb_eeprom["revision"].empty() and + boost::lexical_cast<size_t>(mb_eeprom["revision"]) >= 5) { - UHD_ASSERT_THROW(device_addr.has_key("fpga")); + _fe1 = 0; //map radio0 to FE1 + _fe2 = 1; //map radio1 to FE2 + _gpio_state.atr_sel = 1; //map radio0 ATR pins to FE2 } + //////////////////////////////////////////////////////////////////// + // Load the FPGA image, then reset GPIF + //////////////////////////////////////////////////////////////////// //extract the FPGA path for the B200 std::string b200_fpga_image = find_image_path( device_addr.has_key("fpga")? device_addr["fpga"] : default_file_name @@ -316,7 +333,6 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : /* Initialize the GPIOs, set the default bandsels to the lower range. Note * that calling update_bandsel calls update_gpio_state(). */ - _gpio_state = gpio_state(); update_bandsel("RX", 800e6); update_bandsel("TX", 850e6); @@ -628,7 +644,7 @@ void b200_impl::setup_radio(const size_t dspno) for(size_t direction = 0; direction < 2; direction++) { const std::string x = direction? "rx" : "tx"; - const std::string key = std::string((direction? "RX" : "TX")) + std::string(((dspno == FE1)? "1" : "2")); + const std::string key = std::string((direction? "RX" : "TX")) + std::string(((dspno == _fe1)? "1" : "2")); const fs_path rf_fe_path = mb_path / "dboards" / "A" / (x+"_frontends") / (dspno? "B" : "A"); _tree->create<std::string>(rf_fe_path / "name").set("FE-"+key); @@ -900,6 +916,7 @@ void b200_impl::update_bandsel(const std::string& which, double freq) void b200_impl::update_gpio_state(void) { const boost::uint32_t misc_word = 0 + | (_gpio_state.atr_sel << 8) | (_gpio_state.tx_bandsel_a << 7) | (_gpio_state.tx_bandsel_b << 6) | (_gpio_state.rx_bandsel_a << 5) @@ -924,9 +941,9 @@ void b200_impl::reset_codec_dcm(void) void b200_impl::update_atrs(void) { - if (_radio_perifs.size() > FE1 and _radio_perifs[FE1].atr) + if (_radio_perifs.size() > _fe1 and _radio_perifs[_fe1].atr) { - radio_perifs_t &perif = _radio_perifs[FE1]; + radio_perifs_t &perif = _radio_perifs[_fe1]; const bool enb_rx = bool(perif.rx_streamer.lock()); const bool enb_tx = bool(perif.tx_streamer.lock()); const bool is_rx2 = perif.ant_rx2; @@ -942,9 +959,9 @@ void b200_impl::update_atrs(void) atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, txonly); atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd); } - if (_radio_perifs.size() > FE2 and _radio_perifs[FE2].atr) + if (_radio_perifs.size() > _fe2 and _radio_perifs[_fe2].atr) { - radio_perifs_t &perif = _radio_perifs[FE2]; + radio_perifs_t &perif = _radio_perifs[_fe2]; const bool enb_rx = bool(perif.rx_streamer.lock()); const bool enb_tx = bool(perif.tx_streamer.lock()); const bool is_rx2 = perif.ant_rx2; @@ -972,10 +989,10 @@ void b200_impl::update_antenna_sel(const size_t which, const std::string &ant) void b200_impl::update_enables(void) { //extract settings from state variables - const bool enb_tx1 = (_radio_perifs.size() > FE1) and bool(_radio_perifs[FE1].tx_streamer.lock()); - const bool enb_rx1 = (_radio_perifs.size() > FE1) and bool(_radio_perifs[FE1].rx_streamer.lock()); - const bool enb_tx2 = (_radio_perifs.size() > FE2) and bool(_radio_perifs[FE2].tx_streamer.lock()); - const bool enb_rx2 = (_radio_perifs.size() > FE2) and bool(_radio_perifs[FE2].rx_streamer.lock()); + const bool enb_tx1 = (_radio_perifs.size() > _fe1) and bool(_radio_perifs[_fe1].tx_streamer.lock()); + const bool enb_rx1 = (_radio_perifs.size() > _fe1) and bool(_radio_perifs[_fe1].rx_streamer.lock()); + const bool enb_tx2 = (_radio_perifs.size() > _fe2) and bool(_radio_perifs[_fe2].tx_streamer.lock()); + const bool enb_rx2 = (_radio_perifs.size() > _fe2) and bool(_radio_perifs[_fe2].rx_streamer.lock()); const size_t num_rx = (enb_rx1?1:0) + (enb_rx2?1:0); const size_t num_tx = (enb_tx1?1:0) + (enb_tx2?1:0); const bool mimo = num_rx == 2 or num_tx == 2; diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index 396819f9a..8d0c637d7 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -47,7 +47,7 @@ #include "recv_packet_demuxer_3000.hpp" static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 7; static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0; -static const boost::uint16_t B200_FPGA_COMPAT_NUM = 4; +static const boost::uint16_t B200_FPGA_COMPAT_NUM = 5; static const double B200_BUS_CLOCK_RATE = 100e6; static const double B200_DEFAULT_TICK_RATE = 32e6; static const double B200_DEFAULT_FREQ = 100e6; // Hz @@ -101,6 +101,8 @@ public: void check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const char* direction = NULL); private: + b200_type_t _b200_type; + //controllers b200_iface::sptr _iface; radio_ctrl_core_3000::sptr _local_ctrl; @@ -161,6 +163,13 @@ private: }; std::vector<radio_perifs_t> _radio_perifs; + //mapping of AD936x frontends (FE1 and FE2) to radio perif index (0 and 1) + //FE1 corresponds to the ports labeled "RF B" on the B200/B210 + //FE2 corresponds to the ports labeled "RF A" on the B200/B210 + //the mapping is product and revision specific + size_t _fe1; + size_t _fe2; + /*! \brief Setup the DSP chain for one radio front-end. * */ @@ -168,7 +177,7 @@ private: void handle_overflow(const size_t radio_index); struct gpio_state { - boost::uint32_t tx_bandsel_a, tx_bandsel_b, rx_bandsel_a, rx_bandsel_b, rx_bandsel_c, codec_arst, mimo, ref_sel; + boost::uint32_t tx_bandsel_a, tx_bandsel_b, rx_bandsel_a, rx_bandsel_b, rx_bandsel_c, codec_arst, mimo, ref_sel, atr_sel; gpio_state() { tx_bandsel_a = 0; @@ -179,6 +188,7 @@ private: codec_arst = 0; mimo = 0; ref_sel = 0; + atr_sel = 0; } } _gpio_state; |