// // Copyright 2010-2012 Ettus Research LLC // Copyright 2018 Ettus Research, a National Instruments Company // // SPDX-License-Identifier: GPL-3.0-or-later // #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace uhd; using namespace uhd::usrp; #define FX2_FIRMWARE_LOAD 0xa0 static const bool load_img_msg = true; typedef 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; while (file.get(ch)) { boost::hash_combine(hash, ch); } 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 */ static 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, unsigned int &len, unsigned int &addr, unsigned int &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; if (len > (2 * (record->length() - 9))) // sanity check to prevent buffer overrun return false; for (i = 0; i < len; i++) { std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val; data[i] = (unsigned char) val; } return true; } /*! * USRP control implementation for device discovery and configuration */ class fx2_ctrl_impl : public fx2_ctrl { public: fx2_ctrl_impl(uhd::transport::usb_control::sptr ctrl_transport) { _ctrl_transport = ctrl_transport; } void usrp_fx2_reset(void){ unsigned char reset_y = 1; unsigned char reset_n = 0; usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_y, 1); usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_n, 1); //wait for things to settle std::this_thread::sleep_for(std::chrono::milliseconds(2000)); } void usrp_load_firmware(std::string filestring, bool force) { const char *filename = filestring.c_str(); hash_type hash = generate_hash(filename); hash_type loaded_hash; usrp_get_firmware_hash(loaded_hash); if (not force and (hash == loaded_hash)) return; //FIXME: verify types unsigned int len; unsigned int addr; unsigned int type; unsigned char data[512]; std::ifstream file; file.open(filename, std::ifstream::in); if (!file.good()) { throw uhd::io_error("usrp_load_firmware: cannot open firmware input file"); } unsigned char reset_y = 1; unsigned char reset_n = 0; //hit the reset line if (load_img_msg) UHD_LOGGER_INFO("FX2") << "Loading firmware image: " << filestring << "..."; usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_y, 1); while (!file.eof()) { std::string record; file >> record; if (!(record.length() > 0)) continue; //check for valid record if (not checksum(&record) or not parse_record(&record, len, addr, type, data)) { throw uhd::io_error("usrp_load_firmware: bad record checksum"); } //type 0x00 is data if (type == 0x00) { int ret = usrp_control_write(FX2_FIRMWARE_LOAD, addr, 0, data, len); if (ret < 0) throw uhd::io_error("usrp_load_firmware: usrp_control_write failed"); } //type 0x01 is end else if (type == 0x01) { usrp_set_firmware_hash(hash); //set hash before reset usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_n, 1); file.close(); //wait for things to settle std::this_thread::sleep_for(std::chrono::milliseconds(1000)); if (load_img_msg) UHD_LOGGER_INFO("FX2") << "Firmware loaded"; return; } //type anything else is unhandled else { throw uhd::io_error("usrp_load_firmware: unsupported record"); } } //file did not end throw uhd::io_error("usrp_load_firmware: bad record"); } void usrp_init(void){ //disable usrp_rx_enable(false); usrp_tx_enable(false); //toggle resets usrp_rx_reset(true); usrp_tx_reset(true); usrp_rx_reset(false); usrp_tx_reset(false); } void usrp_load_fpga(std::string filestring) { 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; const int ep0_size = 64; unsigned char buf[ep0_size]; if (load_img_msg) UHD_LOGGER_INFO("FX2") << "Loading FPGA image: " << filestring << "..."; std::ifstream file; file.open(filename, std::ios::in | std::ios::binary); if (not file.good()) { throw uhd::io_error("usrp_load_fpga: cannot open fpga input file"); } usrp_fpga_reset(true); //holding the fpga in reset while loading if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_BEGIN) < 0) { throw uhd::io_error("usrp_load_fpga: fpga load error"); } while (not file.eof()) { file.read((char *)buf, sizeof(buf)); const std::streamsize n = file.gcount(); if(n == 0) continue; int ret = usrp_control_write(VRQ_FPGA_LOAD, 0, FL_XFER, buf, uint16_t(n)); if (ret < 0 or std::streamsize(ret) != n) { throw uhd::io_error("usrp_load_fpga: fpga load error"); } } if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_END) < 0) { throw uhd::io_error("usrp_load_fpga: fpga load error"); } usrp_set_fpga_hash(hash); usrp_fpga_reset(false); //done loading, take fpga out of reset file.close(); if (load_img_msg) UHD_LOGGER_INFO("FX2") << "FPGA image loaded"; } void usrp_load_eeprom(std::string filestring) { if (load_img_msg) UHD_LOGGER_INFO("FX2") << "Loading EEPROM image: " << filestring << "..."; const char *filename = filestring.c_str(); const uint16_t i2c_addr = 0x50; unsigned int addr; unsigned char data[256]; unsigned char sendbuf[17]; std::ifstream file; file.open(filename, std::ifstream::in); if (not file.good()) { throw uhd::io_error("usrp_load_eeprom: cannot open EEPROM input file"); } file.read((char *)data, 256); std::streamsize len = file.gcount(); if(len == 256) { throw uhd::io_error("usrp_load_eeprom: image size too large"); } const int pagesize = 16; addr = 0; while(len > 0) { sendbuf[0] = addr; memcpy(sendbuf+1, &data[addr], len > pagesize ? pagesize : size_t(len)); int ret = usrp_i2c_write(i2c_addr, sendbuf, (len > pagesize ? pagesize : size_t(len))+1); if (ret < 0) { throw uhd::io_error("usrp_load_eeprom: usrp_i2c_write failed"); } addr += pagesize; len -= pagesize; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } file.close(); if (load_img_msg) UHD_LOGGER_INFO("FX2") << "EEPROM image loaded"; } void usrp_set_led(int led_num, bool on) { UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_SET_LED, on, led_num) >= 0); } void usrp_get_firmware_hash(hash_type &hash) { UHD_ASSERT_THROW(usrp_control_read(0xa0, USRP_HASH_SLOT_0_ADDR, 0, (unsigned char*) &hash, sizeof(hash)) >= 0); } void usrp_set_firmware_hash(hash_type hash) { UHD_ASSERT_THROW(usrp_control_write(0xa0, USRP_HASH_SLOT_0_ADDR, 0, (unsigned char*) &hash, sizeof(hash)) >= 0); } void usrp_get_fpga_hash(hash_type &hash) { UHD_ASSERT_THROW(usrp_control_read(0xa0, USRP_HASH_SLOT_1_ADDR, 0, (unsigned char*) &hash, sizeof(hash)) >= 0); } void usrp_set_fpga_hash(hash_type hash) { UHD_ASSERT_THROW(usrp_control_write(0xa0, USRP_HASH_SLOT_1_ADDR, 0, (unsigned char*) &hash, sizeof(hash)) >= 0); } void usrp_tx_enable(bool on) { UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_TX_ENABLE, on, 0) >= 0); } void usrp_rx_enable(bool on) { UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_RX_ENABLE, on, 0) >= 0); } void usrp_tx_reset(bool on) { UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_TX_RESET, on, 0) >= 0); } void usrp_rx_reset(bool on) { UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_RX_RESET, on, 0) >= 0); } void usrp_fpga_reset(bool on) { UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_RESET, on, 0) >= 0); } int usrp_control_write(uint8_t request, uint16_t value, uint16_t index, unsigned char *buff, uint16_t length) { return _ctrl_transport->submit(VRT_VENDOR_OUT, // bmReqeustType request, // bRequest value, // wValue index, // wIndex buff, // data length); // wLength } int usrp_control_read(uint8_t request, uint16_t value, uint16_t index, unsigned char *buff, uint16_t length) { return _ctrl_transport->submit(VRT_VENDOR_IN, // bmReqeustType request, // bRequest value, // wValue index, // wIndex buff, // data length); // wLength } int usrp_control_write_cmd(uint8_t request, uint16_t value, uint16_t index) { return usrp_control_write(request, value, index, 0, 0); } byte_vector_t read_eeprom( uint16_t addr, uint16_t offset, size_t num_bytes ){ this->write_i2c(addr, byte_vector_t(1, uint8_t(offset))); return this->read_i2c(addr, num_bytes); } int usrp_i2c_write(uint16_t i2c_addr, unsigned char *buf, uint16_t len) { return usrp_control_write(VRQ_I2C_WRITE, i2c_addr, 0, buf, len); } int usrp_i2c_read(uint16_t i2c_addr, unsigned char *buf, uint16_t len) { return usrp_control_read(VRQ_I2C_READ, i2c_addr, 0, buf, len); } static const bool iface_debug = false; static const size_t max_i2c_data_bytes = 64; void write_i2c(uint16_t addr, const byte_vector_t &bytes) { UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes); int ret = this->usrp_i2c_write(addr, (unsigned char *)&bytes.front(), bytes.size()); if (iface_debug && (ret < 0)) uhd::runtime_error("USRP: failed i2c write"); } byte_vector_t read_i2c(uint16_t addr, size_t num_bytes) { UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes); byte_vector_t bytes(num_bytes); int ret = this->usrp_i2c_read(addr, (unsigned char *)&bytes.front(), num_bytes); if (iface_debug && ((ret < 0) || (unsigned)ret < (num_bytes))) uhd::runtime_error("USRP: failed i2c read"); return bytes; } private: uhd::transport::usb_control::sptr _ctrl_transport; }; /*********************************************************************** * Public make function for fx2_ctrl interface **********************************************************************/ fx2_ctrl::sptr fx2_ctrl::make(uhd::transport::usb_control::sptr ctrl_transport){ return sptr(new fx2_ctrl_impl(ctrl_transport)); }