aboutsummaryrefslogtreecommitdiffstats
path: root/host/utils
diff options
context:
space:
mode:
Diffstat (limited to 'host/utils')
-rw-r--r--host/utils/CMakeLists.txt16
-rw-r--r--host/utils/cdecode.c80
-rw-r--r--host/utils/cdecode.h28
-rw-r--r--host/utils/nirio_programmer.cpp275
-rw-r--r--host/utils/usrp_cal_utils.hpp2
-rw-r--r--host/utils/usrp_n2xx_simple_net_burner.cpp2
-rw-r--r--host/utils/usrp_x3xx_fpga_burner.cpp498
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;
+}