diff options
Diffstat (limited to 'host/utils')
-rw-r--r-- | host/utils/CMakeLists.txt | 16 | ||||
-rw-r--r-- | host/utils/cdecode.c | 80 | ||||
-rw-r--r-- | host/utils/cdecode.h | 28 | ||||
-rw-r--r-- | host/utils/nirio_programmer.cpp | 275 | ||||
-rw-r--r-- | host/utils/usrp_cal_utils.hpp | 2 | ||||
-rw-r--r-- | host/utils/usrp_n2xx_simple_net_burner.cpp | 2 | ||||
-rw-r--r-- | host/utils/usrp_x3xx_fpga_burner.cpp | 498 |
7 files changed, 898 insertions, 3 deletions
diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index abf2a546b..7604a7d37 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2012 Ettus Research LLC +# Copyright 2010-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 @@ -15,6 +15,8 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # +SET(CMAKE_C_COMPILE_OBJECT ${CMAKE_CXX_COMPILE_OBJECT}) + ######################################################################## # Utilities that get installed into the runtime path ######################################################################## @@ -25,6 +27,12 @@ SET(util_runtime_sources uhd_cal_tx_dc_offset.cpp uhd_cal_tx_iq_balance.cpp usrp_n2xx_simple_net_burner.cpp + nirio_programmer.cpp +) + +SET(x3xx_burner_sources + usrp_x3xx_fpga_burner.cpp + cdecode.c ) #for each source: build an executable and install @@ -35,6 +43,10 @@ FOREACH(util_source ${util_runtime_sources}) UHD_INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${RUNTIME_DIR} COMPONENT utilities) ENDFOREACH(util_source) +ADD_EXECUTABLE(usrp_x3xx_fpga_burner ${x3xx_burner_sources}) +TARGET_LINK_LIBRARIES(usrp_x3xx_fpga_burner uhd ${Boost_LIBRARIES}) +UHD_INSTALL(TARGETS usrp_x3xx_fpga_burner RUNTIME DESTINATION ${RUNTIME_DIR} COMPONENT utilities) + ######################################################################## # Utilities that get installed into the share path ######################################################################## @@ -43,7 +55,6 @@ SET(util_share_sources usrp_burn_db_eeprom.cpp usrp_burn_mb_eeprom.cpp ) - IF(ENABLE_USB) LIST(APPEND util_share_sources fx2_init_eeprom.cpp @@ -71,6 +82,7 @@ FOREACH(util_source ${util_share_sources}) ENDFOREACH(util_source) UHD_INSTALL(TARGETS usrp_n2xx_simple_net_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) +UHD_INSTALL(TARGETS usrp_x3xx_fpga_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) #UHD images downloader configuration CONFIGURE_FILE( diff --git a/host/utils/cdecode.c b/host/utils/cdecode.c new file mode 100644 index 000000000..0a9b5c46b --- /dev/null +++ b/host/utils/cdecode.c @@ -0,0 +1,80 @@ +/*
+cdecoder.c - c source to a base64 decoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#include "cdecode.h"
+
+int base64_decode_value(char value_in){
+ static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
+ static const char decoding_size = sizeof(decoding);
+ value_in -= 43;
+ if ((signed char)value_in < 0 || value_in > decoding_size) return -1;
+ return decoding[(int)value_in];
+}
+
+void base64_init_decodestate(base64_decodestate* state_in){
+ state_in->step = step_a;
+ state_in->plainchar = 0;
+}
+
+size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in){
+ const char* codechar = code_in;
+ char* plainchar = plaintext_out;
+ char fragment;
+
+ *plainchar = state_in->plainchar;
+
+ switch (state_in->step){
+ while (1){
+ case step_a:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_a;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar = (fragment & 0x03f) << 2;
+
+ case step_b:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_b;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x030) >> 4;
+ *plainchar = (fragment & 0x00f) << 4;
+ case step_c:
+ do{
+ if (codechar == code_in+length_in)
+ {
+ state_in->step = step_c;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x03c) >> 2;
+ *plainchar = (fragment & 0x003) << 6;
+ case step_d:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_d;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x03f);
+ }
+ }
+ /* control should not reach here */
+ return plainchar - plaintext_out;
+}
diff --git a/host/utils/cdecode.h b/host/utils/cdecode.h new file mode 100644 index 000000000..e1eee301f --- /dev/null +++ b/host/utils/cdecode.h @@ -0,0 +1,28 @@ +/* +cdecode.h - c header for a base64 decoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CDECODE_H +#define BASE64_CDECODE_H + +#include <stddef.h> + +typedef enum{ + step_a, step_b, step_c, step_d +} base64_decodestep; + +typedef struct{ + base64_decodestep step; + char plainchar; +} base64_decodestate; + +void base64_init_decodestate(base64_decodestate* state_in); + +int base64_decode_value(char value_in); + +size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in); + +#endif /* BASE64_CDECODE_H */ diff --git a/host/utils/nirio_programmer.cpp b/host/utils/nirio_programmer.cpp new file mode 100644 index 000000000..98db862eb --- /dev/null +++ b/host/utils/nirio_programmer.cpp @@ -0,0 +1,275 @@ + +#include <uhd/transport/nirio/niusrprio_session.h> +#include <uhd/transport/nirio/niriok_proxy.h> +#include <uhd/transport/nirio/nifpga_lvbitx.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <iostream> +#include <fstream> +#include <streambuf> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/thread/thread.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/regex.hpp> + +using namespace uhd::niusrprio; +using namespace uhd::usrprio_rpc; + +class dummy_lvbitx : public nifpga_lvbitx { +public: + dummy_lvbitx(const std::string& fpga_lvbitx_path) : _fpga_lvbitx_path(fpga_lvbitx_path) { + std::ifstream lvbitx_stream(_fpga_lvbitx_path.c_str()); + if (lvbitx_stream.is_open()) { + std::string lvbitx_contents; + lvbitx_stream.seekg(0, std::ios::end); + lvbitx_contents.reserve(static_cast<size_t>(lvbitx_stream.tellg())); + lvbitx_stream.seekg(0, std::ios::beg); + lvbitx_contents.assign((std::istreambuf_iterator<char>(lvbitx_stream)), std::istreambuf_iterator<char>()); + try { + boost::smatch md5_match; + if (boost::regex_search(lvbitx_contents, md5_match, boost::regex("<BitstreamMD5>([a-zA-Z0-9]{32})<\\/BitstreamMD5>", boost::regex::icase))) { + _bitstream_checksum = std::string(md5_match[1].first, md5_match[1].second); + } + boost::to_upper(_bitstream_checksum); + } catch (boost::exception&) { + _bitstream_checksum = ""; + } + try { + boost::smatch sig_match; + if (boost::regex_search(lvbitx_contents, sig_match, boost::regex("<SignatureRegister>([a-zA-Z0-9]{32})<\\/SignatureRegister>", boost::regex::icase))) { + _signature = std::string(sig_match[1].first, sig_match[1].second); + } + boost::to_upper(_signature); + } catch (boost::exception&) { + _signature = ""; + } + } + } + ~dummy_lvbitx() {} + + virtual const char* get_bitfile_path() { return _fpga_lvbitx_path.c_str(); } + virtual const char* get_signature() { return _signature.c_str(); } + virtual const char* get_bitstream_checksum() { return _bitstream_checksum.c_str(); } + + virtual size_t get_input_fifo_count() { return 0; } + virtual const char** get_input_fifo_names() { return NULL; } + + virtual size_t get_output_fifo_count() { return 0; } + virtual const char** get_output_fifo_names() { return NULL; } + + virtual size_t get_control_count() { return 0; } + virtual const char** get_control_names() { return NULL; } + + virtual size_t get_indicator_count() { return 0; } + virtual const char** get_indicator_names() { return NULL; } + + virtual void init_register_info(nirio_register_info_vtr& vtr) { vtr.clear(); } + virtual void init_fifo_info(nirio_fifo_info_vtr& vtr) { vtr.clear(); } + +private: + std::string _fpga_lvbitx_path; + std::string _bitstream_checksum; + std::string _signature; +}; + +int main(int argc, char *argv[]) +{ + nirio_status status = NiRio_Status_Success; + + //Setup the program options + uint32_t interface_num, peek_addr, poke_addr, poke_data; + std::string rpc_port, fpga_lvbitx_path, flash_path, peek_tokens_str, poke_tokens_str; + + namespace po = boost::program_options; + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("interface", po::value<uint32_t>(&interface_num)->default_value(0), "The interface number to communicate with.") + ("port", po::value<std::string>(&rpc_port)->default_value("5444"), "Port to communicate with RPC server.") + ("fpga", po::value<std::string>(&fpga_lvbitx_path)->default_value(""), "The absolute path to the LVBITX file to download to the FPGA.") + ("flash", po::value<std::string>(&flash_path)->default_value(""), "The path to the image to download to the flash OR 'erase' to erase the FPGA image from flash.") + ("peek", po::value<std::string>(&peek_tokens_str)->default_value(""), "Peek32.") + ("poke", po::value<std::string>(&poke_tokens_str)->default_value(""), "Poke32.") + ("stats", "Dump interface and DMA stats.") + ; + 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-NIRIO-Programmer\n\n %s") % desc << std::endl; + return ~0; + } + + std::string resource_name = boost::str(boost::format("RIO%u") % interface_num); + + //Download LVBITX image + if (fpga_lvbitx_path != "") + { + printf("Downloading image %s to FPGA as %s...", fpga_lvbitx_path.c_str(), resource_name.c_str()); + fflush(stdout); + uhd::niusrprio::niusrprio_session fpga_session(resource_name, rpc_port); + uhd::niusrprio::nifpga_lvbitx::sptr lvbitx(new dummy_lvbitx(fpga_lvbitx_path)); + nirio_status_chain(fpga_session.open(lvbitx, true), status); + //Download BIN to flash or erase + if (flash_path != "erase") { + if (flash_path != "") { + printf("Writing FPGA image %s to flash...", flash_path.c_str()); + fflush(stdout); + nirio_status_chain(fpga_session.download_bitstream_to_flash(flash_path), status); + printf("DONE\n"); + } + } else { + printf("Erasing FPGA image from flash..."); + fflush(stdout); + nirio_status_chain(fpga_session.download_bitstream_to_flash(""), status); + printf("DONE\n"); + } + fpga_session.close(); + printf("DONE\n"); + } + + fflush(stdout); + usrprio_rpc_client temp_rpc_client("localhost", rpc_port); + std::string interface_path; + nirio_status_chain(temp_rpc_client.niusrprio_get_interface_path(resource_name, interface_path), status); + if (interface_path.empty()) { + printf("ERROR: Could not open a proxy to interface %u. If it exists, try downloading an LVBITX to the FPGA first.\n", interface_num); + exit(EXIT_FAILURE); + } + + niriok_proxy dev_proxy; + dev_proxy.open(interface_path); + + if (poke_tokens_str != ""){ + std::stringstream ss; + std::vector<std::string> poke_tokens; + boost::split(poke_tokens, poke_tokens_str, boost::is_any_of(":")); + ss.clear(); + ss << std::hex << poke_tokens[1]; + ss >> poke_addr; + ss.clear(); + ss << std::hex << poke_tokens[2]; + ss >> poke_data; + + niriok_scoped_addr_space(dev_proxy, poke_tokens[0]=="c"?BUS_INTERFACE:FPGA, status); + if (poke_tokens[0]=="z") { + nirio_status_chain(dev_proxy.poke(poke_addr, (uint32_t)0x70000 + poke_addr), status); + } else { + nirio_status_chain(dev_proxy.poke(poke_addr, poke_data), status); + } + printf("[POKE] %s:0x%x <= 0x%x (%u)\n", poke_tokens[0]=="c"?"Chinch":(poke_tokens[0]=="z"?"ZPU":"FPGA"), poke_addr, poke_data, poke_data); + } + + if (peek_tokens_str != ""){ + std::stringstream ss; + std::vector<std::string> peek_tokens; + boost::split(peek_tokens, peek_tokens_str, boost::is_any_of(":")); + ss.clear(); + ss << std::hex << peek_tokens[1]; + ss >> peek_addr; + + niriok_scoped_addr_space(dev_proxy, peek_tokens[0]=="c"?BUS_INTERFACE:FPGA, status); + uint32_t reg_val; + if (peek_tokens[0]=="z") { + nirio_status_chain(dev_proxy.poke((uint32_t)0x60000 + peek_addr, (uint32_t)0), status); + do { + nirio_status_chain(dev_proxy.peek((uint32_t)0x60000 + peek_addr, reg_val), status); + } while (reg_val != 0); + nirio_status_chain(dev_proxy.peek((uint32_t)0x70000 + peek_addr, reg_val), status); + } else { + nirio_status_chain(dev_proxy.peek(peek_addr, reg_val), status); + } + + printf("[PEEK] %s:0x%x = 0x%x (%u)\n", peek_tokens[0]=="c"?"Chinch":(peek_tokens[0]=="z"?"ZPU":"FPGA"), peek_addr, reg_val, reg_val); + } + + //Display attributes + if (vm.count("stats")){ + printf("[Interface %u]\n", interface_num); + uint32_t attr_val; + nirio_status_chain(dev_proxy.get_attribute(IS_FPGA_PROGRAMMED, attr_val), status); + printf("* Is FPGA Programmed? = %s\n", (attr_val==1)?"YES":"NO"); + + std::string signature; + for (int i = 0; i < 4; i++) { + nirio_status_chain(dev_proxy.peek(0x3FFF4, attr_val), status); + signature += boost::str(boost::format("%08x") % attr_val); + } + printf("* FPGA Signature = %s\n", signature.c_str()); + + std::string checksum; + for (int i = 0; i < 4; i++) { + nirio_status_chain(dev_proxy.peek(0x40030 + (i * 4), attr_val), status); + checksum += boost::str(boost::format("%08x") % attr_val); + } + printf("* FPGA Bitstream Checksum = %s\n", checksum.c_str()); + + uint32_t reg_val; + nirio_status_chain(dev_proxy.set_attribute(ADDRESS_SPACE, BUS_INTERFACE), status); + nirio_status_chain(dev_proxy.peek(0, reg_val), status); + printf("* Chinch Signature = %x\n", reg_val); + nirio_status_chain(dev_proxy.set_attribute(ADDRESS_SPACE, FPGA), status); + nirio_status_chain(dev_proxy.peek(0, reg_val), status); + printf("* PCIe FPGA Signature = %x\n", reg_val); + + printf("\n[DMA Stream Stats]\n"); + + nirio_status_chain(dev_proxy.set_attribute(ADDRESS_SPACE, FPGA), status); + + printf("------------------------------------------------------------------------------------------------"); + printf("\nChannel => |"); + for (uint32_t i = 0; i < 6; i++) { + printf("%11u |", i); + } + printf("\n------------------------------------------------------------------------------------------------"); + printf("\nTX Status |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x40200 + (i * 16), reg_val), status); + printf("%s |", reg_val==0 ? " Good" : " Error"); + } + printf("\nRX Status |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x40400 + (i * 16), reg_val), status); + printf("%s |", reg_val==0 ? " Good" : " Error"); + } + printf("\nTX Frm Size |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x40204 + (i * 16), reg_val), status); + printf("%11u |", reg_val); + } + printf("\nRX Frm Size |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x40404 + (i * 16), reg_val), status); + printf("%11u |", reg_val); + } + printf("\nTX Pkt Count |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x4020C + (i * 16), reg_val), status); + printf("%11u |", reg_val); + } + printf("\nTX Samp Count |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x40208 + (i * 16), reg_val), status); + printf("%11u |", reg_val); + } + printf("\nRX Pkt Count |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x4040C + (i * 16), reg_val), status); + printf("%11u |", reg_val); + } + printf("\nRX Samp Count |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x40408 + (i * 16), reg_val), status); + printf("%11u |", reg_val); + } + printf("\n------------------------------------------------------------------------------------------------\n"); + } + + exit(EXIT_SUCCESS); +} + + diff --git a/host/utils/usrp_cal_utils.hpp b/host/utils/usrp_cal_utils.hpp index bab6ddd91..40626dfaa 100644 --- a/host/utils/usrp_cal_utils.hpp +++ b/host/utils/usrp_cal_utils.hpp @@ -53,7 +53,7 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ const uhd::fs_path mb_path = "/mboards/0"; const std::string mb_name = tree->access<std::string>(mb_path / "name").get(); - if (mb_name.find("USRP2") != std::string::npos or mb_name.find("N200") != std::string::npos or mb_name.find("N210") != std::string::npos){ + if (mb_name.find("USRP2") != std::string::npos or mb_name.find("N200") != std::string::npos or mb_name.find("N210") != std::string::npos or mb_name.find("X300") != std::string::npos or mb_name.find("X310") != std::string::npos){ usrp->set_tx_rate(12.5e6); usrp->set_rx_rate(12.5e6); } diff --git a/host/utils/usrp_n2xx_simple_net_burner.cpp b/host/utils/usrp_n2xx_simple_net_burner.cpp index 1898ee9ae..290612f8b 100644 --- a/host/utils/usrp_n2xx_simple_net_burner.cpp +++ b/host/utils/usrp_n2xx_simple_net_burner.cpp @@ -511,6 +511,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ verify_image(udp_transport, true, fw_image, flash_info[1], fw_image_size); } + delete(flash_info); + //Reset USRP N2XX bool reset = false; if(auto_reboot) reset = true; diff --git a/host/utils/usrp_x3xx_fpga_burner.cpp b/host/utils/usrp_x3xx_fpga_burner.cpp new file mode 100644 index 000000000..07bc63559 --- /dev/null +++ b/host/utils/usrp_x3xx_fpga_burner.cpp @@ -0,0 +1,498 @@ +// +// Copyright 2013-2014 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 <iostream> +#include <map> +#include <fstream> +#include <stdexcept> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <vector> + +#include <boost/foreach.hpp> +#include <boost/asio.hpp> +#include <boost/program_options.hpp> +#include <boost/property_tree/ptree.hpp> +#include <boost/property_tree/xml_parser.hpp> +#include <boost/assign.hpp> +#include <boost/cstdint.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/algorithm/string/erase.hpp> +#include <boost/filesystem.hpp> +#include <boost/thread/thread.hpp> + +#include <uhd/exception.hpp> +#include <uhd/transport/if_addrs.hpp> +#include <uhd/transport/nirio/niusrprio_session.h> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/device.hpp> +#include <uhd/types/device_addr.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/images.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/utils/safe_call.hpp> + +#ifdef _MSC_VER +extern "C" { +#endif +#include "cdecode.h" +#ifdef _MSC_VER +} +#endif + +#define X300_FPGA_BIN_SIZE_BYTES 15877916 +#define X300_FPGA_BIT_MAX_SIZE_BYTES 15878022 +#define X300_FPGA_PROG_UDP_PORT 49157 +#define X300_FLASH_SECTOR_SIZE 131072 +#define X300_PACKET_SIZE_BYTES 256 +#define X300_FPGA_SECTOR_START 32 +#define X300_MAX_RESPONSE_BYTES 128 +#define UDP_TIMEOUT 3 +#define FPGA_LOAD_TIMEOUT 15 + +#define X300_FPGA_PROG_FLAGS_ACK 1 +#define X300_FPGA_PROG_FLAGS_ERROR 2 +#define X300_FPGA_PROG_FLAGS_INIT 4 +#define X300_FPGA_PROG_FLAGS_CLEANUP 8 +#define X300_FPGA_PROG_FLAGS_ERASE 16 +#define X300_FPGA_PROG_FLAGS_VERIFY 32 +#define X300_FPGA_PROG_CONFIGURE 64 +#define X300_FPGA_PROG_CONFIG_STATUS 128 + +namespace fs = boost::filesystem; +namespace po = boost::program_options; + +using namespace uhd; +using namespace uhd::transport; + +typedef struct { + boost::uint32_t flags; + boost::uint32_t sector; + boost::uint32_t index; + boost::uint32_t size; + boost::uint16_t data[128]; +} x300_fpga_update_data_t; + +boost::uint8_t x300_data_in_mem[udp_simple::mtu]; +boost::uint8_t intermediary_packet_data[X300_PACKET_SIZE_BYTES]; + +boost::uint8_t bitswap(uint8_t b){ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); + + return b; +} + +void list_usrps(){ + device_addrs_t found_devices = device::find(device_addr_t("type=x300")); + + std::cout << "Available X3x0 devices:" << std::endl; + BOOST_FOREACH(const device_addr_t &dev, found_devices){ + std::string dev_string; + if(dev.has_key("addr")){ + dev_string = str(boost::format(" * %s (%s, addr: %s)") + % dev["product"] + % dev["fpga"] + % dev["addr"]); + } + else{ + dev_string = str(boost::format(" * %s (%s, resource: %s)") + % dev["product"] + % dev["fpga"] + % dev["resource"]); + } + std::cout << dev_string << std::endl; + } +} + +device_addr_t find_usrp_with_ethernet(std::string ip_addr, bool output){ + if(output) std::cout << "Attempting to find X3x0 with IP address: " << ip_addr << std::endl; + const device_addr_t dev = device_addr_t(str(boost::format("addr=%s") % ip_addr)); + device_addrs_t found_devices = device::find(dev); + + if(found_devices.size() < 1) { + throw std::runtime_error("Could not find X3x0 with the specified address!"); + } + else if(found_devices.size() > 1) { + throw std::runtime_error("Found multiple X3x0 units with the specified address!"); + } + else { + if(output) std::cout << (boost::format("Found %s (%s).\n\n") + % found_devices[0]["product"] + % found_devices[0]["fpga"]); + } + return found_devices[0]; +} + +device_addr_t find_usrp_with_pcie(std::string resource, bool output){ + if(output) std::cout << "Attempting to find X3x0 with resource: " << resource << std::endl; + const device_addr_t dev = device_addr_t(str(boost::format("resource=%s") % resource)); + device_addrs_t found_devices = device::find(dev); + + if(found_devices.size() < 1) { + throw std::runtime_error("Could not find X3x0 with the specified resource!"); + } + else { + if(output) std::cout << (boost::format("Found %s (%s).\n\n") + % found_devices[0]["product"] + % found_devices[0]["fpga"]); + } + return found_devices[0]; +} + +std::string get_default_image_path(std::string model, std::string image_type){ + std::transform(model.begin(), model.end(), model.begin(), ::tolower); + + std::string image_name = str(boost::format("usrp_%s_fpga_%s.bit") + % model.c_str() % image_type.c_str()); + + return find_image_path(image_name); +} + +void extract_from_lvbitx(std::string lvbitx_path, std::vector<char> &bitstream){ + boost::property_tree::ptree pt; + boost::property_tree::xml_parser::read_xml(lvbitx_path.c_str(), pt, + boost::property_tree::xml_parser::no_comments | + boost::property_tree::xml_parser::trim_whitespace); + std::string const encoded_bitstream(pt.get<std::string>("Bitfile.Bitstream")); + std::vector<char> decoded_bitstream(encoded_bitstream.size()); + + base64_decodestate decode_state; + base64_init_decodestate(&decode_state); + size_t const decoded_size = base64_decode_block(encoded_bitstream.c_str(), + encoded_bitstream.size(), &decoded_bitstream.front(), &decode_state); + decoded_bitstream.resize(decoded_size); + bitstream.swap(decoded_bitstream); +} + +void ethernet_burn(udp_simple::sptr udp_transport, std::string fpga_path, bool verify){ + boost::uint32_t max_size; + std::vector<char> bitstream; + + if(fs::extension(fpga_path) == ".bit") max_size = X300_FPGA_BIT_MAX_SIZE_BYTES; + else max_size = X300_FPGA_BIN_SIZE_BYTES; //Use for both .bin and .lvbitx + + bool is_lvbitx = (fs::extension(fpga_path) == ".lvbitx"); + + size_t fpga_image_size; + FILE* file; + if((file = fopen(fpga_path.c_str(), "rb"))){ + fseek(file, 0, SEEK_END); + if(is_lvbitx){ + extract_from_lvbitx(fpga_path, bitstream); + fpga_image_size = bitstream.size(); + } + else fpga_image_size = ftell(file); + if(fpga_image_size > max_size){ + fclose(file); + throw std::runtime_error(str(boost::format("FPGA size is too large (%d > %d).") + % fpga_image_size % max_size)); + } + rewind(file); + } + else{ + throw std::runtime_error(str(boost::format("Could not find FPGA image at location: %s") + % fpga_path.c_str())); + } + + const x300_fpga_update_data_t *update_data_in = reinterpret_cast<const x300_fpga_update_data_t *>(x300_data_in_mem); + + x300_fpga_update_data_t ack_packet; + ack_packet.flags = htonx<boost::uint32_t>(X300_FPGA_PROG_FLAGS_ACK | X300_FPGA_PROG_FLAGS_INIT); + ack_packet.sector = 0; + ack_packet.size = 0; + ack_packet.index = 0; + memset(ack_packet.data, 0, sizeof(ack_packet.data)); + udp_transport->send(boost::asio::buffer(&ack_packet, sizeof(ack_packet))); + + udp_transport->recv(boost::asio::buffer(x300_data_in_mem), UDP_TIMEOUT); + if((ntohl(update_data_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) != X300_FPGA_PROG_FLAGS_ERROR){ + std::cout << "Burning image: " << fpga_path << std::endl; + if(verify) std::cout << "NOTE: Verifying image. Burning will take much longer." << std::endl; + std::cout << std::endl; + } + else{ + throw std::runtime_error("Failed to start image burning! Did you specify the correct IP address? If so, power-cycle the device and try again."); + } + + std::cout << "Progress: " << std::flush; + + int percentage = -1; + int last_percentage = -1; + size_t current_pos = 0; + + //Each sector + for(size_t i = 0; i < fpga_image_size; i += X300_FLASH_SECTOR_SIZE){ + + //Print percentage at beginning of first sector after each 10% + percentage = int(double(i)/double(fpga_image_size)*100); + if((percentage != last_percentage) and (percentage % 10 == 0)){ //Don't print same percentage twice + std::cout << percentage << "%..." << std::flush; + } + last_percentage = percentage; + + //Each packet + for(size_t j = i; (j < fpga_image_size and j < (i+X300_FLASH_SECTOR_SIZE)); j += X300_PACKET_SIZE_BYTES){ + x300_fpga_update_data_t send_packet; + + send_packet.flags = X300_FPGA_PROG_FLAGS_ACK; + if(verify) send_packet.flags |= X300_FPGA_PROG_FLAGS_VERIFY; + if(j == i) send_packet.flags |= X300_FPGA_PROG_FLAGS_ERASE; //Erase the sector before writing + send_packet.flags = htonx<boost::uint32_t>(send_packet.flags); + + send_packet.sector = htonx<boost::uint32_t>(X300_FPGA_SECTOR_START + (i/X300_FLASH_SECTOR_SIZE)); + send_packet.index = htonx<boost::uint32_t>((j % X300_FLASH_SECTOR_SIZE) / 2); + send_packet.size = htonx<boost::uint32_t>(X300_PACKET_SIZE_BYTES / 2); + memset(intermediary_packet_data,0,X300_PACKET_SIZE_BYTES); + memset(send_packet.data,0,X300_PACKET_SIZE_BYTES); + if(!is_lvbitx) current_pos = ftell(file); + + if(current_pos + X300_PACKET_SIZE_BYTES > fpga_image_size){ + if(is_lvbitx){ + memcpy(intermediary_packet_data, (&bitstream[current_pos]), (bitstream.size()-current_pos+1)); + } + else{ + size_t len = fread(intermediary_packet_data, sizeof(boost::uint8_t), (fpga_image_size-current_pos), file); + if(len != (fpga_image_size-current_pos)){ + throw std::runtime_error("Error reading from file!"); + } + } + } + else{ + if(is_lvbitx){ + memcpy(intermediary_packet_data, (&bitstream[current_pos]), X300_PACKET_SIZE_BYTES); + current_pos += X300_PACKET_SIZE_BYTES; + } + else{ + size_t len = fread(intermediary_packet_data, sizeof(boost::uint8_t), X300_PACKET_SIZE_BYTES, file); + if(len != X300_PACKET_SIZE_BYTES){ + throw std::runtime_error("Error reading from file!"); + } + } + } + + for(size_t k = 0; k < X300_PACKET_SIZE_BYTES; k++){ + intermediary_packet_data[k] = bitswap(intermediary_packet_data[k]); + } + + memcpy(send_packet.data, intermediary_packet_data, X300_PACKET_SIZE_BYTES); + + for(size_t k = 0; k < (X300_PACKET_SIZE_BYTES/2); k++){ + send_packet.data[k] = htonx<boost::uint16_t>(send_packet.data[k]); + } + + udp_transport->send(boost::asio::buffer(&send_packet, sizeof(send_packet))); + + udp_transport->recv(boost::asio::buffer(x300_data_in_mem), UDP_TIMEOUT); + const x300_fpga_update_data_t *update_data_in = reinterpret_cast<const x300_fpga_update_data_t *>(x300_data_in_mem); + + if((ntohl(update_data_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) == X300_FPGA_PROG_FLAGS_ERROR){ + throw std::runtime_error("Transfer or data verification failed!"); + } + } + } + fclose(file); + + //Send clean-up signal + x300_fpga_update_data_t cleanup_packet; + cleanup_packet.flags = htonx<boost::uint32_t>(X300_FPGA_PROG_FLAGS_ACK | X300_FPGA_PROG_FLAGS_CLEANUP); + cleanup_packet.sector = 0; + cleanup_packet.size = 0; + cleanup_packet.index = 0; + memset(cleanup_packet.data, 0, sizeof(cleanup_packet.data)); + udp_transport->send(boost::asio::buffer(&cleanup_packet, sizeof(cleanup_packet))); + + udp_transport->recv(boost::asio::buffer(x300_data_in_mem), UDP_TIMEOUT); + const x300_fpga_update_data_t *cleanup_data_in = reinterpret_cast<const x300_fpga_update_data_t *>(x300_data_in_mem); + + if((ntohl(cleanup_data_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) == X300_FPGA_PROG_FLAGS_ERROR){ + throw std::runtime_error("Transfer or data verification failed!"); + } + + std::cout << "100%" << std::endl; +} + +void pcie_burn(std::string resource, std::string rpc_port, std::string fpga_path) +{ + std::cout << "Burning image: " << fpga_path << std::endl; + std::cout << "This will take 3-10 minutes." << std::endl; + + nirio_status status = NiRio_Status_Success; + + uhd::niusrprio::niusrprio_session fpga_session(resource, rpc_port); + nirio_status_chain(fpga_session.download_bitstream_to_flash(fpga_path), status); + + if(nirio_status_fatal(status)) throw std::runtime_error("Failed to burn FPGA image!"); +} + +bool configure_fpga(udp_simple::sptr udp_transport, std::string ip_addr){ + x300_fpga_update_data_t configure_packet; + configure_packet.flags = htonx<boost::uint32_t>(X300_FPGA_PROG_CONFIGURE | X300_FPGA_PROG_FLAGS_ACK); + configure_packet.sector = 0; + configure_packet.size = 0; + configure_packet.index = 0; + memset(configure_packet.data, 0, sizeof(configure_packet.data)); + udp_transport->send(boost::asio::buffer(&configure_packet, sizeof(configure_packet))); + + udp_transport->recv(boost::asio::buffer(x300_data_in_mem), UDP_TIMEOUT); + const x300_fpga_update_data_t *configure_data_in = reinterpret_cast<const x300_fpga_update_data_t *>(x300_data_in_mem); + bool successful = false; + + if((ntohl(configure_data_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) == X300_FPGA_PROG_FLAGS_ERROR){ + throw std::runtime_error("Transfer or data verification failed!"); + } + else{ + std::cout << std::endl << "Waiting for X3x0 to configure FPGA image and reload." << std::endl; + boost::this_thread::sleep(boost::posix_time::milliseconds(5000)); + + x300_fpga_update_data_t config_status_packet; + configure_packet.flags = htonx<boost::uint32_t>(X300_FPGA_PROG_CONFIG_STATUS); + config_status_packet.sector = 0; + config_status_packet.size = 0; + config_status_packet.index = 0; + memset(config_status_packet.data, 0, sizeof(config_status_packet.data)); + for(int i = 0; i < 5; i++){ + udp_transport->send(boost::asio::buffer(&config_status_packet, sizeof(config_status_packet))); + udp_transport->recv(boost::asio::buffer(x300_data_in_mem), 1); + const x300_fpga_update_data_t *config_status_data_in = reinterpret_cast<const x300_fpga_update_data_t *>(x300_data_in_mem); + + if((ntohl(config_status_data_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) != X300_FPGA_PROG_FLAGS_ERROR + and udp_transport->get_recv_addr() == ip_addr){ + successful = true; + break; + } + successful = false; //If it worked, the break would skip this + } + } + return successful; +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + memset(intermediary_packet_data, 0, X300_PACKET_SIZE_BYTES); + std::string ip_addr, resource, fpga_path, image_type, rpc_port; + + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "Display this help message.") + ("addr", po::value<std::string>(&ip_addr), "Specify an IP address.") + ("resource", po::value<std::string>(&resource), "Specify an NI-RIO resource.") + ("rpc-port", po::value<std::string>(&rpc_port)->default_value("5444"), "Specify a port to communicate with the RPC server.") + ("type", po::value<std::string>(&image_type), "Specify an image type (1G, HGS, XGS), leave blank for current type.") + ("fpga-path", po::value<std::string>(&fpga_path), "Specify an FPGA path (overrides --type option).") + ("configure", "Initialize FPGA with image currently burned to flash (Ethernet only).") + ("verify", "Verify data downloaded to flash (Ethernet only, download will take much longer)") + ("list", "List all available X3x0 devices.") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //Print help message + if(vm.count("help")){ + std::cout << "USRP X3x0 FPGA Burner" << std::endl << std::endl; + + std::cout << "Burns an FPGA image onto a USRP X300/X310. To burn the image" << std::endl + << "over Ethernet, specify an IP address with the --addr option," << std::endl + << "or to burn over PCIe, specify an NI-RIO resource (ex. RIO0)" << std::endl + << "with the --resource option." << std::endl << std::endl; + + std::cout << desc << std::endl; + return EXIT_SUCCESS; + } + + //List all available devices + if(vm.count("list")){ + list_usrps(); + return EXIT_SUCCESS; + } + + /* + * The user must specify whether to burn the image over Ethernet or PCI-e. + */ + if(not (vm.count("addr") xor vm.count("resource"))){ + throw std::runtime_error("You must specify addr OR resource!"); + } + + /* + * With settings validated, find X3x0 with specified arguments. + */ + device_addr_t dev = (vm.count("addr")) ? find_usrp_with_ethernet(ip_addr, true) + : find_usrp_with_pcie(resource, true); + + /* + * If custom FPGA path is given, ignore specified type and let FPGA + * figure it out. + */ + if(vm.count("fpga-path")){ + //Expand tilde usage if applicable + #ifndef UHD_PLATFORM_WIN32 + if(fpga_path.find("~/") == 0) fpga_path.replace(0,1,getenv("HOME")); + #endif + } + else{ + if(vm.count("type")){ + //Make sure the specified type is 1G, HGS, or XGS + if((image_type != "1G") and (image_type != "HGS") and (image_type != "XGS")){ + throw std::runtime_error("--type must be 1G, HGS, or XGS!"); + } + else fpga_path = get_default_image_path(dev["product"], image_type); + } + else{ + //Use default image of currently present FPGA type + fpga_path = get_default_image_path(dev["product"], dev["fpga"]); + } + } + + + /* + * Check validity of image through extension + */ + std::string ext = fs::extension(fpga_path.c_str()); + if(ext != ".bin" and ext != ".bit" and ext != ".lvbitx"){ + throw std::runtime_error("The image filename must end in .bin, .bit, or .lvbitx."); + } + + if(vm.count("addr")){ + udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(X300_FPGA_PROG_UDP_PORT)); + + ethernet_burn(udp_transport, fpga_path, vm.count("verify")); + + if(vm.count("configure")){ + if(configure_fpga(udp_transport, ip_addr)) std::cout << "Successfully configured FPGA!" << std::endl; + else throw std::runtime_error("FPGA configuring failed!"); + } + } + else pcie_burn(resource, rpc_port, fpga_path); + + /* + * Attempt to find USRP after burning + */ + std::cout << std::endl << "Attempting to find device..." << std::flush; + boost::this_thread::sleep(boost::posix_time::milliseconds(2000)); //Sometimes needed for Ethernet to reconnect + device_addr_t found_usrp = (vm.count("addr")) ? find_usrp_with_ethernet(ip_addr, false) + : find_usrp_with_pcie(resource, false); + std::cout << "found!" << std::endl; //If unsuccessful, runtime error would occur in find functions + std::cout << "Successfully burned FPGA image!" << std::endl << std::endl; + + if(vm.count("addr")) std::cout << str(boost::format("Power-cycle the USRP %s to use the new image.") % found_usrp["product"]) << std::endl; + else std::cout << str(boost::format("Power-cycle the USRP %s and reboot your machine to use the new image.") % found_usrp["product"]) << std::endl; + + return EXIT_SUCCESS; +} |