aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/b200/b200_iface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/b200/b200_iface.cpp')
-rw-r--r--host/lib/usrp/b200/b200_iface.cpp608
1 files changed, 608 insertions, 0 deletions
diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp
new file mode 100644
index 000000000..5c512c1d9
--- /dev/null
+++ b/host/lib/usrp/b200/b200_iface.cpp
@@ -0,0 +1,608 @@
+//
+// Copyright 2012-2013 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 "b200_iface.hpp"
+
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/format.hpp>
+#include <fstream>
+#include <string>
+#include <vector>
+#include <cstring>
+#include <iomanip>
+#include <libusb.h>
+
+using namespace uhd;
+using namespace uhd::transport;
+
+static const bool load_img_msg = true;
+
+const static boost::uint8_t FX3_FIRMWARE_LOAD = 0xA0;
+const static boost::uint8_t VRT_VENDOR_OUT = (LIBUSB_REQUEST_TYPE_VENDOR
+ | LIBUSB_ENDPOINT_OUT);
+const static boost::uint8_t VRT_VENDOR_IN = (LIBUSB_REQUEST_TYPE_VENDOR
+ | LIBUSB_ENDPOINT_IN);
+const static boost::uint8_t B200_VREQ_FPGA_START = 0x02;
+const static boost::uint8_t B200_VREQ_FPGA_DATA = 0x12;
+const static boost::uint8_t B200_VREQ_GET_COMPAT = 0x15;
+const static boost::uint8_t B200_VREQ_SET_FPGA_HASH = 0x1C;
+const static boost::uint8_t B200_VREQ_GET_FPGA_HASH = 0x1D;
+const static boost::uint8_t B200_VREQ_SET_FW_HASH = 0x1E;
+const static boost::uint8_t B200_VREQ_GET_FW_HASH = 0x1F;
+const static boost::uint8_t B200_VREQ_LOOP = 0x22;
+const static boost::uint8_t B200_VREQ_SPI_WRITE = 0x32;
+const static boost::uint8_t B200_VREQ_SPI_READ = 0x42;
+const static boost::uint8_t B200_VREQ_FPGA_CONFIG = 0x55;
+const static boost::uint8_t B200_VREQ_FPGA_RESET = 0x62;
+const static boost::uint8_t B200_VREQ_GPIF_RESET = 0x72;
+const static boost::uint8_t B200_VREQ_GET_USB = 0x80;
+const static boost::uint8_t B200_VREQ_GET_STATUS = 0x83;
+const static boost::uint8_t B200_VREQ_AD9361_CTRL_WRITE = 0x90;
+const static boost::uint8_t B200_VREQ_AD9361_CTRL_READ = 0x91;
+const static boost::uint8_t B200_VREQ_FX3_RESET = 0x99;
+const static boost::uint8_t B200_VREQ_EEPROM_WRITE = 0xBA;
+const static boost::uint8_t B200_VREQ_EEPROM_READ = 0xBB;
+
+const static boost::uint8_t FX3_STATE_FPGA_READY = 0x01;
+const static boost::uint8_t FX3_STATE_CONFIGURING_FPGA = 0x02;
+const static boost::uint8_t FX3_STATE_BUSY = 0x03;
+const static boost::uint8_t FX3_STATE_RUNNING = 0x04;
+const static boost::uint8_t FX3_STATE_UNCONFIGURED = 0x05;
+const static boost::uint8_t FX3_STATE_ERROR = 0x06;
+
+const static int VREQ_MAX_SIZE_USB2 = 64;
+const static int VREQ_MAX_SIZE_USB3 = 512;
+const static int VREQ_DEFAULT_SIZE = VREQ_MAX_SIZE_USB2;
+const static int VREQ_MAX_SIZE = VREQ_MAX_SIZE_USB3;
+
+typedef boost::uint32_t hash_type;
+
+
+/***********************************************************************
+ * Helper Functions
+ **********************************************************************/
+/*!
+ * Create a file hash
+ * The hash will be used to identify the loaded firmware and fpga image
+ * \param filename file used to generate hash value
+ * \return hash value in a size_t type
+ */
+static hash_type generate_hash(const char *filename)
+{
+ std::ifstream file(filename);
+ if (not file){
+ throw uhd::io_error(std::string("cannot open input file ") + filename);
+ }
+
+ size_t hash = 0;
+
+ char ch;
+ long long count = 0;
+ while (file.get(ch)) {
+ count++;
+ boost::hash_combine(hash, ch);
+ }
+
+ if (count == 0){
+ throw uhd::io_error(std::string("empty input file ") + filename);
+ }
+
+ if (not file.eof()){
+ throw uhd::io_error(std::string("file error ") + filename);
+ }
+
+ file.close();
+ return hash_type(hash);
+}
+
+
+/*!
+ * Verify checksum of a Intel HEX record
+ * \param record a line from an Intel HEX file
+ * \return true if record is valid, false otherwise
+ */
+bool checksum(std::string *record) {
+
+ size_t len = record->length();
+ unsigned int i;
+ unsigned char sum = 0;
+ unsigned int val;
+
+ for (i = 1; i < len; i += 2) {
+ std::istringstream(record->substr(i, 2)) >> std::hex >> val;
+ sum += val;
+ }
+
+ if (sum == 0)
+ return true;
+ else
+ return false;
+}
+
+
+/*!
+ * Parse Intel HEX record
+ *
+ * \param record a line from an Intel HEX file
+ * \param len output length of record
+ * \param addr output address
+ * \param type output type
+ * \param data output data
+ * \return true if record is sucessfully read, false on error
+ */
+bool parse_record(std::string *record, boost::uint16_t &len, \
+ boost::uint16_t &addr, boost::uint16_t &type, unsigned char* data) {
+
+ unsigned int i;
+ std::string _data;
+ unsigned int val;
+
+ if (record->substr(0, 1) != ":")
+ return false;
+
+ std::istringstream(record->substr(1, 2)) >> std::hex >> len;
+ std::istringstream(record->substr(3, 4)) >> std::hex >> addr;
+ std::istringstream(record->substr(7, 2)) >> std::hex >> type;
+
+ for (i = 0; i < len; i++) {
+ std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val;
+ data[i] = (unsigned char) val;
+ }
+
+ return true;
+}
+
+
+/***********************************************************************
+ * The implementation class
+ **********************************************************************/
+class b200_iface_impl : public b200_iface{
+public:
+
+ b200_iface_impl(usb_control::sptr usb_ctrl):
+ _usb_ctrl(usb_ctrl)
+ {
+ //NOP
+ }
+
+
+ int fx3_control_write(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length,
+ boost::int32_t timeout = 0)
+ {
+ return _usb_ctrl->submit(VRT_VENDOR_OUT, // bmReqeustType
+ request, // bRequest
+ value, // wValue
+ index, // wIndex
+ buff, // data
+ length, // wLength
+ timeout); // timeout
+ }
+
+
+ int fx3_control_read(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length,
+ boost::int32_t timeout = 0)
+ {
+ return _usb_ctrl->submit(VRT_VENDOR_IN, // bmReqeustType
+ request, // bRequest
+ value, // wValue
+ index, // wIndex
+ buff, // data
+ length, // wLength
+ timeout); // timeout
+ }
+
+
+ void write_i2c(boost::uint16_t addr, const byte_vector_t &bytes)
+ {
+ throw uhd::not_implemented_error("b200 write i2c");
+ }
+
+
+ byte_vector_t read_i2c(boost::uint16_t addr, size_t num_bytes)
+ {
+ throw uhd::not_implemented_error("b200 read i2c");
+ }
+
+ void write_eeprom(boost::uint16_t addr, boost::uint16_t offset,
+ const byte_vector_t &bytes)
+ {
+ fx3_control_write(B200_VREQ_EEPROM_WRITE,
+ 0, offset | (boost::uint16_t(addr) << 8),
+ (unsigned char *) &bytes[0],
+ bytes.size());
+ }
+
+ byte_vector_t read_eeprom(
+ boost::uint16_t addr,
+ boost::uint16_t offset,
+ size_t num_bytes
+ ){
+ byte_vector_t recv_bytes(num_bytes);
+ int bytes_read = fx3_control_read(B200_VREQ_EEPROM_READ,
+ 0, offset | (boost::uint16_t(addr) << 8),
+ (unsigned char*) &recv_bytes[0],
+ num_bytes);
+ if (bytes_read != num_bytes)
+ throw uhd::io_error("Failed to read data from EEPROM.");
+ return recv_bytes;
+ }
+
+ void transact_spi(
+ unsigned char *tx_data,
+ size_t num_tx_bits,
+ unsigned char *rx_data,
+ size_t num_rx_bits
+ ){
+ int ret = 0;
+ boost::uint16_t tx_length = num_tx_bits / 8;
+
+ if(tx_data[0] & 0x80) {
+ ret = fx3_control_write(B200_VREQ_SPI_WRITE, 0x00, \
+ 0x00, tx_data, tx_length);
+ } else {
+ ret = fx3_control_write(B200_VREQ_SPI_READ, 0x00, \
+ 0x00, tx_data, tx_length);
+ }
+
+ if(ret < 0) {
+ throw uhd::io_error("transact_spi: fx3_control_write failed!");
+ }
+
+
+ if(num_rx_bits) {
+ boost::uint16_t total_length = num_rx_bits / 8;
+
+ ret = fx3_control_read(B200_VREQ_LOOP, 0x00, \
+ 0x00, rx_data, total_length);
+
+ if(ret < 0) {
+ throw uhd::io_error("transact_spi: readback failed!");
+ }
+ }
+ }
+
+ void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) {
+ fx3_control_write(B200_VREQ_AD9361_CTRL_WRITE, 0x00, 0x00, (unsigned char *)in_buff, 64);
+ int ret = 0;
+ for (size_t i = 0; i < 30; i++)
+ {
+ ret = fx3_control_read(B200_VREQ_AD9361_CTRL_READ, 0x00, 0x00, out_buff, 64, 1000);
+ if (ret == 64) return;
+ }
+ throw uhd::io_error(str(boost::format("ad9361_transact failed with usb error: %d") % ret));
+ }
+
+
+ void load_firmware(const std::string filestring, bool force = false)
+ {
+ const char *filename = filestring.c_str();
+
+ /* Fields used in each USB control transfer. */
+ boost::uint16_t len = 0;
+ boost::uint16_t type = 0;
+ boost::uint16_t lower_address_bits = 0x0000;
+ unsigned char data[512];
+
+ /* Can be set by the Intel HEX record 0x04, used for all 0x00 records
+ * thereafter. Note this field takes the place of the 'index' parameter in
+ * libusb calls, and is necessary for FX3's 32-bit addressing. */
+ boost::uint16_t upper_address_bits = 0x0000;
+
+ std::ifstream file;
+ file.open(filename, std::ifstream::in);
+
+ if(!file.good()) {
+ throw uhd::io_error("fx3_load_firmware: cannot open firmware input file");
+ }
+
+ if (load_img_msg) UHD_MSG(status) << "Loading firmware image: " \
+ << filestring << "..." << std::flush;
+
+ while (!file.eof()) {
+ boost::int32_t ret = 0;
+ std::string record;
+ file >> record;
+
+ /* Check for valid Intel HEX record. */
+ if (!checksum(&record) || !parse_record(&record, len, \
+ lower_address_bits, type, data)) {
+ throw uhd::io_error("fx3_load_firmware: bad intel hex record checksum");
+ }
+
+ /* Type 0x00: Data. */
+ if (type == 0x00) {
+ ret = fx3_control_write(FX3_FIRMWARE_LOAD, \
+ lower_address_bits, upper_address_bits, data, len);
+
+ if (ret < 0) {
+ throw uhd::io_error("usrp_load_firmware: usrp_control_write failed");
+ }
+ }
+
+ /* Type 0x01: EOF. */
+ else if (type == 0x01) {
+ if (lower_address_bits != 0x0000 || len != 0 ) {
+ throw uhd::io_error("fx3_load_firmware: For EOF record, address must be 0, length must be 0.");
+ }
+
+ //TODO
+ //usrp_set_firmware_hash(hash); //set hash before reset
+
+ /* Successful termination! */
+ file.close();
+
+ /* Let the system settle. */
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
+ return;
+ }
+
+ /* Type 0x04: Extended Linear Address Record. */
+ else if (type == 0x04) {
+ if (lower_address_bits != 0x0000 || len != 2 ) {
+ throw uhd::io_error("fx3_load_firmware: For ELA record, address must be 0, length must be 2.");
+ }
+
+ upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\
+ + ((boost::uint16_t)(data[1] & 0x00FF));
+ }
+
+ /* Type 0x05: Start Linear Address Record. */
+ else if (type == 0x05) {
+ if (lower_address_bits != 0x0000 || len != 4 ) {
+ throw uhd::io_error("fx3_load_firmware: For SLA record, address must be 0, length must be 4.");
+ }
+
+ /* The firmware load is complete. We now need to tell the CPU
+ * to jump to an execution address start point, now contained within
+ * the data field. Parse these address bits out, and then push the
+ * instruction. */
+ upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\
+ + ((boost::uint16_t)(data[1] & 0x00FF));
+ lower_address_bits = ((boost::uint16_t)((data[2] & 0x00FF) << 8))\
+ + ((boost::uint16_t)(data[3] & 0x00FF));
+
+ fx3_control_write(FX3_FIRMWARE_LOAD, lower_address_bits, \
+ upper_address_bits, 0, 0);
+
+ if (load_img_msg) UHD_MSG(status) << " done" << std::endl;
+ }
+
+ /* If we receive an unknown record type, error out. */
+ else {
+ throw uhd::io_error("fx3_load_firmware: unsupported record type.");
+ }
+ }
+
+ /* There was no valid EOF. */
+ throw uhd::io_error("fx3_load_firmware: No EOF record found.");
+ }
+
+
+ void reset_fx3(void) {
+ unsigned char data[4];
+ memset(data, 0x00, sizeof(data));
+
+ fx3_control_write(B200_VREQ_FX3_RESET, 0x00, 0x00, data, 4);
+ }
+
+ void reset_gpif(void) {
+ unsigned char data[4];
+ memset(data, 0x00, sizeof(data));
+
+ fx3_control_write(B200_VREQ_GPIF_RESET, 0x00, 0x00, data, 4);
+ }
+
+ void set_fpga_reset_pin(const bool reset)
+ {
+ unsigned char data[4];
+ memset(data, (reset)? 0xFF : 0x00, sizeof(data));
+
+ UHD_THROW_INVALID_CODE_PATH();
+
+ fx3_control_write(B200_VREQ_FPGA_RESET, 0x00, 0x00, data, 4);
+ }
+
+ boost::uint8_t get_usb_speed(void) {
+
+ unsigned char rx_data[1];
+
+ fx3_control_read(B200_VREQ_GET_USB, 0x00, 0x00, rx_data, 1);
+
+ return boost::lexical_cast<boost::uint8_t>(rx_data[0]);
+ }
+
+
+ boost::uint8_t get_fx3_status(void) {
+
+ unsigned char rx_data[1];
+
+ fx3_control_read(B200_VREQ_GET_STATUS, 0x00, 0x00, &rx_data[0], 1);
+
+ return boost::lexical_cast<boost::uint8_t>(rx_data[0]);
+ }
+
+ boost::uint16_t get_compat_num(void) {
+
+ unsigned char rx_data[2];
+
+ fx3_control_read(B200_VREQ_GET_COMPAT , 0x00, 0x00, rx_data, 2);
+
+ boost::uint16_t compat = 0x0000;
+ compat |= (((uint16_t) rx_data[0]) << 8);
+ compat |= (rx_data[1] & 0x00FF);
+
+ return compat;
+ }
+
+ void usrp_get_firmware_hash(hash_type &hash) {
+ fx3_control_read(B200_VREQ_GET_FW_HASH, 0x00, 0x00,
+ (unsigned char*) &hash, 4, 500);
+ }
+
+ void usrp_set_firmware_hash(hash_type hash) {
+ fx3_control_write(B200_VREQ_SET_FW_HASH, 0x00, 0x00,
+ (unsigned char*) &hash, 4);
+ }
+
+ void usrp_get_fpga_hash(hash_type &hash) {
+ fx3_control_read(B200_VREQ_GET_FPGA_HASH, 0x00, 0x00,
+ (unsigned char*) &hash, 4, 500);
+ }
+
+ void usrp_set_fpga_hash(hash_type hash) {
+ fx3_control_write(B200_VREQ_SET_FPGA_HASH, 0x00, 0x00,
+ (unsigned char*) &hash, 4);
+ }
+
+ boost::uint32_t load_fpga(const std::string filestring) {
+
+ boost::uint8_t fx3_state = 0;
+ boost::uint32_t wait_count;
+
+ const char *filename = filestring.c_str();
+
+ hash_type hash = generate_hash(filename);
+ hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash);
+ if (hash == loaded_hash) return 0;
+
+ int transfer_size = VREQ_DEFAULT_SIZE;
+ int current_usb_speed = get_usb_speed();
+ if (current_usb_speed == 3)
+ transfer_size = VREQ_MAX_SIZE_USB3;
+ else if (current_usb_speed != 2)
+ throw uhd::io_error("load_fpga: get_usb_speed returned invalid USB speed (not 2 or 3)");
+
+ UHD_ASSERT_THROW(transfer_size <= VREQ_MAX_SIZE);
+
+ unsigned char out_buff[VREQ_MAX_SIZE];
+ memset(out_buff, 0x00, sizeof(out_buff));
+ fx3_control_write(B200_VREQ_FPGA_CONFIG, 0, 0, out_buff, 1, 1000);
+
+ size_t file_size = 0;
+ {
+ std::ifstream file(filename, std::ios::in | std::ios::binary | std::ios::ate);
+ file_size = file.tellg();
+ }
+
+ std::ifstream file;
+ file.open(filename, std::ios::in | std::ios::binary);
+
+ if(!file.good()) {
+ throw uhd::io_error("load_fpga: cannot open FPGA input file.");
+ }
+
+ wait_count = 0;
+ do {
+ fx3_state = get_fx3_status();
+
+ if((wait_count >= 500) || (fx3_state == FX3_STATE_ERROR)) {
+ return fx3_state;
+ }
+
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+
+ wait_count++;
+ } while(fx3_state != FX3_STATE_FPGA_READY);
+
+ if (load_img_msg) UHD_MSG(status) << "Loading FPGA image: " \
+ << filestring << "..." << std::flush;
+
+ fx3_control_write(B200_VREQ_FPGA_START, 0, 0, out_buff, 1, 1000);
+
+ wait_count = 0;
+ do {
+ fx3_state = get_fx3_status();
+
+ if((wait_count >= 1000) || (fx3_state == FX3_STATE_ERROR)) {
+ return fx3_state;
+ }
+
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+
+ wait_count++;
+ } while(fx3_state != FX3_STATE_CONFIGURING_FPGA);
+
+ size_t bytes_sent = 0;
+ while(!file.eof()) {
+ file.read((char *) out_buff, transfer_size);
+ const std::streamsize n = file.gcount();
+ if(n == 0) continue;
+
+ boost::uint16_t transfer_count = boost::uint16_t(n);
+
+ /* Send the data to the device. */
+ fx3_control_write(B200_VREQ_FPGA_DATA, 0, 0, out_buff, transfer_count, 5000);
+
+ if (load_img_msg)
+ {
+ if (bytes_sent == 0) UHD_MSG(status) << " 0%" << std::flush;
+ const size_t percent_before = size_t((bytes_sent*100)/file_size);
+ bytes_sent += transfer_count;
+ const size_t percent_after = size_t((bytes_sent*100)/file_size);
+ if (percent_before/10 != percent_after/10)
+ {
+ UHD_MSG(status) << "\b\b\b\b" << std::setw(3) << percent_after << "%" << std::flush;
+ }
+ }
+ }
+
+ file.close();
+
+ wait_count = 0;
+ do {
+ fx3_state = get_fx3_status();
+
+ if((wait_count >= 500) || (fx3_state == FX3_STATE_ERROR)) {
+ return fx3_state;
+ }
+
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+
+ wait_count++;
+ } while(fx3_state != FX3_STATE_RUNNING);
+
+ usrp_set_fpga_hash(hash);
+
+ if (load_img_msg) UHD_MSG(status) << "\b\b\b\b done" << std::endl;
+
+ return 0;
+ }
+
+private:
+ usb_control::sptr _usb_ctrl;
+};
+
+/***********************************************************************
+ * Make an instance of the implementation
+ **********************************************************************/
+b200_iface::sptr b200_iface::make(usb_control::sptr usb_ctrl)
+{
+ return sptr(new b200_iface_impl(usb_ctrl));
+}