diff options
57 files changed, 1239 insertions, 2556 deletions
diff --git a/.gitmodules b/.gitmodules index 473a21289..c85c089b3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "fpga-src"] path = fpga-src url = https://github.com/EttusResearch/fpga.git - branch = maint + branch = master diff --git a/firmware/fx2/b100/CMakeLists.txt b/firmware/fx2/b100/CMakeLists.txt index 438aa9207..18bc2080a 100644 --- a/firmware/fx2/b100/CMakeLists.txt +++ b/firmware/fx2/b100/CMakeLists.txt @@ -84,5 +84,11 @@ add_custom_target(b100_eeprom ALL COMMAND ${PYTHON_EXECUTABLE} ${BUILD_EEPROM} -r2 b100_boot.bin b100_eeprom.bin ) +add_custom_target(b100_eeprom_header ALL + DEPENDS b100_eeprom + COMMAND xxd -i b100_eeprom.bin ${CMAKE_SOURCE_DIR}/../../host/utils/b100_eeprom.h + COMMAND sed -i 's/char/const char/' ${CMAKE_SOURCE_DIR}/../../host/utils/b100_eeprom.h +) + add_executable(b100_boot ${eeprom1p_sources}) target_link_libraries(b100_boot libb100) diff --git a/firmware/fx2/usrp1/CMakeLists.txt b/firmware/fx2/usrp1/CMakeLists.txt index 6607bc7f2..8b5389535 100644 --- a/firmware/fx2/usrp1/CMakeLists.txt +++ b/firmware/fx2/usrp1/CMakeLists.txt @@ -80,5 +80,11 @@ add_custom_target(usrp1_eeprom ALL COMMAND ${PYTHON_EXECUTABLE} ${BUILD_EEPROM} -r1 usrp1_boot.bin usrp1_eeprom.bin ) +add_custom_target(usrp1_eeprom_header ALL + DEPENDS usrp1_eeprom + COMMAND xxd -i usrp1_eeprom.bin ${CMAKE_SOURCE_DIR}/../../host/utils/usrp1_eeprom.h + COMMAND sed -i 's/char/const char/' ${CMAKE_SOURCE_DIR}/../../host/utils/usrp1_eeprom.h +) + add_executable(usrp1_boot ${eeprom1_sources}) target_link_libraries(usrp1_boot libusrp1) diff --git a/firmware/fx3/b200/b200_main.c b/firmware/fx3/b200/b200_main.c index e552d3177..e5c55d6a7 100644 --- a/firmware/fx3/b200/b200_main.c +++ b/firmware/fx3/b200/b200_main.c @@ -228,7 +228,7 @@ typedef struct Config { int enable_as_superspeed; // 1 int pport_drive_strength; // CY_U3P_DS_THREE_QUARTER_STRENGTH int dma_buffer_size; // [USB3] (max) - int dma_buffer_count; // [USB3] 1 + int dma_buffer_count; // [USB3] 2 int manual_dma; // 0 int sb_baud_div; // 434*2 } CONFIG, *PCONFIG; @@ -244,8 +244,8 @@ static CONFIG g_config = { 0, // disable_usb2 1, // enable_as_superspeed CY_U3P_DS_THREE_QUARTER_STRENGTH, // pport_drive_strength - 64512, // dma_buffer_size 2**16-1, then aligned to next page boundary - 1, // dma_buffer_count + 16*1024, // dma_buffer_size - optimized value from Cypress AN86947 + 2, // dma_buffer_count - optimized value from Cypress AN86947 0, // manual_dma 434*2 // sb_baud_div }; @@ -1175,7 +1175,7 @@ void b200_fw_start(void) { data_buffer_size_from_host = data_buffer_size; g_vendor_req_buff_size = USB3_VREQ_BUF_SIZE; // Max 512 - num_packets_per_burst = USB3_PACKETS_PER_BURST*1+4*0; // 16 + num_packets_per_burst = USB3_PACKETS_PER_BURST; // 8 break; case CY_U3P_NOT_CONNECTED: @@ -1529,7 +1529,7 @@ void event_usb_callback (CyU3PUsbEventType_t event_type, uint16_t event_data) { * of this function is to register that the event happened at all, so that the * application thread knows it can proceed. * - * This function is also responsible for receiving vendor requests, and trigging + * This function is also responsible for receiving vendor requests, and triggering * the appropriate RTOS event to wake up the vendor request handler thread. */ CyBool_t usb_setup_callback(uint32_t data0, uint32_t data1) { diff --git a/firmware/fx3/b200/b200_main.h b/firmware/fx3/b200/b200_main.h index 9fe8b9511..453e7034a 100644 --- a/firmware/fx3/b200/b200_main.h +++ b/firmware/fx3/b200/b200_main.h @@ -41,7 +41,7 @@ #define MASK_GPIO_FPGA_SB_SCL (uint32_t)(1 << (GPIO_FPGA_SB_SCL - 0)) #define MASK_GPIO_FPGA_SB_SDA (uint32_t)(1 << (GPIO_FPGA_SB_SDA - 0)) -#define USB3_PACKETS_PER_BURST (16) +#define USB3_PACKETS_PER_BURST (8) // Optimized value from Cypress AN86947 #define USB2_PACKETS_PER_BURST (1) #define DMA_SIZE_INFINITE (0) diff --git a/firmware/usrp3/x300/x300_aurora_bist.py b/firmware/usrp3/x300/x300_aurora_bist.py index f5e119b66..5f7a22a3b 100755 --- a/firmware/usrp3/x300/x300_aurora_bist.py +++ b/firmware/usrp3/x300/x300_aurora_bist.py @@ -43,8 +43,9 @@ SFP_TYPE_AURORA = 2 MAC_REG_CTRL = 0 MAC_REG_STATUS = 0 MAC_REG_OVERRUNS = 4 -MAC_REG_BIST_SAMPS = 8 -MAC_REG_BIST_ERRORS = 12 +MAC_REG_CHECKSUM_ERRS = 8 +MAC_REG_BIST_SAMPS = 12 +MAC_REG_BIST_ERRORS = 16 MAC_STATUS_LINK_UP_MSK = 0x00000001 MAC_STATUS_HARD_ERR_MSK = 0x00000002 @@ -52,19 +53,16 @@ MAC_STATUS_SOFT_ERR_MSK = 0x00000004 MAC_STATUS_BIST_LOCKED_MSK = 0x00000008 MAC_STATUS_BIST_LATENCY_MSK = 0x000FFFF0 MAC_STATUS_BIST_LATENCY_OFFSET = 4 -MAC_STATUS_CHECKSUM_ERRS_MSK = 0xFFF00000 -MAC_STATUS_CHECKSUM_ERRS_OFFSET = 20 MAC_CTRL_BIST_CHECKER_EN = 0x00000001 MAC_CTRL_BIST_GEN_EN = 0x00000002 MAC_CTRL_BIST_LOOPBACK_EN = 0x00000004 -MAC_CTRL_PHY_RESET = 0x00000100 -MAC_CTRL_BIST_RATE_MSK = 0x000000F8 +MAC_CTRL_PHY_RESET = 0x00000200 +MAC_CTRL_BIST_RATE_MSK = 0x000001F8 MAC_CTRL_BIST_RATE_OFFSET = 3 -AURORA_CLK_RATE = 156.25e6 BUS_CLK_RATE = 166.66e6 -BIST_MAX_TIME_LIMIT = math.floor(pow(2,48)/AURORA_CLK_RATE)-1 +BIST_MAX_TIME_LIMIT = math.floor(pow(2,48)/BUS_CLK_RATE)-1 ######################################################################## # utils @@ -83,10 +81,11 @@ def get_aurora_info(ctrl): return (aur_port, link_up) def get_rate_setting(rate): - for div in range(2,32): - if (rate < 8e-6 * BUS_CLK_RATE * (1.0 - 1.0/div)): - return (div-1, 8e-6 * BUS_CLK_RATE * (1.0 - 1.0/(div-1))) - return (0, 8e-6 * BUS_CLK_RATE) + RATE_RES_BITS = 6 + rate_sett = int((1.0*rate/(8e-6*BUS_CLK_RATE))*pow(2, RATE_RES_BITS))-1 # Round rate down + rate_sett = numpy.clip(rate_sett, 0, pow(2, RATE_RES_BITS)-1) + coerced_rate = ((1.0+rate_sett)/pow(2, RATE_RES_BITS))*(8e-6*BUS_CLK_RATE) + return (rate_sett, coerced_rate) def run_ber_loopback_bist(ctrls, duration, rate_tuple): (rate_sett, rate) = rate_tuple @@ -107,10 +106,7 @@ def run_ber_loopback_bist(ctrls, duration, rate_tuple): # Put the slave in loopback mode and the master in BIST mode if 'slave' in ctrls: ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_LOOPBACK_EN) - if rate_sett == 0: - master_ctrl = MAC_CTRL_BIST_GEN_EN|MAC_CTRL_BIST_CHECKER_EN - else: - master_ctrl = ((rate_sett - 1) << MAC_CTRL_BIST_RATE_OFFSET)|MAC_CTRL_BIST_GEN_EN|MAC_CTRL_BIST_CHECKER_EN + master_ctrl = (rate_sett<<MAC_CTRL_BIST_RATE_OFFSET)|MAC_CTRL_BIST_GEN_EN|MAC_CTRL_BIST_CHECKER_EN ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, master_ctrl) start_time = datetime.datetime.now() # Wait and check if BIST locked @@ -153,7 +149,7 @@ def run_ber_loopback_bist(ctrls, duration, rate_tuple): print('[INFO] BIST Complete!') print('- Elapsed Time = ' + str(time_diff)) print('- Max BER (Bit Error Ratio) = %.4g (%d errors out of %d)'%((mst_errors+1)/mst_samps,mst_errors,mst_samps)) - print('- Max Roundtrip Latency = %.1fus'%(1e6*mst_latency_cyc/AURORA_CLK_RATE)) + print('- Max Roundtrip Latency = %.1fus'%(1e6*mst_latency_cyc/BUS_CLK_RATE)) print('- Approx Throughput = %.0fMB/s'%((8e-6*mst_samps)/time_diff.total_seconds())) else: print('[ERROR] BIST Failed!') @@ -187,10 +183,7 @@ def run_latency_loopback_bist(ctrls, duration, rate_tuple): # Put the slave in loopback mode and the master in BIST mode if 'slave' in ctrls: ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_LOOPBACK_EN) - if rate_sett == 0: - master_ctrl = MAC_CTRL_BIST_GEN_EN|MAC_CTRL_BIST_CHECKER_EN - else: - master_ctrl = ((rate_sett - 1) << MAC_CTRL_BIST_RATE_OFFSET)|MAC_CTRL_BIST_GEN_EN|MAC_CTRL_BIST_CHECKER_EN + master_ctrl = (rate_sett<<MAC_CTRL_BIST_RATE_OFFSET)|MAC_CTRL_BIST_GEN_EN|MAC_CTRL_BIST_CHECKER_EN start_time = datetime.datetime.now() latencies = [] @@ -216,7 +209,7 @@ def run_latency_loopback_bist(ctrls, duration, rate_tuple): ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, 0) # Compure latency mst_latency_cyc = 16.0*((mst_status & MAC_STATUS_BIST_LATENCY_MSK) >> MAC_STATUS_BIST_LATENCY_OFFSET) - mst_latency_us = 1e6*mst_latency_cyc/AURORA_CLK_RATE + mst_latency_us = 1e6*mst_latency_cyc/BUS_CLK_RATE latencies.append(mst_latency_us) except KeyboardInterrupt: print('[WARNING] Operation cancelled by user.') @@ -233,7 +226,7 @@ def run_latency_loopback_bist(ctrls, duration, rate_tuple): print('- Elapsed Time = ' + str(stop_time - start_time)) print('- Roundtrip Latency Mean = %.2fus'%(numpy.mean(latencies))) print('- Roundtrip Latency Stdev = %.2fus'%(numpy.std(latencies))) - # Turn off BIST loopback + # Turn off BIST loopback time.sleep(0.5) if 'slave' in ctrls: ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, 0) @@ -304,4 +297,3 @@ if __name__=='__main__': run_ber_loopback_bist(ctrls, options.duration, get_rate_setting(options.rate)) else: run_latency_loopback_bist(ctrls, options.duration, get_rate_setting(options.rate)) - diff --git a/fpga-src b/fpga-src -Subproject 3212f77ffbc8672d044372241771a25a225a0cc +Subproject 0821320bdf59756dc8f29243db18f0e8111aa70 diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index 22f193ef6..a7274a451 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -80,9 +80,25 @@ ELSEIF(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") ENDIF() ENDIF() +IF(CMAKE_VERSION VERSION_LESS "3.1") + IF(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") + SET(CMAKE_CXX_FLAGS "--std=gnu++11 ${CMAKE_CXX_FLAGS}") + ELSEIF(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + IF("${IS_APPLE}" STREQUAL "") + SET(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") + ELSE() + SET(CMAKE_CXX_FLAGS "-std=c++11 -stdlib=libc++ ${CMAKE_CXX_FLAGS}") + ENDIF() + ENDIF() +ELSE() + SET(CMAKE_CXX_STANDARD 11) +ENDIF() + IF(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" AND ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") SET(CMAKE_EXE_LINKER_FLAGS "-lthr ${CMAKE_EXE_LINKER_FLAGS}") + SET(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}") ENDIF() + ######################################################################## # Packaging Variables ######################################################################## @@ -177,7 +193,7 @@ INCLUDE(CheckCXXCompilerFlag) MACRO(UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG flag have) CHECK_CXX_COMPILER_FLAG(${flag} ${have}) IF(${have}) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}") ENDIF(${have}) ENDMACRO(UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG) @@ -195,7 +211,7 @@ IF(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") IF(STRIP_BINARIES) IF(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s") ENDIF(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") ENDIF(STRIP_BINARIES) ADD_DEFINITIONS(-Wall) diff --git a/host/cmake/Modules/UHDConfigVersion.cmake.in b/host/cmake/Modules/UHDConfigVersion.cmake.in index 549798324..c792b5bf8 100644 --- a/host/cmake/Modules/UHDConfigVersion.cmake.in +++ b/host/cmake/Modules/UHDConfigVersion.cmake.in @@ -29,13 +29,13 @@ set(ENV{UHD_CONFIG_VERSION_USED} TRUE) # version values as set in cmake/Modules/UHDVersion.cmake, placed # statically in here to avoid using Python all over again. -SET(MAJOR_VERSION @TRIMMED_VERSION_MAJOR@) -SET(API_VERSION @TRIMMED_VERSION_API@) -SET(ABI_VERSION @TRIMMED_VERSION_ABI@) -SET(PATCH_VERSION @TRIMMED_VERSION_PATCH@) +SET(MAJOR_VERSION @UHD_VERSION_MAJOR@) +SET(API_VERSION @UHD_VERSION_API@) +SET(ABI_VERSION @UHD_VERSION_ABI@) +SET(PATCH_VERSION @UHD_VERSION_PATCH@) SET(DEVEL_VERSION @UHD_VERSION_DEVEL@) -SET(PACKAGE_VERSION @TRIMMED_UHD_VERSION@) +SET(PACKAGE_VERSION @UHD_VERSION@) SET(ENV{UHD_PACKAGE_VERSION} ${PACKAGE_VERSION}) # There is a bug in CMake whereby calling "find_package(FOO)" within @@ -175,4 +175,4 @@ IF(${PACKAGE_FIND_VERSION} VERSION_EQUAL ${PACKAGE_VERSION}) ENDIF() # Undo our patch-version-number hack -SET(PACKAGE_VERSION @TRIMMED_UHD_VERSION@) +SET(PACKAGE_VERSION @UHD_VERSION@) diff --git a/host/cmake/Modules/UHDGlobalDefs.cmake b/host/cmake/Modules/UHDGlobalDefs.cmake index 70d1a654b..ada6371fb 100644 --- a/host/cmake/Modules/UHDGlobalDefs.cmake +++ b/host/cmake/Modules/UHDGlobalDefs.cmake @@ -24,9 +24,9 @@ CHECK_CXX_SYMBOL_EXISTS(log2 cmath HAVE_LOG2) ## Macros for the version number IF(UHD_VERSION_DEVEL) - MATH(EXPR UHD_VERSION_ADDED "1000000 * ${TRIMMED_VERSION_MAJOR} + 10000 * ${TRIMMED_VERSION_API} + 100 * ${TRIMMED_VERSION_ABI} + 99") + MATH(EXPR UHD_VERSION_ADDED "1000000 * ${UHD_VERSION_MAJOR} + 10000 * ${UHD_VERSION_API} + 100 * ${UHD_VERSION_ABI} + 99") ELSE() - MATH(EXPR UHD_VERSION_ADDED "1000000 * ${TRIMMED_VERSION_MAJOR} + 10000 * ${TRIMMED_VERSION_API} + 100 * ${TRIMMED_VERSION_ABI} + ${TRIMMED_VERSION_PATCH}") + MATH(EXPR UHD_VERSION_ADDED "1000000 * ${UHD_VERSION_MAJOR} + 10000 * ${UHD_VERSION_API} + 100 * ${UHD_VERSION_ABI} + ${UHD_VERSION_PATCH}") ENDIF(UHD_VERSION_DEVEL) ## RFNoC diff --git a/host/cmake/Modules/UHDPackage.cmake b/host/cmake/Modules/UHDPackage.cmake index 3b8b69ab5..138abacb9 100644 --- a/host/cmake/Modules/UHDPackage.cmake +++ b/host/cmake/Modules/UHDPackage.cmake @@ -55,13 +55,12 @@ ENDIF() ######################################################################## # Setup package file name ######################################################################## - IF(DEBIAN AND LIBUHD_PKG) - SET(CPACK_PACKAGE_FILE_NAME "libuhd${UHD_VERSION_MAJOR}_${TRIMMED_UHD_VERSION}_${CMAKE_SYSTEM_PROCESSOR}" CACHE INTERNAL "") + SET(CPACK_PACKAGE_FILE_NAME "libuhd${UHD_VERSION_MAJOR}_${UHD_VERSION}_${CMAKE_SYSTEM_PROCESSOR}" CACHE INTERNAL "") ELSEIF(DEBIAN AND LIBUHDDEV_PKG) - SET(CPACK_PACKAGE_FILE_NAME "libuhd-dev_${TRIMMED_UHD_VERSION}_${CMAKE_SYSTEM_PROCESSOR}" CACHE INTERNAL "") + SET(CPACK_PACKAGE_FILE_NAME "libuhd-dev_${UHD_VERSION}_${CMAKE_SYSTEM_PROCESSOR}" CACHE INTERNAL "") ELSEIF(DEBIAN AND UHDHOST_PKG) - SET(CPACK_PACKAGE_FILE_NAME "uhd-host_${TRIMMED_UHD_VERSION}_${CMAKE_SYSTEM_PROCESSOR}" CACHE INTERNAL "") + SET(CPACK_PACKAGE_FILE_NAME "uhd-host_${UHD_VERSION}_${CMAKE_SYSTEM_PROCESSOR}" CACHE INTERNAL "") ELSE() IF(DEBIAN OR REDHAT) FIND_PROGRAM(LSB_RELEASE_EXECUTABLE lsb_release) @@ -124,7 +123,7 @@ SET(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/LICENSE) # Setup CPack Source ######################################################################## -SET(CPACK_SOURCE_PACKAGE_FILE_NAME "uhd-${TRIMMED_UHD_VERSION}" CACHE INTERNAL "") +SET(CPACK_SOURCE_PACKAGE_FILE_NAME "uhd-${UHD_VERSION}" CACHE INTERNAL "") SET(CPACK_SOURCE_IGNORE_FILES "\\\\.git*;\\\\.swp$") ######################################################################## diff --git a/host/cmake/Modules/UHDVersion.cmake b/host/cmake/Modules/UHDVersion.cmake index 79ba2d017..0e8064ff3 100644 --- a/host/cmake/Modules/UHDVersion.cmake +++ b/host/cmake/Modules/UHDVersion.cmake @@ -27,11 +27,11 @@ FIND_PACKAGE(Git QUIET) # - Increment patch for bugfixes and docs # - set UHD_VERSION_DEVEL to true for master and development branches ######################################################################## -SET(UHD_VERSION_MAJOR 003) -SET(UHD_VERSION_API 010) -SET(UHD_VERSION_ABI 001) -SET(UHD_VERSION_PATCH 001) -SET(UHD_VERSION_DEVEL FALSE) +SET(UHD_VERSION_MAJOR 3) +SET(UHD_VERSION_API 11) +SET(UHD_VERSION_ABI 0) +SET(UHD_VERSION_PATCH git) +SET(UHD_VERSION_DEVEL TRUE) ######################################################################## # If we're on a development branch, we skip the patch version @@ -74,28 +74,6 @@ IF(GIT_FOUND) ENDIF(GIT_FOUND) ######################################################################## -# Set up trimmed version numbers for DLL resource files and packages -######################################################################## -FUNCTION(DEPAD_NUM input_num output_num) - EXECUTE_PROCESS( - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND ${PYTHON_EXECUTABLE} -c "print(\"${input_num}\".lstrip(\"0\") or 0)" - OUTPUT_VARIABLE depadded_num OUTPUT_STRIP_TRAILING_WHITESPACE - ) - SET(${output_num} ${depadded_num} PARENT_SCOPE) -ENDFUNCTION(DEPAD_NUM) - -DEPAD_NUM(${UHD_VERSION_MAJOR} TRIMMED_VERSION_MAJOR) -DEPAD_NUM(${UHD_VERSION_API} TRIMMED_VERSION_API) -DEPAD_NUM(${UHD_VERSION_ABI} TRIMMED_VERSION_ABI) -IF(UHD_VERSION_DEVEL) - SET(TRIMMED_VERSION_PATCH ${UHD_VERSION_PATCH}) -ELSE(UHD_VERSION_DEVEL) - DEPAD_NUM(${UHD_VERSION_PATCH} TRIMMED_VERSION_PATCH) -ENDIF(UHD_VERSION_DEVEL) -SET(TRIMMED_UHD_VERSION "${TRIMMED_VERSION_MAJOR}.${TRIMMED_VERSION_API}.${TRIMMED_VERSION_ABI}.${TRIMMED_VERSION_PATCH}") - -######################################################################## # Version information discovery through git log ######################################################################## diff --git a/host/include/config.h.in b/host/include/config.h.in index 8e72a1f00..0e8982488 100644 --- a/host/include/config.h.in +++ b/host/include/config.h.in @@ -18,10 +18,10 @@ #cmakedefine HAVE_LOG2 /* Version macros */ -#cmakedefine UHD_VERSION_MAJOR ${TRIMMED_VERSION_MAJOR} -#cmakedefine UHD_VERSION_API ${TRIMMED_VERSION_API} -#cmakedefine UHD_VERSION_ABI ${TRIMMED_VERSION_ABI} -#cmakedefine UHD_VERSION_PATCH ${TRIMMED_VERSION_PATCH} +#cmakedefine UHD_VERSION_MAJOR ${UHD_VERSION_MAJOR} +#cmakedefine UHD_VERSION_API ${UHD_VERSION_API} +#cmakedefine UHD_VERSION_ABI ${UHD_VERSION_ABI} +#cmakedefine UHD_VERSION_PATCH ${UHD_VERSION_PATCH} #cmakedefine ENABLE_USB #ifndef UHD_VERSION #cmakedefine UHD_VERSION @UHD_VERSION_ADDED@ diff --git a/host/include/uhd/CMakeLists.txt b/host/include/uhd/CMakeLists.txt index e31ff80a0..42c89a9f3 100644 --- a/host/include/uhd/CMakeLists.txt +++ b/host/include/uhd/CMakeLists.txt @@ -18,6 +18,7 @@ ADD_SUBDIRECTORY(rfnoc) ADD_SUBDIRECTORY(transport) ADD_SUBDIRECTORY(types) +ADD_SUBDIRECTORY(cal) ADD_SUBDIRECTORY(usrp) ADD_SUBDIRECTORY(usrp_clock) ADD_SUBDIRECTORY(utils) diff --git a/host/include/uhd/cal/CMakeLists.txt b/host/include/uhd/cal/CMakeLists.txt new file mode 100644 index 000000000..14107ee53 --- /dev/null +++ b/host/include/uhd/cal/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2016 Ettus Research +# +# 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/>. +# + +UHD_INSTALL(FILES + container.hpp + power_container.hpp + DESTINATION ${INCLUDE_DIR}/uhd/cal + COMPONENT headers +) diff --git a/host/include/uhd/cal/container.hpp b/host/include/uhd/cal/container.hpp new file mode 100644 index 000000000..e4f418311 --- /dev/null +++ b/host/include/uhd/cal/container.hpp @@ -0,0 +1,104 @@ +// +// Copyright 2016 Ettus Research +// +// 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/>. +// + +#ifndef INCLUDED_UHD_CAL_CONTAINER_HPP +#define INCLUDED_UHD_CAL_CONTAINER_HPP + +#include <uhd/config.hpp> +#include <boost/serialization/serialization.hpp> +#include <boost/serialization/vector.hpp> +#include <boost/serialization/string.hpp> +#include <boost/serialization/map.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/shared_ptr.hpp> + +namespace uhd { +namespace cal { + +class base_container { +public: + typedef std::map<std::string, std::string> metadata_t; + typedef boost::shared_ptr<base_container> sptr; +}; + +/*! + * An interface for creating and managing a generic calibration + * data container. + * + * These containers are used to represent N dimensional data structures + * in order to accommodate a mapping from multi-variable input to a scalar + * value (e.g. gain, frequency, temperature -> power level [dBm]). + * + * The container only supports inputs of the same type to be mapped. + * + */ +template<typename in_type, typename out_type> +class UHD_API cal_container : public base_container { +public: + typedef std::map<in_type, out_type> container_t; + + /*! + * Get the mapping from an input to an output + * from the calibration container. + * + * \param args input values + * \returns the output of the mapping (a scalar value) + * \throws uhd::assertion_error if the dimensions of the input args + * are incorrect for this container + */ + virtual out_type get(const in_type &args) = 0; + + /*! + * Add a data point to the container. + * This function records a mapping R^n -> R between an input vector + * and output scalar. + * + * \param output the output of the data point mapping + * \param args input values + */ + virtual void add(const out_type output, const in_type &args) = 0; + + /*! + * Associate some metadata with the container. + * + * \param data a map of metadata (string -> string). + */ + virtual void add_metadata(const metadata_t &data) = 0; + + /*! + * Retrieve metadata from the container. + * + * \returns map of metadata. + */ + virtual const metadata_t &get_metadata() = 0; + +public: + typedef boost::archive::text_iarchive iarchive_type; + typedef boost::archive::text_oarchive oarchive_type; + +protected: + friend class boost::serialization::access; + + virtual void serialize(iarchive_type & ar, const unsigned int) = 0; + virtual void serialize(oarchive_type & ar, const unsigned int) = 0; +}; + +} // namespace cal +} // namespace uhd + +#endif /* INCLUDED_UHD_CAL_CONTAINER_HPP */ diff --git a/host/include/uhd/cal/power_container.hpp b/host/include/uhd/cal/power_container.hpp new file mode 100644 index 000000000..37f7bd8df --- /dev/null +++ b/host/include/uhd/cal/power_container.hpp @@ -0,0 +1,79 @@ +// +// Copyright 2016 Ettus Research +// +// 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/>. +// + +#ifndef INCLUDED_UHD_CAL_POWER_CONTAINER_HPP +#define INCLUDED_UHD_CAL_POWER_CONTAINER_HPP + +#include <uhd/config.hpp> +#include <uhd/cal/container.hpp> +#include <boost/shared_ptr.hpp> + +namespace uhd { +namespace cal { + +class UHD_API power_container : public cal_container<std::vector<double>, double> { +public: + typedef boost::shared_ptr<power_container> sptr; + + /*! + * Create a container for data related to power calibration. + * + * \returns shared pointer to the container + */ + static sptr make(); + + /*! + * Get the mapping from an input to an output + * from the calibration container. + * + * \param args input values + * \returns the output of the mapping (a scalar value) + * \throws uhd::assertion_error if the number of input values are incorrect + * for the container type + */ + virtual double get(const std::vector<double> &args) = 0; + + /*! + * Add a data point to the container. + * This function records a mapping R^n -> R between an input vector + * and output scalar. For example, a mapping might be + * (power level, frequency, temperature) -> gain. + * + * \param output the output of the data point mapping + * \param args input values + */ + virtual void add(const double output, const std::vector<double> &args) = 0; + + /*! + * Associate some metadata with the container. + * + * \param data a map of metadata (string -> string). + */ + virtual void add_metadata(const metadata_t &data) = 0; + + /*! + * Retrieve metadata from the container. + * + * \returns map of metadata. + */ + virtual const metadata_t &get_metadata() = 0; +}; + +} // namespace cal +} // namespace uhd + +#endif /* INCLUDED_UHD_CAL_POWER_CONTAINER_HPP */ diff --git a/host/include/uhd/rfnoc/block_ctrl_base.hpp b/host/include/uhd/rfnoc/block_ctrl_base.hpp index f770cf129..778ded043 100644 --- a/host/include/uhd/rfnoc/block_ctrl_base.hpp +++ b/host/include/uhd/rfnoc/block_ctrl_base.hpp @@ -46,7 +46,6 @@ struct make_args_t { make_args_t(const std::string &key="") : device_index(0), - is_big_endian(true), block_name(""), block_key(key) {} @@ -61,7 +60,6 @@ struct make_args_t // property tree is /mboards/0, pass a subtree starting at /mboards/0 // to the constructor. uhd::property_tree::sptr tree; - bool is_big_endian; //! The name of the block as it will be addressed std::string block_name; //! The key of the block, i.e. how it was registered @@ -391,9 +389,6 @@ protected: //! Root node of this block's properties uhd::fs_path _root_path; - //! Endianness of underlying transport (for data transport) - bool _transport_is_big_endian; - //! Block definition (stores info about the block such as ports) blockdef::sptr _block_def; diff --git a/host/include/uhd/types/sid.hpp b/host/include/uhd/types/sid.hpp index 5ea8e8f41..f1471549e 100644 --- a/host/include/uhd/types/sid.hpp +++ b/host/include/uhd/types/sid.hpp @@ -1,5 +1,5 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014-2016 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 @@ -19,9 +19,8 @@ #define INCLUDED_UHD_TYPES_SID_HPP #include <uhd/config.hpp> -#include <stdint.h> -#include <boost/shared_ptr.hpp> #include <iostream> +#include <stdint.h> namespace uhd { /*! @@ -99,47 +98,47 @@ namespace uhd { // Getters // //! Alias for get_sid() - UHD_INLINE uint32_t get() const { return get_sid(); }; + inline uint32_t get() const { return get_sid(); }; //! Returns a 32-Bit representation of the SID if set, or zero otherwise. - UHD_INLINE uint32_t get_sid() const { return _set ? _sid : 0; }; + inline uint32_t get_sid() const { return _set ? _sid : 0; }; //! Return the 16-bit source address of this SID - UHD_INLINE uint32_t get_src() const { + inline uint32_t get_src() const { return (_sid >> 16) & 0xFFFF; } //! Return the 16-bit destination address of this SID - UHD_INLINE uint32_t get_dst() const { + inline uint32_t get_dst() const { return _sid & 0xFFFF; } //! Return 8-bit address of the source - UHD_INLINE uint32_t get_src_addr() const { + inline uint32_t get_src_addr() const { return (get_src() >> 8) & 0xFF; } //! Return endpoint of the source - UHD_INLINE uint32_t get_src_endpoint() const { + inline uint32_t get_src_endpoint() const { return get_src() & 0xFF; } //! Return crossbar port of the source - UHD_INLINE uint32_t get_src_xbarport() const { + inline uint32_t get_src_xbarport() const { return (get_src_endpoint() >> 4) & 0xF; } //! Return block port of the source - UHD_INLINE uint32_t get_src_blockport() const { + inline uint32_t get_src_blockport() const { return (get_src_endpoint()) & 0xF; } //! Return 8-bit address of the destination - UHD_INLINE uint32_t get_dst_addr() const { + inline uint32_t get_dst_addr() const { return (get_dst() >> 8) & 0xFF; } //! Return endpoint of the destination - UHD_INLINE uint32_t get_dst_endpoint() const { + inline uint32_t get_dst_endpoint() const { return get_dst() & 0xFF; } //! Return crossbar port of the source - UHD_INLINE uint32_t get_dst_xbarport() const { + inline uint32_t get_dst_xbarport() const { return (get_dst_endpoint() >> 4) & 0xF; } //! Return block port of the source - UHD_INLINE uint32_t get_dst_blockport() const { + inline uint32_t get_dst_blockport() const { return (get_dst_endpoint()) & 0xF; } @@ -168,14 +167,14 @@ namespace uhd { // Manipulators //! Swaps dst and src address and returns the new SID. - sid_t reversed(); + sid_t reversed() const; - //! Swaps dst and src in-place. + //! Swaps dst and src in-place. This modifies the current SID. void reverse(); // Overloaded operators - sid_t operator = (uint32_t new_sid) { + sid_t operator = (const uint32_t new_sid) { set_sid(new_sid); return *this; } @@ -185,6 +184,11 @@ namespace uhd { return *this; } + sid_t operator = (const sid_t &sid) { + set_sid(sid.get_sid()); + return *this; + } + sid_t operator = (const std::string &sid_str) { set_from_str(sid_str); return *this; @@ -222,7 +226,7 @@ namespace uhd { }; //! Stream output operator. Honors std::ios::hex. - UHD_INLINE std::ostream& operator<< (std::ostream& out, const sid_t &sid) { + inline std::ostream& operator<< (std::ostream& out, const sid_t &sid) { std::ios_base::fmtflags ff = out.flags(); if (ff & std::ios::hex) { out << sid.to_pp_string_hex(); diff --git a/host/include/uhd/utils/atomic.hpp b/host/include/uhd/utils/atomic.hpp index ec4d3e0a2..f37fb4395 100644 --- a/host/include/uhd/utils/atomic.hpp +++ b/host/include/uhd/utils/atomic.hpp @@ -68,76 +68,6 @@ namespace uhd{ }; /*! - * A reusable barrier to sync multiple threads. - * All threads spin on wait() until count is reset. - */ - class UHD_API reusable_barrier{ - public: - - reusable_barrier():_size (0) {} - - reusable_barrier(const size_t size):_size(size) {} - - //! Resize the barrier for N threads - void resize(const size_t size){ - _size = size; - } - - /*! - * Force the barrier wait to throw a boost::thread_interrupted - * The threads were not getting the interruption_point on windows. - */ - void interrupt(void) - { - _done.inc(); - } - - //! Wait on the barrier condition - UHD_INLINE void wait(void) - { - if (_size == 1) return; - - //entry barrier with condition variable - _entry_counter.inc(); - _entry_counter.cas(0, _size); - boost::mutex::scoped_lock lock(_mutex); - while (_entry_counter.read() != 0) - { - this->check_interrupt(); - _cond.timed_wait(lock, boost::posix_time::milliseconds(1)); - } - lock.unlock(); //unlock before notify - _cond.notify_one(); - - //exit barrier to ensure known condition of entry count - _exit_counter.inc(); - _exit_counter.cas(0, _size); - while (_exit_counter.read() != 0) this->check_interrupt(); - } - - //! Wait on the barrier condition - UHD_INLINE void wait_others(void) - { - while (_entry_counter.read() != (_size-1)) this->check_interrupt(); - } - - private: - size_t _size; - atomic_uint32_t _entry_counter; - atomic_uint32_t _exit_counter; - atomic_uint32_t _done; - boost::mutex _mutex; - boost::condition_variable _cond; - - UHD_INLINE void check_interrupt(void) - { - if (_done.read() != 0) throw boost::thread_interrupted(); - boost::this_thread::interruption_point(); - boost::this_thread::yield(); - } - }; - - /*! * Spin-wait on a condition with a timeout. * \param cond an atomic variable to compare * \param value compare to atomic for true/false diff --git a/host/include/uhd/utils/msg.hpp b/host/include/uhd/utils/msg.hpp index 2cc5893e7..48ffb28a1 100644 --- a/host/include/uhd/utils/msg.hpp +++ b/host/include/uhd/utils/msg.hpp @@ -57,6 +57,13 @@ namespace uhd{ namespace msg{ typedef void (*handler_t)(type_t, const std::string &); /*! + * Default message handler for printing uhd system messages + * \param type message type, such as status, warning, or error + * \param msg contents of the system message as a string + */ + void default_msg_handler(type_t type, const std::string &msg); + + /*! * Register the handler for uhd system messages. * Only one handler can be registered at once. * This replaces the default std::cout/cerr handler. @@ -64,6 +71,12 @@ namespace uhd{ namespace msg{ */ UHD_API void register_handler(const handler_t &handler); + /*! + * Returns the current message handler for uhd system messages + * \returns the current message handler + */ + UHD_API const handler_t& get_handler(); + //! Internal message object (called by UHD_MSG macro) class UHD_API _msg{ public: diff --git a/host/include/uhd/version.hpp.in b/host/include/uhd/version.hpp.in index 8cfc7b8c6..c16739a78 100644 --- a/host/include/uhd/version.hpp.in +++ b/host/include/uhd/version.hpp.in @@ -24,7 +24,7 @@ * The format is oldest API compatible release - ABI compat number. * The compatibility number allows pre-release ABI to be versioned. */ -#define UHD_VERSION_ABI_STRING "@TRIMMED_VERSION_MAJOR@.@TRIMMED_VERSION_API@.@TRIMMED_VERSION_ABI@" +#define UHD_VERSION_ABI_STRING "@UHD_VERSION_MAJOR@.@UHD_VERSION_API@.@UHD_VERSION_ABI@" /*! * A macro to check UHD version at compile-time. diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index 0cd89953c..fce1021c1 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -91,6 +91,7 @@ INCLUDE_SUBDIRECTORY(ic_reg_maps) INCLUDE_SUBDIRECTORY(types) INCLUDE_SUBDIRECTORY(convert) INCLUDE_SUBDIRECTORY(rfnoc) +INCLUDE_SUBDIRECTORY(cal) INCLUDE_SUBDIRECTORY(usrp) INCLUDE_SUBDIRECTORY(usrp_clock) INCLUDE_SUBDIRECTORY(utils) @@ -140,10 +141,10 @@ ENDIF(ENABLE_C_API) # Add DLL resource file to Windows build ######################################################################## IF(MSVC) - MATH(EXPR TRIMMED_VERSION_MAJOR_API "${TRIMMED_VERSION_MAJOR} * 1000 + ${TRIMMED_VERSION_API}") - SET(RC_TRIMMED_VERSION_PATCH ${TRIMMED_VERSION_PATCH}) + MATH(EXPR RC_VERSION_MAJOR_API "${UHD_VERSION_MAJOR} * 1000 + ${UHD_VERSION_API}") + SET(RC_VERSION_PATCH ${UHD_VERSION_PATCH}) IF(UHD_VERSION_DEVEL) - SET(RC_TRIMMED_VERSION_PATCH "999") + SET(RC_VERSION_PATCH "999") ENDIF(UHD_VERSION_DEVEL) # Allow a custom .rc template file to be used diff --git a/host/lib/cal/CMakeLists.txt b/host/lib/cal/CMakeLists.txt new file mode 100644 index 000000000..5c616a9da --- /dev/null +++ b/host/lib/cal/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# Copyright 2016 Ettus Research +# +# 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/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## +INCLUDE(CheckIncludeFileCXX) +MESSAGE(STATUS "") + +######################################################################## +# Convert types generation +######################################################################## +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + +LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/power_container_impl.cpp +) diff --git a/host/lib/cal/interpolation.hpp b/host/lib/cal/interpolation.hpp new file mode 100644 index 000000000..34f084fbd --- /dev/null +++ b/host/lib/cal/interpolation.hpp @@ -0,0 +1,71 @@ +// +// Copyright 2016 Ettus Research +// +// 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/>. +// + +#ifndef INCLUDED_UHD_INTERPOLATION_HPP +#define INCLUDED_UHD_INTERPOLATION_HPP + +#include <uhd/exception.hpp> +#include <boost/format.hpp> +#include <map> +#include <cmath> + +namespace uhd { +namespace cal { + +template<typename in_type, typename out_type> +struct interp +{ +public: + typedef std::vector<in_type> args_t; + typedef std::map<args_t, out_type> container_t; + + /*! + * Nearest neighbor interpolation given a mapping: R^n -> R + * + * 1) search for the nearest point in R^n + * 2) find the nearest output scalars in R + * + * \param data input data container + * \param args input data point + * \returns interpolated output value + */ + const out_type nn_interp(container_t &data, const args_t &args); + + /*! + * Bilinear interpolation given a mapping: R^2 -> R + * + * 1) search for 4 surrounding points in R^2 + * 2) find the output scalars in R + * 3) solve the system of equations given our input mappings + * + * \param data input data container + * \param args input data point + * \returns interpolated output value + */ + const out_type bl_interp(container_t &data, const args_t &args); + +private: + /*! + * Calculate the distance between two points + */ + static in_type calc_dist(const args_t &a, const args_t &b); +}; + +} // namespace cal +} // namespace uhd + +#endif /* INCLUDED_UHD_INTERPOLATION_HPP */ diff --git a/host/lib/cal/interpolation.ipp b/host/lib/cal/interpolation.ipp new file mode 100644 index 000000000..f58b70b68 --- /dev/null +++ b/host/lib/cal/interpolation.ipp @@ -0,0 +1,199 @@ +// +// Copyright 2016 Ettus Research +// +// 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/>. +// + +#ifndef INCLUDED_UHD_INTERPOLATION_IPP +#define INCLUDED_UHD_INTERPOLATION_IPP + +#include "interpolation.hpp" +#include <uhd/utils/msg.hpp> +#include <boost/numeric/ublas/io.hpp> +#include <boost/numeric/ublas/matrix.hpp> +#include <boost/numeric/ublas/lu.hpp> + +using namespace boost::numeric; + +namespace uhd { +namespace cal { + +#define CAL_INTERP_METHOD(return_type, method, args, ...) \ + template<typename in_type, typename out_type> \ + return_type interp<in_type, out_type>::\ + method(args, __VA_ARGS__) + +#define ARGS_T typename interp<in_type, out_type>::args_t +#define CONTAINER_T typename interp<in_type, out_type>::container_t + +CAL_INTERP_METHOD(in_type, calc_dist, const ARGS_T &a, const ARGS_T &b) +{ + in_type dist = 0; + for (size_t i = 0; i < std::min(a.size(), b.size()); i++) + { + dist += std::abs(a[i] - b[i]); + } + return dist; +} + +CAL_INTERP_METHOD(const out_type, nn_interp, CONTAINER_T &data, const ARGS_T &args) +{ + // Check the cache for the output + if (data.find(args) != data.end()) { + return data[args]; + } + + out_type output = 0; + in_type min_dist = 0; + typename container_t::const_iterator citer; + for (citer = data.begin(); citer != data.end(); citer++) + { + in_type dist = calc_dist(citer->first, args); + if (citer == data.begin() || dist < min_dist) { + min_dist = dist; + output = data[citer->first]; + } + } + + return output; +} + +CAL_INTERP_METHOD(const out_type, bl_interp, CONTAINER_T &data, const ARGS_T &args) +{ + if (args.size() != 2) { + throw uhd::assertion_error(str(boost::format( + "Bilinear interpolation expects 2D values. Received %d.") + % args.size() + )); + } + + if (data.size() < 4) { + throw uhd::assertion_error(str(boost::format( + "Bilinear interpolation requires at least 4 input points. Found %d.") + % data.size() + )); + } + + // Locate the nearest 4 points + typedef std::pair<interp<in_type, out_type>::args_t, out_type> cal_pair_t; + typename std::vector<cal_pair_t> nearest; + + // Initialize the resulting pair to something + cal_pair_t pair = *data.begin(); + + for (size_t i = 0; i < 4; i++) { + bool init = true; + in_type min_dist = 0; + typename container_t::const_iterator citer; + for (citer = data.begin(); citer != data.end(); citer++) + { + cal_pair_t temp = *citer; + if (std::find(nearest.begin(), nearest.end(), temp) == nearest.end()) + { + in_type dist = calc_dist(citer->first, args); + if (dist < min_dist || init) + { + min_dist = dist; + pair = temp; + init = false; + } + } + } + // Push back the nearest pair + nearest.push_back(pair); + } + + // + // Since these points are not grid aligned, + // we perform irregular bilinear interpolation. + // This math involves finding our interpolation + // function using lagrange multipliers: + // + // f(x, y) = ax^2 + bxy + cy^2 + dx + ey + f + // + // The solution is to solve the following system: + // + // A x b + // | E X' | | s | - | 0 | + // | X 0 | | l | - | z | + // + // where s is a vector of the above coefficients. + // + typename ublas::matrix<in_type> A(10, 10, 0.0); + + // E + A(0, 0) = 1.0; A(1, 1) = 1.0; A(2, 2) = 1.0; + + in_type x1, x2, x3, x4; + in_type y1, y2, y3, y4; + + x1 = nearest[0].first[0]; y1 = nearest[0].first[1]; + x2 = nearest[1].first[0]; y2 = nearest[1].first[1]; + x3 = nearest[2].first[0]; y3 = nearest[2].first[1]; + x4 = nearest[3].first[0]; y4 = nearest[3].first[1]; + + // X + A(0, 6) = x1*x1; A(1, 6) = x1*y1; A(2, 6) = y1*y1; A(3, 6) = x1; A(4, 6) = y1; A(5, 6) = 1.0; + A(0, 7) = x2*x2; A(1, 7) = x2*y2; A(2, 7) = y2*y2; A(3, 7) = x2; A(4, 7) = y2; A(5, 7) = 1.0; + A(0, 8) = x3*x3; A(1, 8) = x3*y3; A(2, 8) = y3*y3; A(3, 8) = x3; A(4, 8) = y3; A(5, 8) = 1.0; + A(0, 9) = x4*x4; A(1, 9) = x4*y4; A(2, 9) = y4*y4; A(3, 9) = x4; A(4, 9) = y4; A(5, 9) = 1.0; + + // X' + A(6, 0) = x1*x1; A(6, 1) = x1*y1; A(6, 2) = y1*y1; A(6, 3) = x1; A(6, 4) = y1; A(6, 5) = 1.0; + A(7, 0) = x2*x2; A(7, 1) = x2*y2; A(7, 2) = y2*y2; A(7, 3) = x2; A(7, 4) = y2; A(7, 5) = 1.0; + A(8, 0) = x3*x3; A(8, 1) = x3*y3; A(8, 2) = y3*y3; A(8, 3) = x3; A(8, 4) = y3; A(8, 5) = 1.0; + A(9, 0) = x4*x4; A(9, 1) = x4*y4; A(9, 2) = y4*y4; A(9, 3) = x4; A(9, 4) = y4; A(9, 5) = 1.0; + + // z + typename ublas::vector<in_type> b(10, 0.0); + b(6) = nearest[0].second; + b(7) = nearest[1].second; + b(8) = nearest[2].second; + b(9) = nearest[3].second; + + typename ublas::matrix<in_type> A_t = A; + typename ublas::vector<in_type> s = b; + typename ublas::permutation_matrix<in_type> P(A_t.size1()); + + // Use LUP factorization to solve for the coefficients + // We're solving the problem in the form of Ax = b + bool is_singular = ublas::lu_factorize(A_t, P); + + out_type output = 0; + + // Fall back to 1D interpolation if the matrix is singular + if (is_singular) { + // Warn the user that the A matrix is singular + UHD_MSG(warning) << "Bilinear interpolation: singular matrix detected." << std::endl + << "Performing 1D linear interpolation against the nearest measurements." << std::endl + << "Provide calibration data with more measurements" << std::endl; + + output = (b[7] - b[6]) / 2.0; + output += b[6]; + return output; + } + ublas::lu_substitute(A_t, P, s); + + in_type x = args[0]; + in_type y = args[1]; + + // Utilize the solution to calculate the interpolation function + output = s[0]*x*x + s[1]*x*y + s[2]*y*y + s[3]*x + s[4]*y + s[5]; + return output; +} + +} // namespace cal +} // namespace uhd + +#endif /* INCLUDED_UHD_INTERPOLATION_IPP */ diff --git a/host/lib/cal/power_container_impl.cpp b/host/lib/cal/power_container_impl.cpp new file mode 100644 index 000000000..5d07c3b7e --- /dev/null +++ b/host/lib/cal/power_container_impl.cpp @@ -0,0 +1,80 @@ +// +// Copyright 2016 Ettus Research +// +// 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 "power_container_impl.hpp" +#include <uhd/exception.hpp> +#include <boost/format.hpp> + +using namespace uhd; +using namespace uhd::cal; + +power_container::sptr power_container::make() +{ + return power_container::sptr(new power_container_impl()); +} + +power_container_impl::power_container_impl() : + _nargs(0), _mode(interp_mode_t::NEAREST) +{ + /* NOP */ +} + +double power_container_impl::get(const std::vector<double> &args) +{ + this->verify_nargs(args); + switch (_mode) + { + case interp_mode_t::BILINEAR : + return _interpolator.bl_interp(_data, args); + case interp_mode_t::NEAREST : + return _interpolator.nn_interp(_data, args); + default: + return _interpolator.nn_interp(_data, args); + } +} + +void power_container_impl::add(const double output, const std::vector<double> &args) +{ + if (_nargs == 0) + { + _nargs = args.size(); + _mode = _nargs == 2 ? interp_mode_t::BILINEAR : interp_mode_t::NEAREST; + } + this->verify_nargs(args); + _data[args] = output; +} + +void power_container_impl::add_metadata(const power_container::metadata_t &data) +{ + _metadata = data; +} + +const power_container_impl::metadata_t &power_container_impl::get_metadata() +{ + return _metadata; +} + +void power_container_impl::verify_nargs(const std::vector<double> &args) +{ + // Check that the number of arguments expected are correct + if (args.size() != _nargs) { + throw uhd::assertion_error(str(boost::format( + "power_container_impl: Expected %d number of arguments/values instead of %d") + % _nargs % args.size() + )); + } +} diff --git a/host/lib/cal/power_container_impl.hpp b/host/lib/cal/power_container_impl.hpp new file mode 100644 index 000000000..4c2bcab79 --- /dev/null +++ b/host/lib/cal/power_container_impl.hpp @@ -0,0 +1,77 @@ +// +// Copyright 2016 Ettus Research +// +// 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/>. +// + +#ifndef INCLUDED_UHD_CAL_POWER_CONTAINER_IMPL_HPP +#define INCLUDED_UHD_CAL_POWER_CONTAINER_IMPL_HPP + +#include "interpolation.ipp" +#include <uhd/cal/power_container.hpp> + +namespace uhd { +namespace cal { + +enum interp_mode_t +{ + BILINEAR, //! linear interpolation + NEAREST //! nearest neighbor interpolation +}; + +class power_container_impl : public power_container { +public: + typedef std::map<std::vector<double>, double> container_t; + + power_container_impl(); + + double get(const std::vector<double> &args); + const metadata_t &get_metadata(); + + void add(const double output, const std::vector<double> &args); + void add_metadata(const metadata_t &data); + +private: + // Container data to be serialized + size_t _nargs; + metadata_t _metadata; + interp_mode_t _mode; + container_t _data; + + interp<double, double> _interpolator; + + void verify_nargs(const std::vector<double> &args); + +protected: + friend class boost::serialization::access; + + void serialize(iarchive_type & ar, const unsigned int) { + ar & _nargs; + ar & _metadata; + ar & _mode; + ar & _data; + } + + void serialize(oarchive_type & ar, const unsigned int) { + ar & _nargs; + ar & _metadata; + ar & _mode; + ar & _data; + } +}; + +} // namespace cal +} // namespace uhd + +#endif /* INCLUDED_UHD_CAL_POWER_CONTAINER_IMPL_HPP */ diff --git a/host/lib/rfnoc/block_ctrl_base.cpp b/host/lib/rfnoc/block_ctrl_base.cpp index 24dad6b47..4ee577891 100644 --- a/host/lib/rfnoc/block_ctrl_base.cpp +++ b/host/lib/rfnoc/block_ctrl_base.cpp @@ -48,7 +48,6 @@ inline uint32_t _sr_to_addr64(uint32_t reg) { return reg * 8; }; // for peek64 block_ctrl_base::block_ctrl_base( const make_args_t &make_args ) : _tree(make_args.tree), - _transport_is_big_endian(make_args.is_big_endian), _ctrl_ifaces(make_args.ctrl_ifaces), _base_address(make_args.base_address & 0xFFF0) { diff --git a/host/lib/rfnoc/ctrl_iface.cpp b/host/lib/rfnoc/ctrl_iface.cpp index b2ac1778e..8e4474af7 100644 --- a/host/lib/rfnoc/ctrl_iface.cpp +++ b/host/lib/rfnoc/ctrl_iface.cpp @@ -16,7 +16,6 @@ // #include "ctrl_iface.hpp" -#include "async_packet_handler.hpp" #include <uhd/exception.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/byteswap.hpp> @@ -37,7 +36,6 @@ using namespace uhd::transport; static const double ACK_TIMEOUT = 2.0; //supposed to be worst case practical timeout static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command -static const size_t SR_READBACK = 32; ctrl_iface::~ctrl_iface(void){ /* NOP */ @@ -60,13 +58,12 @@ public: _name(name), _seq_out(0), _timeout(ACK_TIMEOUT), - _resp_queue(128/*max response msgs*/), - _resp_queue_size(_resp_xport ? _resp_xport->get_num_recv_frames() : 3), + _resp_queue_size(_resp_xport->get_num_recv_frames()), _rb_address(uhd::rfnoc::SR_READBACK) { - if (resp_xport) { - while (resp_xport->get_recv_buff(0.0)) {} //flush - } + UHD_ASSERT_THROW(_ctrl_xport); + UHD_ASSERT_THROW(_resp_xport); + while (resp_xport->get_recv_buff(0.0)) {} //flush this->set_time(uhd::time_spec_t(0.0)); this->set_tick_rate(1.0); //something possible but bogus } @@ -76,7 +73,6 @@ public: _timeout = ACK_TIMEOUT; //reset timeout to something small UHD_SAFE_CALL( this->peek32(0);//dummy peek with the purpose of ack'ing all packets - _async_task.reset();//now its ok to release the task ) } @@ -196,50 +192,16 @@ private: uint32_t const *pkt = NULL; managed_recv_buffer::sptr buff; - //get buffer from response endpoint - or die in timeout - if (_resp_xport) - { - buff = _resp_xport->get_recv_buff(_timeout); - try - { - UHD_ASSERT_THROW(bool(buff)); - UHD_ASSERT_THROW(buff->size() > 0); - } - catch(const std::exception &ex) - { - throw uhd::io_error(str(boost::format("Block ctrl (%s) no response packet - %s") % _name % ex.what())); - } - pkt = buff->cast<const uint32_t *>(); - packet_info.num_packet_words32 = buff->size()/sizeof(uint32_t); + buff = _resp_xport->get_recv_buff(_timeout); + try { + UHD_ASSERT_THROW(bool(buff)); + UHD_ASSERT_THROW(buff->size() > 0); } - - //get buffer from response endpoint - or die in timeout - else - { - /* - * Couldn't get message with haste. - * Now check both possible queues for messages. - * Messages should come in on _resp_queue, - * but could end up in dump_queue. - * If we don't get a message --> Die in timeout. - */ - double accum_timeout = 0.0; - const double short_timeout = 0.005; // == 5ms - while(not ((_resp_queue.pop_with_haste(resp_buff)) - || (check_dump_queue(resp_buff)) - || (_resp_queue.pop_with_timed_wait(resp_buff, short_timeout)) - )){ - /* - * If a message couldn't be received within a given timeout - * --> throw AssertionError! - */ - accum_timeout += short_timeout; - UHD_ASSERT_THROW(accum_timeout < _timeout); - } - - pkt = resp_buff.data; - packet_info.num_packet_words32 = sizeof(resp_buff)/sizeof(uint32_t); + catch(const std::exception &ex) { + throw uhd::io_error(str(boost::format("Block ctrl (%s) no response packet - %s") % _name % ex.what())); } + pkt = buff->cast<const uint32_t *>(); + packet_info.num_packet_words32 = buff->size()/sizeof(uint32_t); //parse the buffer try @@ -306,48 +268,11 @@ private: return 0; } - /* - * If ctrl_core waits for a message that didn't arrive it can search for it in the dump queue. - * This actually happens during shutdown. - * handle_async_task can't access queue anymore thus it returns the corresponding message. - * msg_task class implements a dump_queue to store such messages. - * With check_dump_queue we can check if a message we are waiting for got stranded there. - * If a message got stuck we get it here and push it onto our own message_queue. - */ - bool check_dump_queue(resp_buff_type& b) { - const size_t min_buff_size = 8; // Same value as in b200_io_impl->handle_async_task - uint32_t recv_sid = (((_sid)<<16)|((_sid)>>16)); - uhd::msg_task::msg_payload_t msg; - do{ - msg = _async_task->get_msg_from_dump_queue(recv_sid); - } - while(msg.size() < min_buff_size && msg.size() != 0); - - if(msg.size() >= min_buff_size) { - memcpy(b.data, &msg.front(), std::min(msg.size(), sizeof(b.data))); - return true; - } - return false; - } - - void push_response(const uint32_t *buff) - { - resp_buff_type resp_buff; - std::memcpy(resp_buff.data, buff, sizeof(resp_buff)); - _resp_queue.push_with_haste(resp_buff); - } - - void hold_task(uhd::msg_task::sptr task) - { - _async_task = task; - } - const vrt::if_packet_info_t::link_type_t _link_type; const vrt::if_packet_info_t::packet_type_t _packet_type; const bool _bige; const uhd::transport::zero_copy_if::sptr _ctrl_xport; const uhd::transport::zero_copy_if::sptr _resp_xport; - uhd::msg_task::sptr _async_task; const uint32_t _sid; const std::string _name; boost::mutex _mutex; @@ -357,7 +282,6 @@ private: double _tick_rate; double _timeout; std::queue<size_t> _outstanding_seqs; - bounded_buffer<resp_buff_type> _resp_queue; const size_t _resp_queue_size; const size_t _rb_address; diff --git a/host/lib/rfnoc/ctrl_iface.hpp b/host/lib/rfnoc/ctrl_iface.hpp index 3690874f9..5e88c3dd6 100644 --- a/host/lib/rfnoc/ctrl_iface.hpp +++ b/host/lib/rfnoc/ctrl_iface.hpp @@ -47,18 +47,6 @@ public: const std::string &name = "0" ); - //! Hold a ref to a task thats feeding push response - virtual void hold_task(uhd::msg_task::sptr task) = 0; - - //! Push a response externall (resp_xport is NULL) - virtual void push_response(const uint32_t *buff) = 0; - - //! Set the command time that will activate - virtual void set_time(const uhd::time_spec_t &time) = 0; - - //! Get the command time that will activate - virtual uhd::time_spec_t get_time(void) = 0; - //! Set the tick rate (converting time into ticks) virtual void set_tick_rate(const double rate) = 0; }; diff --git a/host/lib/rfnoc/legacy_compat.cpp b/host/lib/rfnoc/legacy_compat.cpp index a396fd677..a8b6d4f68 100644 --- a/host/lib/rfnoc/legacy_compat.cpp +++ b/host/lib/rfnoc/legacy_compat.cpp @@ -96,16 +96,6 @@ size_t calc_num_tx_chans_per_radio( ); } -double lambda_const_double(const double d) -{ - return d; -} - -uhd::meta_range_t lambda_const_meta_range(const double start, const double stop, const double step) -{ - return uhd::meta_range_t(start, stop, step); -} - /*! Recreate passed property without bound subscribers. Maintains current property value. */ template <typename T> @@ -114,6 +104,7 @@ static void recreate_property(const uhd::fs_path &path, uhd::property_tree::sptr tree->remove(path); tree->create<T>(path).set(temp); } + /************************************************************************ * Class Definition ***********************************************************************/ @@ -637,10 +628,10 @@ private: // methods ) ; _tree->create<double>(rx_dsp_base_path / "freq/value") - .set_publisher(boost::bind(&lambda_const_double, 0.0)) + .set_publisher([](){ return 0.0; }) ; _tree->create<uhd::meta_range_t>(rx_dsp_base_path / "freq/range") - .set_publisher(boost::bind(&lambda_const_meta_range, 0.0, 0.0, 0.0)) + .set_publisher([](){ return uhd::meta_range_t(0.0, 0.0, 0.0); }) ; } } @@ -670,10 +661,10 @@ private: // methods ) ; _tree->create<double>(tx_dsp_base_path / "freq/value") - .set_publisher(boost::bind(&lambda_const_double, 0.0)) + .set_publisher([](){ return 0.0; }) ; _tree->create<uhd::meta_range_t>(tx_dsp_base_path / "freq/range") - .set_publisher(boost::bind(&lambda_const_meta_range, 0.0, 0.0, 0.0)) + .set_publisher([](){ return uhd::meta_range_t(0.0, 0.0, 0.0); }) ; } } diff --git a/host/lib/rfnoc/xports.hpp b/host/lib/rfnoc/xports.hpp index 98cf71b6d..7bd4f2fc8 100644 --- a/host/lib/rfnoc/xports.hpp +++ b/host/lib/rfnoc/xports.hpp @@ -16,6 +16,7 @@ // #include <uhd/types/sid.hpp> +#include <uhd/types/endianness.hpp> #include <uhd/transport/zero_copy.hpp> namespace uhd { @@ -31,6 +32,7 @@ namespace uhd { size_t send_buff_size; uhd::sid_t send_sid; uhd::sid_t recv_sid; + uhd::endianness_t endianness; }; }; diff --git a/host/lib/types/sid.cpp b/host/lib/types/sid.cpp index b697bd614..40f84a2aa 100644 --- a/host/lib/types/sid.cpp +++ b/host/lib/types/sid.cpp @@ -1,5 +1,5 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014-2016 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,12 +15,12 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include <uhd/types/sid.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/cast.hpp> #include <boost/format.hpp> #include <boost/regex.hpp> #include <boost/lexical_cast.hpp> -#include <uhd/exception.hpp> -#include <uhd/types/sid.hpp> -#include <uhd/utils/cast.hpp> using namespace uhd; @@ -141,7 +141,7 @@ void sid_t::set_dst_blockport(uint32_t new_blockport) set_sid((_sid & 0xFFFFFFF0) | ((new_blockport & 0xF) << 0)); } -sid_t sid_t::reversed() +sid_t sid_t::reversed() const { return sid_t((get_dst() << 16) | get_src()); } diff --git a/host/lib/uhd.rc.in b/host/lib/uhd.rc.in index 24177a00a..dee6bb8a3 100644 --- a/host/lib/uhd.rc.in +++ b/host/lib/uhd.rc.in @@ -1,8 +1,8 @@ #include <windows.h> VS_VERSION_INFO VERSIONINFO - FILEVERSION @TRIMMED_VERSION_MAJOR_API@,@TRIMMED_VERSION_ABI@,@RC_TRIMMED_VERSION_PATCH@,@UHD_GIT_COUNT@ - PRODUCTVERSION @TRIMMED_VERSION_MAJOR_API@,@TRIMMED_VERSION_ABI@,@RC_TRIMMED_VERSION_PATCH@,@UHD_GIT_COUNT@ + FILEVERSION @RC_VERSION_MAJOR_API@,@UHD_VERSION_ABI@,@RC_VERSION_PATCH@,@UHD_GIT_COUNT@ + PRODUCTVERSION @RC_VERSION_MAJOR_API@,@UHD_VERSION_ABI@,@RC_VERSION_PATCH@,@UHD_GIT_COUNT@ FILEFLAGSMASK 0x3fL #ifndef NDEBUG FILEFLAGS 0x0L diff --git a/host/lib/usrp/device3/device3_impl.cpp b/host/lib/usrp/device3/device3_impl.cpp index 50598a519..28b7bc34b 100644 --- a/host/lib/usrp/device3/device3_impl.cpp +++ b/host/lib/usrp/device3/device3_impl.cpp @@ -105,8 +105,7 @@ void device3_impl::enumerate_rfnoc_blocks( size_t n_blocks, size_t base_port, const uhd::sid_t &base_sid, - uhd::device_addr_t transport_args, - uhd::endianness_t endianness + uhd::device_addr_t transport_args ) { // entries that are already connected to this block uhd::sid_t ctrl_sid = base_sid; @@ -131,7 +130,7 @@ void device3_impl::enumerate_rfnoc_blocks( ); UHD_DEVICE3_LOG() << str(boost::format("Setting up NoC-Shell Control for port #0 (SID: %s)...") % xport.send_sid.to_pp_string_hex()); uhd::rfnoc::ctrl_iface::sptr ctrl = uhd::rfnoc::ctrl_iface::make( - endianness == ENDIANNESS_BIG, + xport.endianness == uhd::ENDIANNESS_BIG, xport.send, xport.recv, xport.send_sid, @@ -160,7 +159,7 @@ void device3_impl::enumerate_rfnoc_blocks( ); UHD_DEVICE3_LOG() << str(boost::format("Setting up NoC-Shell Control for port #%d (SID: %s)...") % port_number % xport1.send_sid.to_pp_string_hex()); uhd::rfnoc::ctrl_iface::sptr ctrl1 = uhd::rfnoc::ctrl_iface::make( - endianness == ENDIANNESS_BIG, + xport1.endianness == uhd::ENDIANNESS_BIG, xport1.send, xport1.recv, xport1.send_sid, @@ -173,7 +172,6 @@ void device3_impl::enumerate_rfnoc_blocks( make_args.base_address = xport.send_sid.get_dst(); make_args.device_index = device_index; make_args.tree = subtree; - make_args.is_big_endian = (endianness == ENDIANNESS_BIG); _rfnoc_block_ctrl.push_back(uhd::rfnoc::block_ctrl_base::make(make_args, noc_id)); } } diff --git a/host/lib/usrp/device3/device3_impl.hpp b/host/lib/usrp/device3/device3_impl.hpp index 117e4af1c..c496b5105 100644 --- a/host/lib/usrp/device3/device3_impl.hpp +++ b/host/lib/usrp/device3/device3_impl.hpp @@ -134,7 +134,6 @@ protected: virtual uhd::device_addr_t get_tx_hints(size_t) { return uhd::device_addr_t(); }; virtual uhd::device_addr_t get_rx_hints(size_t) { return uhd::device_addr_t(); }; - virtual uhd::endianness_t get_transport_endianness(size_t mb_index) = 0; //! Is called after a streamer is generated virtual void post_streamer_hooks(uhd::direction_t) {}; @@ -167,8 +166,7 @@ protected: size_t n_blocks, size_t base_port, const uhd::sid_t &base_sid, - uhd::device_addr_t transport_args, - uhd::endianness_t endianness + uhd::device_addr_t transport_args ); /*********************************************************************** diff --git a/host/lib/usrp/device3/device3_io_impl.cpp b/host/lib/usrp/device3/device3_io_impl.cpp index c1bfd1606..2f2e778c9 100644 --- a/host/lib/usrp/device3/device3_io_impl.cpp +++ b/host/lib/usrp/device3/device3_io_impl.cpp @@ -565,7 +565,7 @@ rx_streamer::sptr device3_impl::get_rx_stream(const stream_args_t &args_) //init some streamer stuff std::string conv_endianness; - if (get_transport_endianness(mb_index) == ENDIANNESS_BIG) { + if (xport.endianness == ENDIANNESS_BIG) { my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_be); conv_endianness = "be"; } else { @@ -618,7 +618,7 @@ rx_streamer::sptr device3_impl::get_rx_stream(const stream_args_t &args_) &handle_rx_flowctrl, xport.send_sid, xport.send, - get_transport_endianness(mb_index), + xport.endianness, fc_cache, _1 ), @@ -755,7 +755,7 @@ tx_streamer::sptr device3_impl::get_tx_stream(const uhd::stream_args_t &args_) //init some streamer stuff std::string conv_endianness; - if (get_transport_endianness(mb_index) == ENDIANNESS_BIG) { + if (xport.endianness == ENDIANNESS_BIG) { my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_be); conv_endianness = "be"; } else { @@ -803,7 +803,7 @@ tx_streamer::sptr device3_impl::get_tx_stream(const uhd::stream_args_t &args_) &handle_tx_async_msgs, fc_cache, xport.recv, - get_transport_endianness(mb_index), + xport.endianness, tick_rate_retriever ) ); diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp index 4cf58cd05..0e6cb8574 100644 --- a/host/lib/usrp/mboard_eeprom.cpp +++ b/host/lib/usrp/mboard_eeprom.cpp @@ -600,7 +600,7 @@ template <typename T> static const byte_vector_t to_bytes(const T &item){ } #define sizeof_member(struct_name, member_name) \ - sizeof(reinterpret_cast<struct_name*>(NULL)->member_name) + sizeof(reinterpret_cast<struct_name*>(0)->member_name) static void load_e100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ const size_t num_bytes = offsetof(e100_eeprom_map, model); diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index 028058a58..aa54c2228 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -381,7 +381,33 @@ static void x300_load_fw(wb_iface::sptr fw_reg_ctrl, const std::string &file_nam UHD_MSG(status) << " done!" << std::endl; } -x300_impl::x300_impl(const uhd::device_addr_t &dev_addr) +static const std::string thread_final_msg = "Finished multi-threaded initialization\n"; + +static void thread_msg_handler(uhd::msg::type_t type, const std::string &msg) +{ + static boost::mutex msg_mutex; + boost::mutex::scoped_lock lock(msg_mutex); + + typedef std::pair<uhd::msg::type_t, std::string> msg_pair_t; + typedef std::map<boost::thread::id, std::vector<msg_pair_t> > thread_map_t; + + static thread_map_t thread_list; + thread_list[boost::this_thread::get_id()].push_back(msg_pair_t(type, msg)); + + if (msg == thread_final_msg) + { + BOOST_FOREACH(const thread_map_t::value_type &thread_pair, thread_list) + { + BOOST_FOREACH(const msg_pair_t &msg_pair, thread_pair.second) + { + // Forward the message to the default handler + uhd::msg::default_msg_handler(msg_pair.first, msg_pair.second); + } + } + } +} + +x300_impl::x300_impl(const uhd::device_addr_t &dev_addr) : device3_impl() , _sid_framer(0) { @@ -391,10 +417,33 @@ x300_impl::x300_impl(const uhd::device_addr_t &dev_addr) const device_addrs_t device_args = separate_device_addr(dev_addr); _mb.resize(device_args.size()); + + // Serialize the initialization process + if (dev_addr.has_key("serialize_init") or device_args.size() == 1) { + for (size_t i = 0; i < device_args.size(); i++) + { + this->setup_mb(i, device_args[i]); + } + return; + } + + // Setup a custom messenger handler + uhd::msg::handler_t current_handler = uhd::msg::get_handler(); + uhd::msg::register_handler(&thread_msg_handler); + + // Thread the initialization process + boost::thread_group setup_threads; for (size_t i = 0; i < device_args.size(); i++) { - this->setup_mb(i, device_args[i]); + setup_threads.create_thread( + boost::bind(&x300_impl::setup_mb, this, i, device_args[i]) + ); } + setup_threads.join_all(); + + // restore the original message handler + UHD_MSG(status) << thread_final_msg; + uhd::msg::register_handler(current_handler); } void x300_impl::mboard_members_t::discover_eth( @@ -505,6 +554,16 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) mboard_members_t &mb = _mb[mb_i]; mb.initialization_done = false; + const std::string thread_id( + boost::lexical_cast<std::string>(boost::this_thread::get_id()) + ); + const std::string thread_msg( + "Thread ID " + thread_id + " for motherboard " + + boost::lexical_cast<std::string>(mb_i) + ); + UHD_MSG(status) << std::endl; + UHD_MSG(status) << thread_msg << std::endl; + std::vector<std::string> eth_addrs; // Not choosing eth0 based on resource might cause user issues std::string eth0_addr = dev_addr.has_key("resource") ? dev_addr["resource"] : dev_addr["addr"]; @@ -886,14 +945,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) } //////////////////////////////////////////////////////////////////// - //clear router? - //////////////////////////////////////////////////////////////////// - for (size_t i = 0; i < 512; i++) { - mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, i), 0); - } - - - //////////////////////////////////////////////////////////////////// // setup time sources and properties //////////////////////////////////////////////////////////////////// _tree->create<std::string>(mb_path / "time_source" / "value") @@ -950,8 +1001,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) n_rfnoc_blocks, X300_XB_DST_PCI + 1, /* base port */ uhd::sid_t(X300_SRC_ADDR0, 0, X300_DST_ADDR + mb_i, 0), - dev_addr, - mb.if_pkt_is_big_endian ? ENDIANNESS_BIG : ENDIANNESS_LITTLE + dev_addr ); //////////////// RFNOC ///////////////// @@ -1070,6 +1120,7 @@ uhd::both_xports_t x300_impl::make_transport( zero_copy_xport_params default_buff_args; both_xports_t xports; + xports.endianness = mb.if_pkt_is_big_endian ? ENDIANNESS_BIG : ENDIANNESS_LITTLE; if (mb.xport_path == "nirio") { xports.send_sid = this->allocate_sid(mb, address, X300_SRC_ADDR0, X300_XB_DST_PCI); xports.recv_sid = xports.send_sid.reversed(); diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 133938daf..d082ab76a 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -283,9 +283,6 @@ private: /// More IO stuff uhd::device_addr_t get_tx_hints(size_t mb_index); uhd::device_addr_t get_rx_hints(size_t mb_index); - uhd::endianness_t get_transport_endianness(size_t mb_index) { - return _mb[mb_index].if_pkt_is_big_endian ? uhd::ENDIANNESS_BIG : uhd::ENDIANNESS_LITTLE; - }; void post_streamer_hooks(uhd::direction_t dir); }; diff --git a/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp b/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp index 0e11cb42c..9bf61f998 100644 --- a/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp +++ b/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp @@ -813,8 +813,8 @@ void x300_radio_ctrl_impl::_self_cal_adc_capture_delay(bool print_status) //and count deviations from the expected value _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); - //10ms @ 200MHz = 2 million samples - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + //5ms @ 200MHz = 1 million samples + boost::this_thread::sleep(boost::posix_time::milliseconds(5)); if (_regs->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_I_LOCKED)) { err_code += _regs->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_I_ERROR); } else { @@ -828,8 +828,8 @@ void x300_radio_ctrl_impl::_self_cal_adc_capture_delay(bool print_status) //and count deviations from the expected value _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); - //10ms @ 200MHz = 2 million samples - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + //5ms @ 200MHz = 1 million samples + boost::this_thread::sleep(boost::posix_time::milliseconds(5)); if (_regs->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_Q_LOCKED)) { err_code += _regs->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_Q_ERROR); } else { diff --git a/host/lib/utils/msg.cpp b/host/lib/utils/msg.cpp index 95879a116..d9d2fb66a 100644 --- a/host/lib/utils/msg.cpp +++ b/host/lib/utils/msg.cpp @@ -78,7 +78,12 @@ void uhd::msg::register_handler(const handler_t &handler){ msg_rs().handler = handler; } -static void default_msg_handler(uhd::msg::type_t type, const std::string &msg){ +const uhd::msg::handler_t& uhd::msg::get_handler(){ + boost::mutex::scoped_lock lock(msg_rs().mutex); + return msg_rs().handler; +} + +void uhd::msg::default_msg_handler(uhd::msg::type_t type, const std::string &msg){ static boost::mutex msg_mutex; boost::mutex::scoped_lock lock(msg_mutex); switch(type){ @@ -104,7 +109,7 @@ static void default_msg_handler(uhd::msg::type_t type, const std::string &msg){ } UHD_STATIC_BLOCK(msg_register_default_handler){ - uhd::msg::register_handler(&default_msg_handler); + uhd::msg::register_handler(&uhd::msg::default_msg_handler); } /*********************************************************************** diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 8f7fdcd7c..c691ce1fd 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -28,6 +28,7 @@ SET(test_sources buffer_test.cpp byteswap_test.cpp cast_test.cpp + cal_container_test.cpp chdr_test.cpp convert_test.cpp dict_test.cpp diff --git a/host/tests/cal_container_test.cpp b/host/tests/cal_container_test.cpp new file mode 100644 index 000000000..f45ca429d --- /dev/null +++ b/host/tests/cal_container_test.cpp @@ -0,0 +1,200 @@ +// +// Copyright 2016 Ettus Research +// +// 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 <uhd/cal/power_container.hpp> +#include <uhd/exception.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/shared_ptr.hpp> +#include <vector> +#include <fstream> + +using namespace uhd; +using namespace uhd::cal; + +static const double eps = 1e-8; + +//////////////////////////////////////////////////////////////////////// +BOOST_AUTO_TEST_CASE(test_power_container_bilinear){ +//////////////////////////////////////////////////////////////////////// + + // Create the data container + power_container::sptr container = power_container::make(); + + // Create some data points to add + std::vector<double> pt0(2, 0.0); + std::vector<double> pt1(2, 0.0); + std::vector<double> pt2(2, 0.0); + std::vector<double> pt3(2, 2.0); + + pt1[0] = 2.0; + pt2[1] = 2.0; + + container->add(1.0, pt0); + container->add(1.0, pt1); + container->add(0.0, pt2); + container->add(0.0, pt3); + + // Add points to interpolate against + std::vector<double> test0(2, 1.0); + std::vector<double> test1(2, 1.5); + std::vector<double> test2(2, 0.0); + test2[1] = 1.0; + + BOOST_CHECK_CLOSE(container->get(test0), 0.50, eps); + BOOST_CHECK_CLOSE(container->get(test1), 0.25, eps); + BOOST_CHECK_CLOSE(container->get(test2), 0.50, eps); +} + + +//////////////////////////////////////////////////////////////////////// +BOOST_AUTO_TEST_CASE(test_power_temp_container){ +//////////////////////////////////////////////////////////////////////// + + // Create the data container + power_container::sptr container = power_container::make(); + + // Create some data points to add + std::vector<double> pt0(3, 1.0); + std::vector<double> pt1(3, 2.0); + std::vector<double> pt2(3, 3.0); + + container->add(1.0, pt0); + container->add(2.0, pt1); + container->add(5.0, pt2); + + // Add points to interpolate against + std::vector<double> test0(3, 1.99); + std::vector<double> test1(3, 1.29); + std::vector<double> test2; + test2.push_back(2.59); + test2.push_back(1.29); + test2.push_back(2.99); + + BOOST_CHECK_CLOSE(container->get(test0), 2.0, eps); + BOOST_CHECK_CLOSE(container->get(test1), 1.0, eps); + BOOST_CHECK_CLOSE(container->get(test2), 5.0, eps); +} + +//////////////////////////////////////////////////////////////////////// +BOOST_AUTO_TEST_CASE(test_power_container_metadata){ +//////////////////////////////////////////////////////////////////////// + + // Create the data container + power_container::sptr container = power_container::make(); + + // Create some metadata to add + base_container::metadata_t data; + + std::string fake_serial = "F2A432"; + data["x300"] = fake_serial; + + // Add some metadata + container->add_metadata(data); + + // Check to see if the metadata matches + power_container::metadata_t recovered_data = container->get_metadata(); + + BOOST_CHECK_EQUAL(recovered_data["x300"], fake_serial); +} + +//////////////////////////////////////////////////////////////////////// +BOOST_AUTO_TEST_CASE(test_power_serialization){ +//////////////////////////////////////////////////////////////////////// + + // Create the data container + power_container::sptr container = power_container::make(); + + // Create some metadata to add + base_container::metadata_t data; + + std::string fake_serial = "F2A432"; + data["x300"] = fake_serial; + + // Add some metadata + container->add_metadata(data); + + // Create some data points to add + std::vector<double> pt0(3, 1.0); + std::vector<double> pt1(3, 2.0); + std::vector<double> pt2(3, 3.0); + + container->add(1.0, pt0); + container->add(2.0, pt1); + container->add(5.0, pt2); + + std::string filename("test_power_serialization"); + + // Create/open a file to store the container + { + std::ofstream ofile(filename.c_str()); + + boost::archive::text_oarchive oarchive(ofile); + oarchive << *container; + } + + // Restore to another data container + power_container::sptr new_container = power_container::make(); + + { + std::ifstream ifile(filename.c_str()); + boost::archive::text_iarchive iarchive(ifile); + + iarchive >> *new_container; + } + + // Add points to interpolate against + std::vector<double> test0(3, 1.99); + std::vector<double> test1(3, 1.29); + std::vector<double> test2; + test2.push_back(2.59); + test2.push_back(1.29); + test2.push_back(2.99); + + power_container::metadata_t recovered_data = new_container->get_metadata(); + + BOOST_CHECK_CLOSE(new_container->get(test0), 2.0, eps); + BOOST_CHECK_CLOSE(new_container->get(test1), 1.0, eps); + BOOST_CHECK_CLOSE(new_container->get(test2), 5.0, eps); + + // Check to see if the metadata matches + BOOST_CHECK_EQUAL(recovered_data["x300"], fake_serial); + + std::remove(filename.c_str()); +} + +//////////////////////////////////////////////////////////////////////// +BOOST_AUTO_TEST_CASE(test_interp_singular){ +//////////////////////////////////////////////////////////////////////// + + // Create the data container + power_container::sptr container = power_container::make(); + + // Create some data points to add + // that result in a singular matrix + std::vector<double> pt0(2, 1.0); + std::vector<double> pt1(2, 2.0); + std::vector<double> pt2(2, 3.0); + std::vector<double> pt3(2, 4.0); + + container->add(1.0, pt0); + container->add(2.0, pt1); + container->add(3.0, pt2); + container->add(4.0, pt3); + + std::vector<double> test(2, 2.5); + BOOST_CHECK_CLOSE(container->get(test), 2.5, eps); +} diff --git a/host/tests/device3_test.cpp b/host/tests/device3_test.cpp index 657436717..a81f4ca0a 100644 --- a/host/tests/device3_test.cpp +++ b/host/tests/device3_test.cpp @@ -87,7 +87,6 @@ class pseudo_device3_impl : public uhd::device3 make_args.base_address = TEST_SID0.get_dst(); make_args.device_index = 0; make_args.tree = _tree; - make_args.is_big_endian = false; std::cout << "[PSEUDO] Generating block controls 1/2:" << std::endl; _rfnoc_block_ctrl.push_back( block_ctrl_base::make(make_args) ); diff --git a/host/tests/sid_t_test.cpp b/host/tests/sid_t_test.cpp index 2043398a1..e07e1c9bc 100644 --- a/host/tests/sid_t_test.cpp +++ b/host/tests/sid_t_test.cpp @@ -118,8 +118,9 @@ BOOST_AUTO_TEST_CASE(test_sid_t_set) { BOOST_CHECK_EQUAL(sid.get_dst_addr(), (uint32_t)0x0c); BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (uint32_t)0xbc); - sid_t flipped_sid = sid.reversed(); + const sid_t flipped_sid = sid.reversed(); BOOST_CHECK_EQUAL(flipped_sid.get(), (uint32_t)0x0cbc0a0b); + BOOST_CHECK_EQUAL(flipped_sid.reversed(), sid); // In-place sid.reverse(); diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index eb5a29df9..33cb5a972 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -156,9 +156,7 @@ ENDIF(NOT HAVE_PYTHON_MODULE_REQUESTS) IF(ENABLE_USRP2) SET(burners usrp2_card_burner.py - usrp2_card_burner_gui.py usrp_n2xx_net_burner.py - usrp_n2xx_net_burner_gui.py ) IF(WIN32 AND UHD_RELEASE_MODE) #include dd.exe diff --git a/host/utils/b100_eeprom.h b/host/utils/b100_eeprom.h new file mode 100644 index 000000000..afb6f7ec5 --- /dev/null +++ b/host/utils/b100_eeprom.h @@ -0,0 +1,15 @@ +unsigned const char b100_eeprom_bin[] = { + 0xc2, 0x00, 0x25, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, + 0x02, 0x00, 0x6b, 0x12, 0x00, 0x08, 0x80, 0xfe, 0x75, 0x80, 0x00, 0x75, + 0xb2, 0x0b, 0x75, 0xa0, 0x03, 0x75, 0xb4, 0x03, 0x75, 0xb1, 0x00, 0x75, + 0xb6, 0x00, 0x90, 0xe6, 0x8a, 0xe4, 0xf0, 0x00, 0x90, 0xe6, 0x80, 0xe4, + 0xf0, 0x7f, 0x00, 0xef, 0x24, 0xe0, 0xf5, 0x82, 0xe4, 0x34, 0xe1, 0xf5, + 0x83, 0xe4, 0xf0, 0x0f, 0xbf, 0x10, 0xf0, 0x7e, 0x00, 0x7f, 0x00, 0x0e, + 0xbe, 0x00, 0x01, 0x0f, 0xef, 0x30, 0xe7, 0xf7, 0x63, 0xa0, 0x01, 0x80, + 0xf2, 0x78, 0x00, 0xe8, 0x44, 0x00, 0x60, 0x0c, 0x79, 0x00, 0x90, 0x18, + 0x00, 0xe4, 0xf0, 0xa3, 0xd8, 0xfc, 0xd9, 0xfa, 0xd0, 0x83, 0xd0, 0x82, + 0xf6, 0xd8, 0xfd, 0xc0, 0x82, 0xc0, 0x83, 0x75, 0x82, 0x00, 0x22, 0x75, + 0x81, 0x07, 0x12, 0x00, 0x49, 0xe5, 0x82, 0x60, 0x03, 0x02, 0x00, 0x03, + 0x02, 0x00, 0x03, 0x80, 0x01, 0xe6, 0x00, 0x00 +}; +unsigned int b100_eeprom_bin_len = 140; diff --git a/host/utils/fx2_init_eeprom.cpp b/host/utils/fx2_init_eeprom.cpp index cf7fb2de2..e0915d9f2 100644 --- a/host/utils/fx2_init_eeprom.cpp +++ b/host/utils/fx2_init_eeprom.cpp @@ -20,11 +20,13 @@ #include <uhd/property_tree.hpp> #include <boost/program_options.hpp> #include <boost/format.hpp> +#include <boost/filesystem.hpp> #include <boost/algorithm/string/predicate.hpp> #include <iostream> -//#include <cstdlib> -#ifdef UHD_PLATFORM_LINUX #include <fstream> +#include "usrp1_eeprom.h" +#include "b100_eeprom.h" +#ifdef UHD_PLATFORM_LINUX #include <unistd.h> // syscall constants #include <fcntl.h> // O_NONBLOCK #include <sys/syscall.h> @@ -39,13 +41,14 @@ namespace po = boost::program_options; int UHD_SAFE_MAIN(int argc, char *argv[]){ std::string type; + std::string image; po::options_description desc("Allowed options"); desc.add_options() ("help", "help message") - ("image", po::value<std::string>(), "BIN image file") + ("image", po::value<std::string>(), "BIN image file; if not specified, use built-in image") ("vid", po::value<std::string>(), "VID of device to program") ("pid", po::value<std::string>(), "PID of device to program") - ("type", po::value<std::string>(), "device type (usrp1 or b100)") + ("type", po::value<std::string>(&type), "device type (usrp1 or b100, required if using built-in image)") ; po::variables_map vm; @@ -78,9 +81,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //load the options into the address uhd::device_addr_t device_addr; device_addr["type"] = type; - if(vm.count("vid") or vm.count("pid") or vm.count("type")) { + if(vm.count("vid") or vm.count("pid")) { if(not (vm.count("vid") and vm.count("pid") and vm.count("type"))) { - std::cerr << "ERROR: Must specify vid, pid, and type if specifying any of the three args" << std::endl; + std::cerr << "ERROR: Must specify vid, pid, and type if specifying any of the two former args" << std::endl; } else { device_addr["vid"] = vm["vid"].as<std::string>(); device_addr["pid"] = vm["pid"].as<std::string>(); @@ -90,6 +93,38 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ device_addr["vid"] = FX2_VENDOR_ID; device_addr["pid"] = FX2_PRODUCT_ID; } + if(vm.count("image")) { + //if specified, use external image file + image = vm["image"].as<std::string>(); + } else { + //if not specified, use built-ins; requires user to define type + size_t image_len; + unsigned const char* image_data; + + if(!vm.count("type")) { + std::cerr << boost::format("ERROR: Image file not specified and type of device not given. Cannot use built-in images.\n"); + return EXIT_FAILURE; + } + + std::cout << boost::format("Using built-in image for \"%s\".\n") % type; + + if(vm["type"].as<std::string>() == "usrp1") { + image_len = usrp1_eeprom_bin_len; + image_data = usrp1_eeprom_bin; + } else if(vm["type"].as<std::string>() == "b100") { + image_len = b100_eeprom_bin_len; + image_data = b100_eeprom_bin; + } else { + std::cerr << boost::format("ERROR: Unsupported device type \"%s\" specified and no EEPROM image file given.\n") % type; + return EXIT_FAILURE; + } + + //get temporary file name, and write image to that. + image = boost::filesystem::unique_path().string(); + std::ofstream tmp_image(image, std::ofstream::binary); + tmp_image.write((const char*)image_data, image_len); + tmp_image.close(); + } //find and create a control transport to do the writing. @@ -105,9 +140,13 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //uhd::device_addrs_t devs = uhd::device::find(found_addrs[i]); uhd::device::sptr dev = uhd::device::make(found_addrs[i], uhd::device::USRP); uhd::property_tree::sptr tree = dev->get_tree(); - tree->access<std::string>("/mboards/0/load_eeprom").set(vm["image"].as<std::string>()); + tree->access<std::string>("/mboards/0/load_eeprom").set(image); } + //delete temporary image file if we created one + if(!vm.count("image")) { + boost::filesystem::remove(image); + } std::cout << "Power-cycle the usrp for the changes to take effect." << std::endl; return EXIT_SUCCESS; diff --git a/host/utils/octoclock_firmware_burner.cpp b/host/utils/octoclock_firmware_burner.cpp index 4ffaeb0d7..57d6f3cc8 100644 --- a/host/utils/octoclock_firmware_burner.cpp +++ b/host/utils/octoclock_firmware_burner.cpp @@ -15,95 +15,12 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include <algorithm> -#include <csignal> -#include <iostream> -#include <fstream> -#include <stdexcept> -#include <vector> - -#include <boost/foreach.hpp> -#include <boost/asio.hpp> #include <boost/program_options.hpp> -#include <boost/assign.hpp> -#include <stdint.h> -#include <boost/assign/list_of.hpp> #include <boost/format.hpp> -#include <boost/filesystem.hpp> -#include <boost/thread.hpp> - -#include <uhd/device.hpp> -#include <uhd/transport/udp_simple.hpp> -#include <uhd/types/device_addr.hpp> -#include <uhd/types/time_spec.hpp> -#include <uhd/utils/byteswap.hpp> -#include <uhd/utils/paths.hpp> -#include <uhd/utils/paths.hpp> -#include <uhd/utils/safe_main.hpp> - -#include "../lib/usrp_clock/octoclock/common.h" -#include "../lib/utils/ihex.hpp" - -#define MAX_FIRMWARE_SIZE 1024*120 -#define BLOCK_SIZE 256 -#define UDP_TIMEOUT 5 +#include <iostream> -namespace fs = boost::filesystem; namespace po = boost::program_options; -using namespace uhd; -using namespace uhd::transport; - -static int num_ctrl_c = 0; -void sig_int_handler(int){ - num_ctrl_c++; - if(num_ctrl_c == 1){ - std::cout << std::endl << "Are you sure you want to abort the image burning? If you do, your " - "OctoClock device will be bricked!" << std::endl - << "Press Ctrl+C again to abort the image burning procedure." << std::endl << std::endl; - } - else{ - std::cout << std::endl << "Aborting. Your OctoClock device will be bricked." << std::endl - << "Refer to http://files.ettus.com/manual/page_octoclock.html#bootloader" << std::endl - << "for details on restoring your device." << std::endl; - exit(EXIT_FAILURE); - } -} - -uint8_t firmware_image[MAX_FIRMWARE_SIZE]; -size_t firmware_size = 0; -uint8_t octoclock_data[udp_simple::mtu]; -octoclock_packet_t *pkt_in = reinterpret_cast<octoclock_packet_t *>(octoclock_data); -std::string firmware_path, actual_firmware_path; -size_t num_blocks = 0; -bool hex = true; - -static uint16_t calculate_crc(uint8_t* buffer, uint16_t len){ - uint16_t crc = 0xFFFF; - - for(size_t i = 0; i < len; i++){ - crc ^= buffer[i]; - for(uint8_t j = 0; j < 8; ++j){ - if(crc & 1) crc = (crc >> 1) ^ 0xA001; - else crc = (crc >> 1); - } - } - - return crc; -} - -/* - * Functions - */ -void list_octoclocks(){ - device_addrs_t found_octoclocks = device::find(uhd::device_addr_t(), device::CLOCK); - - std::cout << "Available OctoClock devices:" << std::endl; - BOOST_FOREACH(const device_addr_t &oc, found_octoclocks){ - std::cout << " * " << oc["addr"] << std::endl; - } -} - void print_image_loader_warning(const std::string &fw_path, const po::variables_map &vm){ // Newline + indent #ifdef UHD_PLATFORM_WIN32 @@ -115,10 +32,8 @@ void print_image_loader_warning(const std::string &fw_path, const po::variables_ std::string uhd_image_loader = str(boost::format("uhd_image_loader --args=\"type=octoclock,addr=%s\"" "%s --fw-path=%s") % vm["addr"].as<std::string>() % nl % fw_path); - std::cout << "************************************************************************************************" << std::endl - << "WARNING: This utility will be removed in an upcoming version of UHD. In the future, use" << std::endl - << " this command:" << std::endl + << "ERROR: This utility has been removed in this version of UHD. Use this command:" << std::endl << std::endl << uhd_image_loader << std::endl << std::endl @@ -126,301 +41,21 @@ void print_image_loader_warning(const std::string &fw_path, const po::variables_ << std::endl; } -/* - * Manually find bootloader. This sends multiple packets in order to increase chances of getting - * bootloader before it switches to the application. - */ -device_addrs_t bootloader_find(const std::string &ip_addr){ - udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(OCTOCLOCK_UDP_CTRL_PORT)); - - octoclock_packet_t pkt_out; - pkt_out.sequence = uhd::htonx<uint32_t>(std::rand()); - pkt_out.code = OCTOCLOCK_QUERY_CMD; - pkt_out.len = 0; - size_t len = 0; - - device_addrs_t addrs; - - boost::system_time comm_timeout = boost::get_system_time() + boost::posix_time::milliseconds(3000); - - while(boost::get_system_time() < comm_timeout){ - UHD_OCTOCLOCK_SEND_AND_RECV(udp_transport, OCTOCLOCK_FW_COMPAT_NUM, OCTOCLOCK_QUERY_CMD, pkt_out, len, octoclock_data); - if(UHD_OCTOCLOCK_PACKET_MATCHES(OCTOCLOCK_QUERY_ACK, pkt_out, pkt_in, len) and - pkt_in->proto_ver == OCTOCLOCK_BOOTLOADER_PROTO_VER){ - addrs.push_back(device_addr_t()); - addrs[0]["type"] = "octoclock-bootloader"; - addrs[0]["addr"] = udp_transport->get_recv_addr(); - break; - } - } - - return addrs; -} - -void read_firmware(){ - std::ifstream firmware_file(actual_firmware_path.c_str(), std::ios::binary); - firmware_size = size_t(fs::file_size(actual_firmware_path)); - if(firmware_size > MAX_FIRMWARE_SIZE){ - firmware_file.close(); - throw uhd::runtime_error(str(boost::format("Firmware file too large: %d > %d") - % firmware_size % (MAX_FIRMWARE_SIZE))); - } - firmware_file.read((char*)firmware_image, firmware_size); - firmware_file.close(); - - num_blocks = (firmware_size % BLOCK_SIZE) ? ((firmware_size / BLOCK_SIZE) + 1) - : (firmware_size / BLOCK_SIZE); -} - -void burn_firmware(udp_simple::sptr udp_transport){ - octoclock_packet_t pkt_out; - pkt_out.sequence = uhd::htonx<uint32_t>(std::rand()); - pkt_out.len = (uint16_t)firmware_size; - pkt_out.crc = calculate_crc(firmware_image, firmware_size); - size_t len = 0, current_pos = 0; - - //Tell OctoClock not to jump to application, wait for us instead - std::cout << "Telling OctoClock to prepare for firmware download..." << std::flush; - UHD_OCTOCLOCK_SEND_AND_RECV(udp_transport, OCTOCLOCK_FW_COMPAT_NUM, PREPARE_FW_BURN_CMD, pkt_out, len, octoclock_data); - if(UHD_OCTOCLOCK_PACKET_MATCHES(FW_BURN_READY_ACK, pkt_out, pkt_in, len)) std::cout << "ready." << std::endl; - else{ - std::cout << std::endl; - if(hex) fs::remove(actual_firmware_path); - throw uhd::runtime_error("Could not get OctoClock in valid state for firmware download."); - } - - std::cout << std::endl << "Burning firmware." << std::endl; - pkt_out.code = FILE_TRANSFER_CMD; - - //Actual burning below - size_t num_tries = 0; - for(size_t i = 0; i < num_blocks; i++){ - num_tries = 0; - pkt_out.sequence++; - pkt_out.addr = i*BLOCK_SIZE; - std::cout << "\r * Progress: " << int(double(i)/double(num_blocks)*100) - << "% (" << (i+1) << "/" << num_blocks << " blocks)" << std::flush; - - memset(pkt_out.data, 0, BLOCK_SIZE); - memcpy((void*)(pkt_out.data), &firmware_image[i*BLOCK_SIZE], BLOCK_SIZE); - - bool success = false; - while(num_tries <= 5){ - UHD_OCTOCLOCK_SEND_AND_RECV(udp_transport, OCTOCLOCK_FW_COMPAT_NUM, FILE_TRANSFER_CMD, pkt_out, len, octoclock_data); - if(UHD_OCTOCLOCK_PACKET_MATCHES(FILE_TRANSFER_ACK, pkt_out, pkt_in, len)){ - success = true; - break; - } - else{ - num_tries++; - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } - } - if(not success){ - std::cout << std::endl; - if(hex) fs::remove(actual_firmware_path); - throw uhd::runtime_error("Failed to burn firmware to OctoClock!"); - } - - current_pos += BLOCK_SIZE; - } - - std::cout << "\r * Progress: 100% (" << num_blocks << "/" << num_blocks << " blocks)" << std::endl; -} - -void verify_firmware(udp_simple::sptr udp_transport){ - octoclock_packet_t pkt_out; - pkt_out.proto_ver = OCTOCLOCK_FW_COMPAT_NUM; - pkt_out.sequence = uhd::htonx<uint32_t>(std::rand()); - size_t len = 0, current_pos = 0; - - for(size_t i = 0; i < num_blocks; i++){ - pkt_out.sequence++; - pkt_out.addr = i*BLOCK_SIZE; - std::cout << "\r * Progress: " << int(double(i)/double(num_blocks)*100) - << "% (" << (i+1) << "/" << num_blocks << " blocks)" << std::flush; - - UHD_OCTOCLOCK_SEND_AND_RECV(udp_transport, OCTOCLOCK_FW_COMPAT_NUM, READ_FW_CMD, pkt_out, len, octoclock_data); - if(UHD_OCTOCLOCK_PACKET_MATCHES(READ_FW_ACK, pkt_out, pkt_in, len)){ - if(memcmp((void*)(pkt_in->data), &firmware_image[i*BLOCK_SIZE], - std::min(int(firmware_size-current_pos), BLOCK_SIZE))){ - std::cout << std::endl; - if(hex) fs::remove(actual_firmware_path); - throw uhd::runtime_error("Failed to verify OctoClock firmware!"); - } - } - else{ - std::cout << std::endl; - if(hex) fs::remove(actual_firmware_path); - throw uhd::runtime_error("Failed to verify OctoClock firmware!"); - } - } - - std::cout << "\r * Progress: 100% (" << num_blocks << "/" << num_blocks << " blocks)" << std::endl; -} - -bool reset_octoclock(const std::string &ip_addr){ - udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(OCTOCLOCK_UDP_CTRL_PORT)); - - octoclock_packet_t pkt_out; - pkt_out.sequence = uhd::htonx<uint32_t>(std::rand()); - size_t len; - - UHD_OCTOCLOCK_SEND_AND_RECV(udp_transport, OCTOCLOCK_FW_COMPAT_NUM, RESET_CMD, pkt_out, len, octoclock_data); - if(not UHD_OCTOCLOCK_PACKET_MATCHES(RESET_ACK, pkt_out, pkt_in, len)){ - std::cout << std::endl; - if(hex) fs::remove(actual_firmware_path); - throw uhd::runtime_error("Failed to place device in state to receive firmware."); - } - - boost::this_thread::sleep(boost::posix_time::milliseconds(500)); - return (bootloader_find(ip_addr).size() == 1); -} - -void finalize(udp_simple::sptr udp_transport){ - octoclock_packet_t pkt_out; - pkt_out.len = 0; - pkt_out.sequence = uhd::htonx<uint32_t>(std::rand()); - size_t len = 0; - - UHD_OCTOCLOCK_SEND_AND_RECV(udp_transport, OCTOCLOCK_FW_COMPAT_NUM, FINALIZE_BURNING_CMD, pkt_out, len, octoclock_data); - if(not UHD_OCTOCLOCK_PACKET_MATCHES(FINALIZE_BURNING_ACK, pkt_out, pkt_in, len)){ - std::cout << std::endl; - if(hex) fs::remove(actual_firmware_path); - std::cout << "no ACK. Bootloader may not have loaded application." << std::endl; - } -} - -void octoclock_convert_ihex(const std::string &hex_path, const std::string &bin_path){ - ihex_reader hex_reader(hex_path); - hex_reader.to_bin_file(bin_path); -} - -int UHD_SAFE_MAIN(UHD_UNUSED(int argc), UHD_UNUSED(char *argv[])){ - - std::string ip_addr; +int main(int argc, const char *argv[]) +{ + std::string ip_addr, firmware_path; 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.") - ("fw-path", po::value<std::string>(&firmware_path), "Specify a custom firmware path.") + ("addr", po::value<std::string>(&ip_addr)->default_value("addr=1.2.3.4"), "Specify an IP address.") + ("fw-path", po::value<std::string>(&firmware_path)->default_value("path/to/firmware"), "Specify a custom firmware path.") ("list", "List all available OctoClock 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 << "OctoClock Firmware Burner" << std::endl << std::endl; - - std::cout << "Burns a firmware image file onto an OctoClock device. Specify" << std::endl - << "the address of the OctoClock with the --addr option. To burn" << std::endl - << "a custom firmware image, use the --fw-path option. Otherwise, the" << std::endl - << "utility will use the default image. To list all available" << std::endl - << "OctoClock devices without burning firmware, use the --list" << std::endl - << "option." << std::endl << std::endl; - - std::cout << desc << std::endl; - return EXIT_SUCCESS; - } - - //List all available devices - if(vm.count("list")){ - list_octoclocks(); - return EXIT_SUCCESS; - } - - if(not (vm.count("addr"))){ - throw uhd::runtime_error("You must specify an address with the --addr option!"); - } - udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(OCTOCLOCK_UDP_FW_PORT)); - - //If custom path given, make sure it exists - if(vm.count("fw-path")){ - //Expand tilde usage if applicable - #ifndef UHD_PLATFORM_WIN32 - if(firmware_path.find("~/") == 0) firmware_path.replace(0,1,getenv("HOME")); - #endif - - if(not fs::exists(firmware_path)){ - throw uhd::runtime_error(str(boost::format("This filepath does not exist: %s") % firmware_path)); - } - } - else firmware_path = find_image_path("octoclock_r4_fw.hex"); - - //If Intel hex file detected, convert to binary - std::string ext = fs::extension(firmware_path); - if(ext == ".hex"){ - std::cout << "Found firmware at path: " << firmware_path << std::endl; - - //Write firmware .bin file to temporary directory - fs::path temp_bin = fs::path(fs::path(get_tmp_path()) / str(boost::format("octoclock_fw_%d.bin") - % time_spec_t::get_system_time().get_full_secs())); - octoclock_convert_ihex(firmware_path, temp_bin.string()); - - actual_firmware_path = temp_bin.string(); - } - else if(ext == ".bin"){ - hex = false; - actual_firmware_path = firmware_path; - std::cout << "Found firmware at path: " << firmware_path << std::endl; - } - else throw uhd::runtime_error("The firmware file has in improper extension (must be .hex or .bin)."); - - std::cout << std::endl << boost::format("Searching for OctoClock with IP address %s...") % ip_addr << std::flush; - device_addrs_t octoclocks = device::find(str(boost::format("addr=%s") % ip_addr), device::CLOCK); - if(octoclocks.size() == 1){ - if(octoclocks[0]["type"] == "octoclock"){ - std::cout << "found. Resetting..." << std::flush; - if(reset_octoclock(ip_addr)) std::cout << "successful." << std::endl; - else{ - std::cout << "failed." << std::endl; - if(hex) fs::remove(actual_firmware_path); - throw uhd::runtime_error("Failed to reset OctoClock device into its bootloader."); - } - } - else std::cout << "found." << std::endl; - } - else{ - std::cout << "failed." << std::endl; - if(hex) fs::remove(actual_firmware_path); - throw uhd::runtime_error("Could not find OctoClock with given IP address!"); - } - - read_firmware(); - print_image_loader_warning(firmware_path, vm); - std::signal(SIGINT, &sig_int_handler); - - burn_firmware(udp_transport); - std::cout << "Verifying firmware." << std::endl; - verify_firmware(udp_transport); - std::cout << std::endl << "Telling OctoClock bootloader to load application..." << std::flush; - finalize(udp_transport); - std::cout << "done." << std::endl; - - std::cout << "Waiting for OctoClock to reinitialize..." << std::flush; - boost::this_thread::sleep(boost::posix_time::milliseconds(500)); - octoclocks = device::find(str(boost::format("addr=%s") % ip_addr), device::CLOCK); - if(octoclocks.size() == 1){ - if(octoclocks[0]["type"] == "octoclock-bootloader"){ - std::cout << std::endl; - if(hex) fs::remove(actual_firmware_path); - throw uhd::runtime_error("Firmware did not load properly."); - } - else{ - std::cout << "found." << std::endl << std::endl - << "Successfully burned firmware." << std::endl << std::endl; - } - } - else{ - std::cout << std::endl; - if(hex) fs::remove(actual_firmware_path); - throw uhd::runtime_error("Failed to reinitialize OctoClock."); - } - if(hex) fs::remove(actual_firmware_path); - - return EXIT_SUCCESS; + return EXIT_FAILURE; } diff --git a/host/utils/uhd_find_devices.cpp b/host/utils/uhd_find_devices.cpp index c258c580e..0ef1055cf 100644 --- a/host/utils/uhd_find_devices.cpp +++ b/host/utils/uhd_find_devices.cpp @@ -17,11 +17,11 @@ #include <uhd/utils/safe_main.hpp> #include <uhd/device.hpp> +#include <boost/lexical_cast.hpp> #include <boost/program_options.hpp> #include <boost/format.hpp> #include <iostream> #include <cstdlib> - namespace po = boost::program_options; int UHD_SAFE_MAIN(int argc, char *argv[]){ @@ -49,12 +49,50 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ return EXIT_FAILURE; } - for (size_t i = 0; i < device_addrs.size(); i++){ - std::cout << "--------------------------------------------------" << std::endl; + typedef std::map<std::string, std::set<std::string> > device_multi_addrs_t; + typedef std::map<std::string, device_multi_addrs_t> device_addrs_filtered_t; + device_addrs_filtered_t found_devices; + for (auto it = device_addrs.begin(); it != device_addrs.end(); ++it) { + std::string serial = (*it)["serial"]; + found_devices[serial] = device_multi_addrs_t(); + BOOST_FOREACH (std::string key, it->keys()) { + if (key != "serial") { + found_devices[serial][key].insert(it->get(key)); + } + } + for (auto sit = it + 1; sit != device_addrs.end();) { + if ((*sit)["serial"] == serial) { + BOOST_FOREACH (std::string key, sit->keys()) { + if (key != "serial") { + found_devices[serial][key].insert(sit->get(key)); + } + } + sit = device_addrs.erase(sit); + } else { + sit++; + } + } + } + + int i = 0; + for (auto dit = found_devices.begin(); dit != found_devices.end(); ++dit) { + std::cout << "--------------------------------------------------" + << std::endl; std::cout << "-- UHD Device " << i << std::endl; - std::cout << "--------------------------------------------------" << std::endl; - std::cout << device_addrs[i].to_pp_string() << std::endl << std::endl; - //uhd::device::make(device_addrs[i]); //test make + std::cout << "--------------------------------------------------" + << std::endl; + std::stringstream ss; + ss << "Device Address:" << std::endl; + ss << boost::format(" serial: %s") % dit->first << std::endl; + for (auto mit = dit->second.begin(); mit != dit->second.end(); ++mit) { + for (auto vit = mit->second.begin(); vit != mit->second.end(); + ++vit) { + ss << boost::format(" %s: %s") % mit->first % *vit + << std::endl; + } + } + std::cout << ss.str() << std::endl << std::endl; + i++; } return EXIT_SUCCESS; diff --git a/host/utils/usrp1_eeprom.h b/host/utils/usrp1_eeprom.h new file mode 100644 index 000000000..d544073b0 --- /dev/null +++ b/host/utils/usrp1_eeprom.h @@ -0,0 +1,15 @@ +unsigned const char usrp1_eeprom_bin[] = { + 0xc2, 0xfe, 0xff, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, + 0x02, 0x00, 0x6b, 0x12, 0x00, 0x08, 0x80, 0xfe, 0x75, 0x80, 0x38, 0x75, + 0xb2, 0x3b, 0x75, 0xa0, 0xc0, 0x75, 0xb4, 0xcf, 0x75, 0xb1, 0xf0, 0x75, + 0xb6, 0xf8, 0x90, 0xe6, 0x8a, 0xe4, 0xf0, 0x00, 0x90, 0xe6, 0x80, 0xe4, + 0xf0, 0x7f, 0x00, 0xef, 0x24, 0xe0, 0xf5, 0x82, 0xe4, 0x34, 0xe1, 0xf5, + 0x83, 0xe4, 0xf0, 0x0f, 0xbf, 0x10, 0xf0, 0x7e, 0x00, 0x7f, 0x00, 0x0e, + 0xbe, 0x00, 0x01, 0x0f, 0xef, 0x30, 0xe7, 0xf7, 0x63, 0xa0, 0x40, 0x80, + 0xf2, 0x78, 0x00, 0xe8, 0x44, 0x00, 0x60, 0x0c, 0x79, 0x00, 0x90, 0x18, + 0x00, 0xe4, 0xf0, 0xa3, 0xd8, 0xfc, 0xd9, 0xfa, 0xd0, 0x83, 0xd0, 0x82, + 0xf6, 0xd8, 0xfd, 0xc0, 0x82, 0xc0, 0x83, 0x75, 0x82, 0x00, 0x22, 0x75, + 0x81, 0x07, 0x12, 0x00, 0x49, 0xe5, 0x82, 0x60, 0x03, 0x02, 0x00, 0x03, + 0x02, 0x00, 0x03, 0x80, 0x01, 0xe6, 0x00, 0x00 +}; +unsigned int usrp1_eeprom_bin_len = 140; diff --git a/host/utils/usrp_n2xx_net_burner.py b/host/utils/usrp_n2xx_net_burner.py index 5605b0028..372078f91 100755 --- a/host/utils/usrp_n2xx_net_burner.py +++ b/host/utils/usrp_n2xx_net_burner.py @@ -16,8 +16,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -# TODO: make it autodetect UHD devices - import optparse import math import os @@ -29,72 +27,7 @@ import time import platform import subprocess -######################################################################## -# constants -######################################################################## -UDP_FW_UPDATE_PORT = 49154 -UDP_MAX_XFER_BYTES = 1024 -UDP_TIMEOUT = 3 -UDP_POLL_INTERVAL = 0.10 #in seconds - -USRP2_FW_PROTO_VERSION = 7 #should be unused after r6 - -#from bootloader_utils.h - -FPGA_IMAGE_SIZE_BYTES = 1572864 -FW_IMAGE_SIZE_BYTES = 31744 -SAFE_FPGA_IMAGE_LOCATION_ADDR = 0x00000000 -SAFE_FW_IMAGE_LOCATION_ADDR = 0x003F0000 -PROD_FPGA_IMAGE_LOCATION_ADDR = 0x00180000 -PROD_FW_IMAGE_LOCATION_ADDR = 0x00300000 - -FLASH_DATA_PACKET_SIZE = 256 - -#see fw_common.h -FLASH_ARGS_FMT = '!LLLLL256s' -FLASH_INFO_FMT = '!LLLLL256x' -FLASH_IP_FMT = '!LLLL260x' -FLASH_HW_REV_FMT = '!LLLL260x' - -n2xx_revs = { - 0x0a00: ["n200_r3", "n200_r2"], - 0x0a10: ["n200_r4"], - 0x0a01: ["n210_r3", "n210_r2"], - 0x0a11: ["n210_r4"] - } - -class update_id_t: - USRP2_FW_UPDATE_ID_WAT = ord(' ') - USRP2_FW_UPDATE_ID_OHAI_LOL = ord('a') - USRP2_FW_UPDATE_ID_OHAI_OMG = ord('A') - USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = ord('f') - USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = ord('F') - USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = ord('e') - USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = ord('E') - USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = ord('d') - USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = ord('D') - USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = ord('B') - USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = ord('w') - USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = ord('W') - USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = ord('r') - USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = ord('R') - USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = ord('s') - USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = ord('S') - USRP2_FW_UPDATE_ID_I_CAN_HAS_HW_REV_LOL = ord('v') - USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_OMG = ord('V') - USRP2_FW_UPDATE_ID_KTHXBAI = ord('~') - -_seq = -1 -def seq(): - global _seq - _seq = _seq+1 - return _seq - -######################################################################## -# print equivalent uhd_image_loader command -######################################################################## def print_image_loader_warning(fw, fpga, reset, safe, addr): - # Newline + indent if platform.system() == "Windows": nl = " ^\n " @@ -121,8 +54,7 @@ def print_image_loader_warning(fw, fpga, reset, safe, addr): print("") print("************************************************************************************************") - print("WARNING: This utility will be removed in an upcoming version of UHD. In the future, use") - print(" this command:") + print("ERROR: This utility has been removed in this version of UHD. Use this command:") print("") print(uhd_image_loader) print("") @@ -130,383 +62,16 @@ def print_image_loader_warning(fw, fpga, reset, safe, addr): print("") ######################################################################## -# helper functions -######################################################################## -def unpack_flash_args_fmt(s): - return struct.unpack(FLASH_ARGS_FMT, s) #(proto_ver, pktid, seq, flash_addr, length, data) - -def unpack_flash_info_fmt(s): - return struct.unpack(FLASH_INFO_FMT, s) #(proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes) - -def unpack_flash_ip_fmt(s): - return struct.unpack(FLASH_IP_FMT, s) #(proto_ver, pktid, seq, ip_addr) - -def unpack_flash_hw_rev_fmt(s): - return struct.unpack(FLASH_HW_REV_FMT, s) #proto_ver, pktid, seq, hw_rev - -def pack_flash_args_fmt(proto_ver, pktid, seq, flash_addr, length, data=bytes()): - return struct.pack(FLASH_ARGS_FMT, proto_ver, pktid, seq, flash_addr, length, data) - -def pack_flash_info_fmt(proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes): - return struct.pack(FLASH_INFO_FMT, proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes) - -def pack_flash_hw_rev_fmt(proto_ver, pktid, seq, hw_rev): - return struct.pack(FLASH_HW_REV_FMT, proto_ver, pktid, seq, hw_rev) - -def is_valid_fpga_image(fpga_image): - for i in range(0,63): - if fpga_image[i:i+1] == bytes(b'\xFF'): continue - if fpga_image[i:i+2] == bytes(b'\xAA\x99'): return True - return False - -def is_valid_fw_image(fw_image): - return fw_image[:4] == bytes(b'\x0B\x0B\x0B\x0B') - - -######################################################################## -# interface discovery and device enumeration -######################################################################## -def command(*args): - p = subprocess.Popen( - args, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) - ret = p.wait() - verbose = p.stdout.read().decode('utf-8') - if ret != 0: raise Exception(verbose) - return verbose - -def get_interfaces(): - if(platform.system() is "Windows"): return win_get_interfaces() - else: return unix_get_interfaces() - -def unix_get_interfaces(): - ifconfig = command("/sbin/ifconfig") - ip_addr_re = "cast\D*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" - bcasts = re.findall(ip_addr_re, ifconfig) - return bcasts - -def win_get_interfaces(): - from ctypes import Structure, windll, sizeof - from ctypes import POINTER, byref - from ctypes import c_ulong, c_uint, c_ubyte, c_char - MAX_ADAPTER_DESCRIPTION_LENGTH = 128 - MAX_ADAPTER_NAME_LENGTH = 256 - MAX_ADAPTER_ADDRESS_LENGTH = 8 - class IP_ADDR_STRING(Structure): - pass - LP_IP_ADDR_STRING = POINTER(IP_ADDR_STRING) - IP_ADDR_STRING._fields_ = [ - ("next", LP_IP_ADDR_STRING), - ("ipAddress", c_char * 16), - ("ipMask", c_char * 16), - ("context", c_ulong)] - class IP_ADAPTER_INFO (Structure): - pass - LP_IP_ADAPTER_INFO = POINTER(IP_ADAPTER_INFO) - IP_ADAPTER_INFO._fields_ = [ - ("next", LP_IP_ADAPTER_INFO), - ("comboIndex", c_ulong), - ("adapterName", c_char * (MAX_ADAPTER_NAME_LENGTH + 4)), - ("description", c_char * (MAX_ADAPTER_DESCRIPTION_LENGTH + 4)), - ("addressLength", c_uint), - ("address", c_ubyte * MAX_ADAPTER_ADDRESS_LENGTH), - ("index", c_ulong), - ("type", c_uint), - ("dhcpEnabled", c_uint), - ("currentIpAddress", LP_IP_ADDR_STRING), - ("ipAddressList", IP_ADDR_STRING), - ("gatewayList", IP_ADDR_STRING), - ("dhcpServer", IP_ADDR_STRING), - ("haveWins", c_uint), - ("primaryWinsServer", IP_ADDR_STRING), - ("secondaryWinsServer", IP_ADDR_STRING), - ("leaseObtained", c_ulong), - ("leaseExpires", c_ulong)] - GetAdaptersInfo = windll.iphlpapi.GetAdaptersInfo - GetAdaptersInfo.restype = c_ulong - GetAdaptersInfo.argtypes = [LP_IP_ADAPTER_INFO, POINTER(c_ulong)] - adapterList = (IP_ADAPTER_INFO * 10)() - buflen = c_ulong(sizeof(adapterList)) - rc = GetAdaptersInfo(byref(adapterList[0]), byref(buflen)) - if rc == 0: - for a in adapterList: - adNode = a.ipAddressList - while True: - #convert ipAddr and ipMask into hex addrs that can be turned into a bcast addr - try: - ipAddr = adNode.ipAddress.decode() - ipMask = adNode.ipMask.decode() - except: ipAddr = None - if ipAddr and ipMask: - hexAddr = struct.unpack("<L", socket.inet_aton(ipAddr))[0] - hexMask = struct.unpack("<L", socket.inet_aton(ipMask))[0] - if(hexAddr and hexMask): #don't broadcast on 255.255.255.255, that's just lame - yield socket.inet_ntoa(struct.pack("<L", (hexAddr & hexMask) | (~hexMask) & 0xFFFFFFFF)) - try: adNode = adNode.next - except: break - if not adNode: break - -def enumerate_devices(): - for bcast_addr in get_interfaces(): - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - sock.settimeout(0.1) - out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_OHAI_LOL, 0, 0, 0) - sock.sendto(out_pkt, (bcast_addr, UDP_FW_UPDATE_PORT)) - still_goin = True - while(still_goin): - try: - pkt = sock.recv(UDP_MAX_XFER_BYTES) - (proto_ver, pktid, rxseq, ip_addr) = unpack_flash_ip_fmt(pkt) - if(pktid == update_id_t.USRP2_FW_UPDATE_ID_OHAI_OMG): - use_addr = socket.inet_ntoa(struct.pack("<L", socket.ntohl(ip_addr))) - burner = burner_socket(use_addr, True) - yield "%s (%s)" % (socket.inet_ntoa(struct.pack("<L", socket.ntohl(ip_addr))), n2xx_revs[burner.get_hw_rev()][0]) - except socket.timeout: - still_goin = False - -######################################################################## # Burner class, holds a socket and send/recv routines ######################################################################## class burner_socket(object): - def __init__(self, addr, quiet): - self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + def __init__(self, addr): self._addr = addr - self._quiet = quiet - self._sock.settimeout(UDP_TIMEOUT) - self._sock.connect((addr, UDP_FW_UPDATE_PORT)) - self.set_callbacks(lambda *a: None, lambda *a: None) - self.init_update(quiet) #check that the device is there - self.get_hw_rev() - - def set_callbacks(self, progress_cb, status_cb): - self._progress_cb = progress_cb - self._status_cb = status_cb - - def send_and_recv(self, pkt): - self._sock.send(pkt) - return self._sock.recv(UDP_MAX_XFER_BYTES) - - #just here to validate comms - def init_update(self,quiet): - out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_OHAI_LOL, seq(), 0, 0) - try: in_pkt = self.send_and_recv(out_pkt) - except socket.timeout: raise Exception("No response from device") - (proto_ver, pktid, rxseq, ip_addr) = unpack_flash_ip_fmt(in_pkt) - if pktid == update_id_t.USRP2_FW_UPDATE_ID_OHAI_OMG: - if not quiet: print("USRP-N2XX found.") - else: - raise Exception("Invalid reply received from device.") - - def get_hw_rev(self): - out_pkt = pack_flash_hw_rev_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_I_CAN_HAS_HW_REV_LOL, seq(), 0) - in_pkt = self.send_and_recv(out_pkt) - (proto_ver, pktid, rxseq, hw_rev) = unpack_flash_hw_rev_fmt(in_pkt) - if(pktid != update_id_t.USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_OMG): hw_rev = 0 - return socket.ntohs(hw_rev) - - memory_size_bytes = 0 - sector_size_bytes = 0 - def get_flash_info(self): - if (self.memory_size_bytes != 0) and (self.sector_size_bytes != 0): - return (self.memory_size_bytes, self.sector_size_bytes) - - out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL, seq(), 0, 0) - in_pkt = self.send_and_recv(out_pkt) - - (proto_ver, pktid, rxseq, self.sector_size_bytes, self.memory_size_bytes) = unpack_flash_info_fmt(in_pkt) - - if pktid != update_id_t.USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG: - raise Exception("Invalid reply %c from device." % (chr(pktid))) - - return (self.memory_size_bytes, self.sector_size_bytes) def burn_fw(self, fw, fpga, reset, safe, check_rev=True): + " Just a dummy lol " print_image_loader_warning(fw, fpga, reset, safe, self._addr) - (flash_size, sector_size) = self.get_flash_info() - hw_rev = self.get_hw_rev() - - if hw_rev in n2xx_revs: print("Hardware type: %s" % n2xx_revs[hw_rev][0]) - print("Flash size: %i\nSector size: %i\n" % (flash_size, sector_size)) - - if fpga: - #validate fpga image name against hardware rev - if(check_rev and hw_rev != 0 and not any(name in fpga for name in n2xx_revs[hw_rev])): - raise Exception("Error: incorrect FPGA image version. Please use the correct image for device %s" % n2xx_revs[hw_rev][0]) - - if safe: image_location = SAFE_FPGA_IMAGE_LOCATION_ADDR - else: image_location = PROD_FPGA_IMAGE_LOCATION_ADDR - - fpga_file = open(fpga, 'rb') - fpga_image = fpga_file.read() - - if len(fpga_image) > FPGA_IMAGE_SIZE_BYTES: - raise Exception("Error: FPGA image file too large.") - - if not is_valid_fpga_image(fpga_image): - raise Exception("Error: Invalid FPGA image file.") - - if (len(fpga_image) + image_location) > flash_size: - raise Exception("Error: Cannot write past end of device") - - print("Begin FPGA write: this should take about 1 minute...") - start_time = time.time() - self.erase_image(image_location, FPGA_IMAGE_SIZE_BYTES) - self.write_image(fpga_image, image_location) - self.verify_image(fpga_image, image_location) - print("Time elapsed: %f seconds"%(time.time() - start_time)) - print("\n\n") - - if fw: - if safe: image_location = SAFE_FW_IMAGE_LOCATION_ADDR - else: image_location = PROD_FW_IMAGE_LOCATION_ADDR - - fw_file = open(fw, 'rb') - fw_image = fw_file.read() - - if len(fw_image) > FW_IMAGE_SIZE_BYTES: - raise Exception("Error: Firmware image file too large.") - - if not is_valid_fw_image(fw_image): - raise Exception("Error: Invalid firmware image file.") - - if (len(fw_image) + image_location) > flash_size: - raise Exception("Error: Cannot write past end of device") - - print("Begin firmware write: this should take about 1 second...") - start_time = time.time() - self.erase_image(image_location, FW_IMAGE_SIZE_BYTES) - self.write_image(fw_image, image_location) - self.verify_image(fw_image, image_location) - print("Time elapsed: %f seconds"%(time.time() - start_time)) - print("\n\n") - - if reset: self.reset_usrp() - - def write_image(self, image, addr): - print("Writing image") - self._status_cb("Writing") - writedata = image - #we split the image into smaller (256B) bits and send them down the wire - (mem_size, sector_size) = self.get_flash_info() - if (addr + len(writedata)) > mem_size: - raise Exception("Error: Cannot write past end of device") - - while writedata: - out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL, seq(), addr, FLASH_DATA_PACKET_SIZE, writedata[:FLASH_DATA_PACKET_SIZE]) - in_pkt = self.send_and_recv(out_pkt) - - (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) - - if pktid != update_id_t.USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG: - raise Exception("Invalid reply %c from device." % (chr(pktid))) - - writedata = writedata[FLASH_DATA_PACKET_SIZE:] - addr += FLASH_DATA_PACKET_SIZE - self._progress_cb(float(len(image)-len(writedata))/len(image)) - - def verify_image(self, image, addr): - print("Verifying data") - self._status_cb("Verifying") - readsize = len(image) - readdata = bytes() - while readsize > 0: - if readsize < FLASH_DATA_PACKET_SIZE: thisreadsize = readsize - else: thisreadsize = FLASH_DATA_PACKET_SIZE - out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL, seq(), addr, thisreadsize) - in_pkt = self.send_and_recv(out_pkt) - - (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) - - if pktid != update_id_t.USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG: - raise Exception("Invalid reply %c from device." % (chr(pktid))) - - readdata += data[:thisreadsize] - readsize -= FLASH_DATA_PACKET_SIZE - addr += FLASH_DATA_PACKET_SIZE - self._progress_cb(float(len(readdata))/len(image)) - - print("Read back %i bytes" % len(readdata)) - # print readdata - - # for i in range(256, 512): - # print "out: %i in: %i" % (ord(image[i]), ord(readdata[i])) - - if readdata != image: - raise Exception("Verify failed. Image did not write correctly.") - else: - print("Success.") - - def read_image(self, image, size, addr): - print("Reading image") - readsize = size - readdata = str() - while readsize > 0: - if readsize < FLASH_DATA_PACKET_SIZE: thisreadsize = readsize - else: thisreadsize = FLASH_DATA_PACKET_SIZE - out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL, seq(), addr, thisreadsize) - in_pkt = self.send_and_recv(out_pkt) - - (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) - - if pktid != update_id_t.USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG: - raise Exception("Invalid reply %c from device." % (chr(pktid))) - - readdata += data[:thisreadsize] - readsize -= FLASH_DATA_PACKET_SIZE - addr += FLASH_DATA_PACKET_SIZE - - print("Read back %i bytes" % len(readdata)) - - #write to disk - f = open(image, 'w') - f.write(readdata) - f.close() - - def reset_usrp(self): - out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL, seq(), 0, 0) - try: in_pkt = self.send_and_recv(out_pkt) - except socket.timeout: return - - (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) - if pktid == update_id_t.USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG: - raise Exception("Device failed to reset.") - - def erase_image(self, addr, length): - self._status_cb("Erasing") - #get flash info first - (flash_size, sector_size) = self.get_flash_info() - if (addr + length) > flash_size: - raise Exception("Cannot erase past end of device") - - out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL, seq(), addr, length) - in_pkt = self.send_and_recv(out_pkt) - - (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) - - if pktid != update_id_t.USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG: - raise Exception("Invalid reply %c from device." % (chr(pktid))) - - print("Erasing %i bytes at %i" % (length, addr)) - start_time = time.time() - - #now wait for it to finish - while(True): - out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL, seq(), 0, 0) - in_pkt = self.send_and_recv(out_pkt) - - (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) - - if pktid == update_id_t.USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG: break - elif pktid != update_id_t.USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG: - raise Exception("Invalid reply %c from device." % (chr(pktid))) - time.sleep(0.01) #decrease network overhead by waiting a bit before polling - self._progress_cb(min(1.0, (time.time() - start_time)/(length/80e3))) - - ######################################################################## # command line options ######################################################################## @@ -529,47 +94,4 @@ def get_options(): ######################################################################## if __name__=='__main__': options = get_options() - - if options.list: - print('Possible network devices:') - print(' ' + '\n '.join(enumerate_devices())) - #enumerate_devices() - exit() - - if not options.addr: raise Exception('no address specified') - - if not options.fpga and not options.fw and not options.reset: raise Exception('Must specify either a firmware image or FPGA image, and/or reset.') - - if options.overwrite_safe and not options.read: - print("Are you REALLY, REALLY sure you want to overwrite the safe image? This is ALMOST ALWAYS a terrible idea.") - print("If your image is faulty, your USRP2+ will become a brick until reprogrammed via JTAG.") - - python_major_version = int(platform.python_version_tuple()[0]) - if python_major_version > 2: - response = input("""Type "yes" to continue, or anything else to quit: """) - else: - response = raw_input("""Type "yes" to continue, or anything else to quit: """) - if response != "yes": sys.exit(0) - - burner = burner_socket(addr=options.addr,quiet=False) - - if options.read: - if options.fw: - file = options.fw - if os.path.isfile(file): - response = raw_input("File already exists -- overwrite? (y/n) ") - if response != "y": sys.exit(0) - size = FW_IMAGE_SIZE_BYTES - addr = SAFE_FW_IMAGE_LOCATION_ADDR if options.overwrite_safe else PROD_FW_IMAGE_LOCATION_ADDR - burner.read_image(file, size, addr) - - if options.fpga: - file = options.fpga - if os.path.isfile(file): - response = input("File already exists -- overwrite? (y/n) ") - if response != "y": sys.exit(0) - size = FPGA_IMAGE_SIZE_BYTES - addr = SAFE_FPGA_IMAGE_LOCATION_ADDR if options.overwrite_safe else PROD_FPGA_IMAGE_LOCATION_ADDR - burner.read_image(file, size, addr) - - else: burner.burn_fw(fw=options.fw, fpga=options.fpga, reset=options.reset, safe=options.overwrite_safe, check_rev=not options.dont_check_rev) + burner_socket(options.addr).burn_fw(fw=options.fw, fpga=options.fpga, reset=options.reset, safe=options.overwrite_safe, check_rev=not options.dont_check_rev) diff --git a/host/utils/usrp_n2xx_net_burner_gui.py b/host/utils/usrp_n2xx_net_burner_gui.py deleted file mode 100755 index bad065f08..000000000 --- a/host/utils/usrp_n2xx_net_burner_gui.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2011 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/>. -# - -import usrp_n2xx_net_burner #import implementation -try: - import tkinter, tkinter.filedialog, tkinter.font, tkinter.messagebox -except ImportError: - import tkFileDialog, tkFont, tkMessageBox - import Tkinter as tkinter - tkinter.filedialog = tkFileDialog - tkinter.font = tkFont - tkinter.messagebox = tkMessageBox -import os - -class BinFileEntry(tkinter.Frame): - """ - Simple file entry widget for getting the file path of bin files. - Combines a label, entry, and button with file dialog callback. - """ - - def __init__(self, root, what, def_path=''): - self._what = what - tkinter.Frame.__init__(self, root) - tkinter.Label(self, text=what+":").pack(side=tkinter.LEFT) - self._entry = tkinter.Entry(self, width=50) - self._entry.insert(tkinter.END, def_path) - self._entry.pack(side=tkinter.LEFT) - tkinter.Button(self, text="...", command=self._button_cb).pack(side=tkinter.LEFT) - - def _button_cb(self): - filename = tkinter.filedialog.askopenfilename( - parent=self, - filetypes=[('bin files', '*.bin'), ('all files', '*.*')], - title="Select bin file for %s"%self._what, - initialdir=os.path.dirname(self.get_filename()), - ) - - # open file on your own - if filename: - self._entry.delete(0, tkinter.END) - self._entry.insert(0, filename) - - def get_filename(self): - return self._entry.get() - -class ProgressBar(tkinter.Canvas): - """ - A simple implementation of a progress bar. - Draws rectangle that fills from left to right. - """ - - def __init__(self, root, width=500, height=20): - self._width = width - self._height = height - tkinter.Canvas.__init__(self, root, relief="sunken", borderwidth=2, width=self._width-2, height=self._height-2) - self._last_fill_pixels = None - self.set(0.0) - - def set(self, frac): - """ - Update the progress where fraction is between 0.0 and 1.0 - """ - #determine the number of pixels to draw - fill_pixels = int(round(self._width*frac)) - if fill_pixels == self._last_fill_pixels: return - self._last_fill_pixels = fill_pixels - - #draw a rectangle representing the progress - if frac: self.create_rectangle(0, 0, fill_pixels, self._height, fill="#357EC7") - else: self.create_rectangle(0, 0, self._width, self._height, fill="#E8E8E8") - -class DeviceEntryWidget(tkinter.Frame): - """ - Simple entry widget for getting the network device name. - Combines a label, entry, and helpful text box with hints. - """ - - def __init__(self, root, text=''): - tkinter.Frame.__init__(self, root) - - tkinter.Button(self, text="Rescan for Devices", command=self._reload_cb).pack() - - self._hints = tkinter.Listbox(self) - self._hints_addrs_only = tkinter.Listbox(self) - - self._hints.bind("<<ListboxSelect>>", self._listbox_cb) - self._hints_addrs_only.bind("<<ListboxSelect>>", self._listbox_cb) - - self._reload_cb() - self._hints.pack(expand=tkinter.YES, fill=tkinter.X) - - frame = tkinter.Frame(self) - frame.pack() - - tkinter.Label(frame, text="Network Address:").pack(side=tkinter.LEFT) - self._entry = tkinter.Entry(frame, width=50) - self._entry.insert(tkinter.END, text) - self._entry.pack(side=tkinter.LEFT) - - def _reload_cb(self): - self._hints.delete(0, tkinter.END) - for hint in usrp_n2xx_net_burner.enumerate_devices(): - self._hints.insert(tkinter.END, hint) - self._hints_addrs_only.insert(tkinter.END, hint.split(" (")[0]) - - def _listbox_cb(self, event): - try: - sel = self._hints_addrs_only.get(self._hints.curselection()[0]) - self._entry.delete(0, tkinter.END) - self._entry.insert(0, sel) - except Exception as e: print(e) - - def get_devname(self): - return self._entry.get() - -class SectionLabel(tkinter.Label): - """ - Make a text label with bold font. - """ - - def __init__(self, root, text): - tkinter.Label.__init__(self, root, text=text) - - #set the font bold - f = tkinter.font.Font(font=self['font']) - f['weight'] = 'bold' - self['font'] = f.name - -class USRPN2XXNetBurnerApp(tkinter.Frame): - """ - The top level gui application for the usrp-n2xx network burner. - Creates entry widgets and button with callback to write images. - """ - - def __init__(self, root, addr, fw, fpga): - - tkinter.Frame.__init__(self, root) - - #pack the file entry widgets - SectionLabel(self, text="Select Images").pack(pady=5) - self._fw_img_entry = BinFileEntry(self, "Firmware Image", def_path=fw) - self._fw_img_entry.pack() - self._fpga_img_entry = BinFileEntry(self, "FPGA Image", def_path=fpga) - self._fpga_img_entry.pack() - - #pack the destination entry widget - SectionLabel(self, text="Select Device").pack(pady=5) - self._net_dev_entry = DeviceEntryWidget(self, text=addr) - self._net_dev_entry.pack() - - #the do it button - SectionLabel(self, text="").pack(pady=5) - button = tkinter.Button(self, text="Burn Images", command=self._burn) - self._enable_input = lambda: button.configure(state=tkinter.NORMAL) - self._disable_input = lambda: button.configure(state=tkinter.DISABLED) - button.pack() - - #a progress bar to monitor the status - progress_frame = tkinter.Frame(self) - progress_frame.pack() - self._status = tkinter.StringVar() - tkinter.Label(progress_frame, textvariable=self._status).pack(side=tkinter.LEFT) - self._pbar = ProgressBar(progress_frame) - self._pbar.pack(side=tkinter.RIGHT, expand=True) - - def _burn(self): - #grab strings from the gui - fw = self._fw_img_entry.get_filename() - fpga = self._fpga_img_entry.get_filename() - addr = self._net_dev_entry.get_devname() - - #check input - if not addr: - tkinter.messagebox.showerror('Error:', 'No address specified!') - return - if not fw and not fpga: - tkinter.messagebox.showerror('Error:', 'No images specified!') - return - if fw and not os.path.exists(fw): - tkinter.messagebox.showerror('Error:', 'Firmware image not found!') - return - if fpga and not os.path.exists(fpga): - tkinter.messagebox.showerror('Error:', 'FPGA image not found!') - return - - self._disable_input() - try: - #make a new burner object and attempt the burner operation - burner = usrp_n2xx_net_burner.burner_socket(addr=addr,quiet=False) - - #setup callbacks that update the gui - def status_cb(status): - self._pbar.set(0.0) #status change, reset the progress - self._status.set("%s %s "%(status.title(), image_type)) - self.update() - def progress_cb(progress): - self._pbar.set(progress) - self.update() - - if options.overwrite_safe: - if tkinter.messagebox.askyesno("Overwrite safe images?", "Overwrite safe images! This is ALMOST ALWAYS a terrible idea."): - for (image_type, fw_img, fpga_img) in (('FPGA', '', fpga), ('Firmware', fw, '')): - burner.set_callbacks(progress_cb=progress_cb, status_cb=status_cb) - burner.burn_fw(fw=fw_img, fpga=fpga_img, reset=False, safe=True, check_rev=not options.dont_check_rev) - - if tkinter.messagebox.askyesno("Burn was successful!", "Reset the device?"): - burner.reset_usrp() - else: - for (image_type, fw_img, fpga_img) in (('FPGA', '', fpga), ('Firmware', fw, '')): - burner.set_callbacks(progress_cb=progress_cb, status_cb=status_cb) - burner.burn_fw(fw=fw_img, fpga=fpga_img, reset=False, safe=False, check_rev=not options.dont_check_rev) - - if tkinter.messagebox.askyesno("Burn was successful!", "Reset the device?"): - burner.reset_usrp() - - except Exception as e: - tkinter.messagebox.showerror('Verbose:', 'Error: %s'%str(e)) - - #reset the progress bar - self._pbar.set(0.0) - self._status.set("") - self._enable_input() - -######################################################################## -# main -######################################################################## -if __name__=='__main__': - options = usrp_n2xx_net_burner.get_options() - root = tkinter.Tk() - root.title('USRP-N2XX Net Burner') - USRPN2XXNetBurnerApp(root, addr=options.addr, fw=options.fw, fpga=options.fpga).pack() - root.mainloop() - exit() diff --git a/host/utils/usrp_n2xx_simple_net_burner.cpp b/host/utils/usrp_n2xx_simple_net_burner.cpp index f85ea9def..9077ef2f6 100644 --- a/host/utils/usrp_n2xx_simple_net_burner.cpp +++ b/host/utils/usrp_n2xx_simple_net_burner.cpp @@ -15,171 +15,11 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include <csignal> -#include <iostream> -#include <fstream> -#include <time.h> -#include <vector> - -#include <boost/foreach.hpp> -#include <boost/asio.hpp> -#include <boost/filesystem.hpp> #include <boost/program_options.hpp> -#include <boost/assign.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/property_tree.hpp> -#include <uhd/transport/if_addrs.hpp> -#include <uhd/transport/udp_simple.hpp> -#include <uhd/types/dict.hpp> -#include <uhd/utils/byteswap.hpp> -#include <uhd/utils/paths.hpp> -#include <uhd/utils/safe_main.hpp> -#include <uhd/utils/safe_call.hpp> +#include <iostream> -namespace fs = boost::filesystem; namespace po = boost::program_options; -using namespace boost::algorithm; -using namespace uhd; -using namespace uhd::transport; - -#define UDP_FW_UPDATE_PORT 49154 -#define UDP_MAX_XFER_BYTES 1024 -#define UDP_TIMEOUT 3 -#define UDP_POLL_INTERVAL 0.10 //in seconds -#define USRP2_FW_PROTO_VERSION 7 //should be unused after r6 -#define USRP2_UDP_UPDATE_PORT 49154 -#define FLASH_DATA_PACKET_SIZE 256 -#define FPGA_IMAGE_SIZE_BYTES 1572864 -#define FW_IMAGE_SIZE_BYTES 31744 - -#define PROD_FPGA_IMAGE_LOCATION_ADDR 0x00180000 -#define SAFE_FPGA_IMAGE_LOCATION_ADDR 0x00000000 - -#define PROD_FW_IMAGE_LOCATION_ADDR 0x00300000 -#define SAFE_FW_IMAGE_LOCATION_ADDR 0x003F0000 - -typedef enum { - UNKNOWN = ' ', - - USRP2_QUERY = 'a', - USRP2_ACK = 'A', - - GET_FLASH_INFO_CMD = 'f', - GET_FLASH_INFO_ACK = 'F', - - ERASE_FLASH_CMD = 'e', - ERASE_FLASH_ACK = 'E', - - CHECK_ERASING_DONE_CMD = 'd', - DONE_ERASING_ACK = 'D', - NOT_DONE_ERASING_ACK = 'B', - - WRITE_FLASH_CMD = 'w', - WRITE_FLASH_ACK = 'W', - - READ_FLASH_CMD = 'r', - READ_FLASH_ACK = 'R', - - RESET_USRP_CMD = 's', - RESET_USRP_ACK = 'S', - - GET_HW_REV_CMD = 'v', - GET_HW_REV_ACK = 'V', - -} usrp2_fw_update_id_t; - -typedef struct { - uint32_t proto_ver; - uint32_t id; - uint32_t seq; - union { - uint32_t ip_addr; - uint32_t hw_rev; - struct { - uint32_t flash_addr; - uint32_t length; - uint8_t data[256]; - } flash_args; - struct { - uint32_t sector_size_bytes; - uint32_t memory_size_bytes; - } flash_info_args; - } data; -} usrp2_fw_update_data_t; - -//Mapping revision numbers to filenames -uhd::dict<uint32_t, std::string> filename_map = boost::assign::map_list_of - (0, "N2XX") - (0xa, "n200_r3") - (0x100a, "n200_r4") - (0x10a, "n210_r3") - (0x110a, "n210_r4") -; - -uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; -uint8_t fpga_image[FPGA_IMAGE_SIZE_BYTES]; -uint8_t fw_image[FW_IMAGE_SIZE_BYTES]; - -/*********************************************************************** - * Signal handlers - **********************************************************************/ -static int num_ctrl_c = 0; -void sig_int_handler(int){ - num_ctrl_c++; - if(num_ctrl_c == 1){ - std::cout << std::endl << "Are you sure you want to abort the image burning? If you do, your " - "USRP-N Series unit will be bricked!" << std::endl - << "Press Ctrl+C again to abort the image burning procedure." << std::endl << std::endl; - } - else{ - std::cout << std::endl << "Aborting. Your USRP-N Series unit will be bricked." << std::endl - << "Refer to http://files.ettus.com/manual/page_usrp2.html#usrp2_loadflash_brick" << std::endl - << "for details on restoring your device." << std::endl; - exit(EXIT_FAILURE); - } -} - -/*********************************************************************** - * List all connected USRP N2XX devices - **********************************************************************/ -void list_usrps(){ - udp_simple::sptr udp_bc_transport; - const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); - uint32_t hw_rev; - - usrp2_fw_update_data_t usrp2_ack_pkt = usrp2_fw_update_data_t(); - usrp2_ack_pkt.proto_ver = htonx<uint32_t>(USRP2_FW_PROTO_VERSION); - usrp2_ack_pkt.id = htonx<uint32_t>(USRP2_QUERY); - - std::cout << "Available USRP N2XX devices:" << std::endl; - - //Send UDP packets to all broadcast addresses - BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){ - //Avoid the loopback device - if(if_addrs.inet == boost::asio::ip::address_v4::loopback().to_string()) continue; - udp_bc_transport = udp_simple::make_broadcast(if_addrs.bcast, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); - udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt))); - - size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_ACK){ - usrp2_ack_pkt.id = htonx<uint32_t>(GET_HW_REV_CMD); - udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt))); - - size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == GET_HW_REV_ACK){ - hw_rev = ntohl(update_data_in->data.hw_rev); - } - - std::cout << boost::format(" * %s (%s)\n") % udp_bc_transport->get_recv_addr() % filename_map[hw_rev]; - } - } -} /*********************************************************************** * Find USRP N2XX with specified IP address and return type @@ -225,8 +65,7 @@ void print_image_loader_warning(const std::string &fw_path, } std::cout << "************************************************************************************************" << std::endl - << "WARNING: This utility will be removed in an upcoming version of UHD. In the future, use" << std::endl - << " this command:" << std::endl + << "ERROR: This utility has been removed in this version of UHD. Use this command:" << std::endl << std::endl << uhd_image_loader << std::endl << std::endl @@ -234,336 +73,8 @@ void print_image_loader_warning(const std::string &fw_path, << std::endl; } -/*********************************************************************** - * Find USRP N2XX with specified IP address and return type - **********************************************************************/ -uint32_t find_usrp(udp_simple::sptr udp_transport, bool check_rev){ - uint32_t hw_rev; - bool found_it = false; - - // If the user chooses to not care about the rev, simply check - // for the presence of a USRP N2XX. - uint32_t cmd_id = (check_rev) ? GET_HW_REV_CMD - : USRP2_QUERY; - uint32_t ack_id = (check_rev) ? GET_HW_REV_ACK - : USRP2_ACK; - - const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); - usrp2_fw_update_data_t hw_info_pkt = usrp2_fw_update_data_t(); - hw_info_pkt.proto_ver = htonx<uint32_t>(USRP2_FW_PROTO_VERSION); - hw_info_pkt.id = htonx<uint32_t>(cmd_id); - udp_transport->send(boost::asio::buffer(&hw_info_pkt, sizeof(hw_info_pkt))); - - //Loop and receive until the timeout - size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == ack_id){ - hw_rev = ntohl(update_data_in->data.hw_rev); - if(filename_map.has_key(hw_rev)){ - std::cout << boost::format("Found %s.\n\n") % filename_map[hw_rev]; - found_it = true; - } - else{ - if(check_rev) throw std::runtime_error("Invalid revision found."); - else{ - hw_rev = 0; - std::cout << "Found USRP N2XX." << std::endl; - found_it = true; - } - } - } - if(not found_it) throw std::runtime_error("No USRP N2XX found."); - - return hw_rev; -} - -/*********************************************************************** - * Custom filename validation functions - **********************************************************************/ - -void validate_custom_fpga_file(std::string rev_str, std::string& fpga_path, bool check_rev){ - - //Check for existence of file - if(not fs::exists(fpga_path)) throw std::runtime_error(str(boost::format("No file at specified FPGA path: %s") % fpga_path)); - - //If user cares about revision, use revision string to detect invalid image filename - uhd::fs_path custom_fpga_path(fpga_path); - if(custom_fpga_path.leaf().find(rev_str) == std::string::npos and check_rev){ - throw std::runtime_error(str(boost::format("Invalid FPGA image filename at path: %s\nFilename must contain '%s' to be considered valid for this model.") - % fpga_path % rev_str)); - } -} - -void validate_custom_fw_file(std::string rev_str, std::string& fw_path, bool check_rev){ - - //Check for existence of file - if(not fs::exists(fw_path)) throw std::runtime_error(str(boost::format("No file at specified firmware path: %s") % fw_path)); - - //If user cares about revision, use revision string to detect invalid image filename - uhd::fs_path custom_fw_path(fw_path); - if(custom_fw_path.leaf().find(erase_tail_copy(rev_str,3)) == std::string::npos and check_rev){ - throw std::runtime_error(str(boost::format("Invalid firmware image filename at path: %s\nFilename must contain '%s' to be considered valid for this model.") - % fw_path % erase_tail_copy(rev_str,3))); - } -} - -/*********************************************************************** - * Reading and validating image binaries - **********************************************************************/ - -int read_fpga_image(std::string& fpga_path){ - - //Check size of given image - std::ifstream fpga_file(fpga_path.c_str(), std::ios::binary); - fpga_file.seekg(0, std::ios::end); - size_t fpga_image_size = size_t(fpga_file.tellg()); - if(fpga_image_size > FPGA_IMAGE_SIZE_BYTES){ - throw std::runtime_error(str(boost::format("FPGA image is too large. %d > %d") - % fpga_image_size % FPGA_IMAGE_SIZE_BYTES)); - } - - //Check sequence of bytes in image before reading - uint8_t fpga_test_bytes[63]; - fpga_file.seekg(0, std::ios::beg); - fpga_file.read((char*)fpga_test_bytes,63); - bool is_good = false; - for(int i = 0; i < 62; i++){ - if(fpga_test_bytes[i] == 255) continue; - else if(fpga_test_bytes[i] == 170 and - fpga_test_bytes[i+1] == 153){ - is_good = true; - break; - } - } - if(not is_good) throw std::runtime_error("Not a valid FPGA image."); - - //With image validated, read into utility - fpga_file.seekg(0, std::ios::beg); - fpga_file.read((char*)fpga_image,fpga_image_size); - fpga_file.close(); - - //Return image size - return fpga_image_size; -} - -int read_fw_image(std::string& fw_path){ - - //Check size of given image - std::ifstream fw_file(fw_path.c_str(), std::ios::binary); - fw_file.seekg(0, std::ios::end); - size_t fw_image_size = size_t(fw_file.tellg()); - if(fw_image_size > FW_IMAGE_SIZE_BYTES){ - throw std::runtime_error(str(boost::format("Firmware image is too large. %d > %d") - % fw_image_size % FW_IMAGE_SIZE_BYTES)); - } - - //Check sequence of bytes in image before reading - uint8_t fw_test_bytes[4]; - fw_file.seekg(0, std::ios::beg); - fw_file.read((char*)fw_test_bytes,4); - for(int i = 0; i < 4; i++) if(fw_test_bytes[i] != 11) throw std::runtime_error("Not a valid firmware image."); - - //With image validated, read into utility - fw_file.seekg(0, std::ios::beg); - fw_file.read((char*)fw_image,fw_image_size); - fw_file.close(); - - return fw_image_size; -} - -uint32_t* get_flash_info(std::string& ip_addr){ - - uint32_t *flash_info = new uint32_t[2]; - const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); - - udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); - usrp2_fw_update_data_t get_flash_info_pkt = usrp2_fw_update_data_t(); - get_flash_info_pkt.proto_ver = htonx<uint32_t>(USRP2_FW_PROTO_VERSION); - get_flash_info_pkt.id = htonx<uint32_t>(GET_FLASH_INFO_CMD); - udp_transport->send(boost::asio::buffer(&get_flash_info_pkt, sizeof(get_flash_info_pkt))); - - //Loop and receive until the timeout - size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == GET_FLASH_INFO_ACK){ - flash_info[0] = ntohl(update_data_in->data.flash_info_args.sector_size_bytes); - flash_info[1] = ntohl(update_data_in->data.flash_info_args.memory_size_bytes); - } - else if(ntohl(update_data_in->id) != GET_FLASH_INFO_ACK){ - throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") - % ntohl(update_data_in->id))); - } - - return flash_info; -} - -/*********************************************************************** - * Image burning functions - **********************************************************************/ - -void erase_image(udp_simple::sptr udp_transport, bool is_fw, uint32_t memory_size, bool overwrite_safe){ - - uint32_t image_location_addr = is_fw ? overwrite_safe ? SAFE_FW_IMAGE_LOCATION_ADDR - : PROD_FW_IMAGE_LOCATION_ADDR - : overwrite_safe ? SAFE_FPGA_IMAGE_LOCATION_ADDR - : PROD_FPGA_IMAGE_LOCATION_ADDR; - uint32_t image_size = is_fw ? FW_IMAGE_SIZE_BYTES - : FPGA_IMAGE_SIZE_BYTES; - - //Making sure this won't attempt to erase past end of device - if((image_location_addr+image_size) > memory_size) throw std::runtime_error("Cannot erase past end of device."); - - //UDP receive buffer - const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); - - //Setting up UDP packet - usrp2_fw_update_data_t erase_pkt = usrp2_fw_update_data_t(); - erase_pkt.id = htonx<uint32_t>(ERASE_FLASH_CMD); - erase_pkt.proto_ver = htonx<uint32_t>(USRP2_FW_PROTO_VERSION); - erase_pkt.data.flash_args.flash_addr = htonx<uint32_t>(image_location_addr); - erase_pkt.data.flash_args.length = htonx<uint32_t>(image_size); - - //Begin erasing - udp_transport->send(boost::asio::buffer(&erase_pkt, sizeof(erase_pkt))); - size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == ERASE_FLASH_ACK){ - if(is_fw) std::cout << "Erasing firmware image." << std::endl; - else std::cout << "Erasing FPGA image." << std::endl; - } - else if(ntohl(update_data_in->id) != ERASE_FLASH_ACK){ - throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") - % ntohl(update_data_in->id))); - } - - //Check for erase completion - erase_pkt.id = htonx<uint32_t>(CHECK_ERASING_DONE_CMD); - while(true){ - udp_transport->send(boost::asio::buffer(&erase_pkt, sizeof(erase_pkt))); - size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == DONE_ERASING_ACK){ - std::cout << boost::format(" * Successfully erased %d bytes at %d.\n") - % image_size % image_location_addr; - break; - } - else if(ntohl(update_data_in->id) != NOT_DONE_ERASING_ACK){ - throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") - % ntohl(update_data_in->id))); - } - } -} - -void write_image(udp_simple::sptr udp_transport, bool is_fw, uint8_t* image, - uint32_t memory_size, int image_size, bool overwrite_safe){ - - uint32_t begin_addr = is_fw ? overwrite_safe ? SAFE_FW_IMAGE_LOCATION_ADDR - : PROD_FW_IMAGE_LOCATION_ADDR - : overwrite_safe ? SAFE_FPGA_IMAGE_LOCATION_ADDR - : PROD_FPGA_IMAGE_LOCATION_ADDR; - uint32_t current_addr = begin_addr; - std::string type = is_fw ? "firmware" : "FPGA"; - - //Making sure this won't attempt to write past end of device - if(current_addr+image_size > memory_size) throw std::runtime_error("Cannot write past end of device."); - - //UDP receive buffer - const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); - - //Setting up UDP packet - usrp2_fw_update_data_t write_pkt = usrp2_fw_update_data_t(); - write_pkt.id = htonx<uint32_t>(WRITE_FLASH_CMD); - write_pkt.proto_ver = htonx<uint32_t>(USRP2_FW_PROTO_VERSION); - write_pkt.data.flash_args.length = htonx<uint32_t>(FLASH_DATA_PACKET_SIZE); - - for(int i = 0; i < ((image_size/FLASH_DATA_PACKET_SIZE)+1); i++){ - //Print progress - std::cout << "\rWriting " << type << " image (" - << int((double(current_addr-begin_addr)/double(image_size))*100) << "%)." << std::flush; - - write_pkt.data.flash_args.flash_addr = htonx<uint32_t>(current_addr); - std::copy(image+(i*FLASH_DATA_PACKET_SIZE), image+((i+1)*FLASH_DATA_PACKET_SIZE), write_pkt.data.flash_args.data); - - udp_transport->send(boost::asio::buffer(&write_pkt, sizeof(write_pkt))); - size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != WRITE_FLASH_ACK){ - throw std::runtime_error(str(boost::format("Invalid reply %d from device.") - % ntohl(update_data_in->id))); - } - - current_addr += FLASH_DATA_PACKET_SIZE; - } - std::cout << std::flush << "\rWriting " << type << " image (100%)." << std::endl; - std::cout << boost::format(" * Successfully wrote %d bytes.\n") % image_size; -} - -void verify_image(udp_simple::sptr udp_transport, bool is_fw, uint8_t* image, - uint32_t memory_size, int image_size, bool overwrite_safe){ - - int current_index = 0; - uint32_t begin_addr = is_fw ? overwrite_safe ? SAFE_FW_IMAGE_LOCATION_ADDR - : PROD_FW_IMAGE_LOCATION_ADDR - : overwrite_safe ? SAFE_FPGA_IMAGE_LOCATION_ADDR - : PROD_FPGA_IMAGE_LOCATION_ADDR; - uint32_t current_addr = begin_addr; - std::string type = is_fw ? "firmware" : "FPGA"; - - //Array size needs to be known at runtime, this constant is guaranteed to be larger than any firmware or FPGA image - uint8_t from_usrp[FPGA_IMAGE_SIZE_BYTES]; - - //Making sure this won't attempt to read past end of device - if(current_addr+image_size > memory_size) throw std::runtime_error("Cannot read past end of device."); - - //UDP receive buffer - const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); - - //Setting up UDP packet - usrp2_fw_update_data_t verify_pkt = usrp2_fw_update_data_t(); - verify_pkt.id = htonx<uint32_t>(READ_FLASH_CMD); - verify_pkt.proto_ver = htonx<uint32_t>(USRP2_FW_PROTO_VERSION); - verify_pkt.data.flash_args.length = htonx<uint32_t>(FLASH_DATA_PACKET_SIZE); - - for(int i = 0; i < ((image_size/FLASH_DATA_PACKET_SIZE)+1); i++){ - //Print progress - std::cout << "\rVerifying " << type << " image (" - << int((double(current_addr-begin_addr)/double(image_size))*100) << "%)." << std::flush; - - verify_pkt.data.flash_args.flash_addr = htonx<uint32_t>(current_addr); - - udp_transport->send(boost::asio::buffer(&verify_pkt, sizeof(verify_pkt))); - size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != READ_FLASH_ACK){ - throw std::runtime_error(str(boost::format("Invalid reply %d from device.") - % ntohl(update_data_in->id))); - } - for(int j = 0; j < FLASH_DATA_PACKET_SIZE; j++) from_usrp[current_index+j] = update_data_in->data.flash_args.data[j]; - - current_addr += FLASH_DATA_PACKET_SIZE; - current_index += FLASH_DATA_PACKET_SIZE; - } - for(int i = 0; i < image_size; i++) if(from_usrp[i] != image[i]) throw std::runtime_error("Image write failed."); - - std::cout << std::flush << "\rVerifying " << type << " image (100%)." << std::endl; - std::cout << " * Successful." << std::endl; -} - -void reset_usrp(udp_simple::sptr udp_transport){ - - //Set up UDP transport - const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); - - //Set up UDP packet - usrp2_fw_update_data_t reset_pkt = usrp2_fw_update_data_t(); - reset_pkt.id = htonx<uint32_t>(RESET_USRP_CMD); - reset_pkt.proto_ver = htonx<uint32_t>(USRP2_FW_PROTO_VERSION); - - //Reset USRP - udp_transport->send(boost::asio::buffer(&reset_pkt, sizeof(reset_pkt))); - size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == RESET_USRP_ACK){ - throw std::runtime_error("USRP reset failed."); //There should be no response to this UDP packet - } - else std::cout << std::endl << "Resetting USRP." << std::endl; -} - -int UHD_SAFE_MAIN(int argc, char *argv[]){ - +int main(int argc, char *argv[]) +{ //Establish user options std::string fw_path; std::string ip_addr; @@ -586,142 +97,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); - //Print help message - if(vm.count("help") > 0){ - std::cout << boost::format("N2XX Simple Net Burner\n"); - std::cout << boost::format("Automatically detects and burns standard firmware and FPGA images onto USRP N2XX devices.\n"); - std::cout << boost::format("Can optionally take user input for custom images.\n\n"); - std::cout << desc << std::endl; - return EXIT_SUCCESS; - } - - //List options - if(vm.count("list")){ - list_usrps(); - return EXIT_SUCCESS; - } - - //Store user options - bool burn_fpga = (vm.count("no-fpga") == 0); - bool burn_fw = (vm.count("no-fw") == 0); - bool use_custom_fpga = (vm.count("fpga") > 0); - bool use_custom_fw = (vm.count("fw") > 0); - bool auto_reboot = (vm.count("auto-reboot") > 0); - bool check_rev = (vm.count("dont-check-rev") == 0); - bool overwrite_safe = (vm.count("overwrite-safe") > 0); - int fpga_image_size = 0; - int fw_image_size = 0; - - //Process options and detect invalid option combinations - if(not burn_fpga && not burn_fw){ - std::cout << "No images will be burned." << std::endl; - return EXIT_FAILURE; - } - if(not check_rev){ - //Without knowing a revision, the utility cannot automatically generate a filepath, so the user - //must specify one. The user must also burn both types of images for consistency. - if(not (burn_fpga and burn_fw)) - throw std::runtime_error("If the --dont-check-rev option is used, both FPGA and firmware images need to be burned."); - if(not (use_custom_fpga and use_custom_fw)) - throw std::runtime_error("If the --dont-check-rev option is used, the user must specify image filepaths."); - } - if(overwrite_safe){ - //If the user specifies overwriting safe images, both image types must be burned for consistency. - if(not (burn_fpga and burn_fw)) - throw std::runtime_error("If the --overwrite-safe option is used, both FPGA and firmware images need to be burned."); - - std::cout << "Are you REALLY sure you want to overwrite the safe images?" << std::endl; - std::cout << "This is ALMOST ALWAYS a terrible idea." << std::endl; - std::cout << "Type \"yes\" to continue, or anything else to quit: " << std::flush; - std::string safe_response; - std::getline(std::cin, safe_response); - if(safe_response != "yes"){ - std::cout << "Exiting." << std::endl; - return EXIT_SUCCESS; - } - else std::cout << std::endl; //Formatting - } - - //Find USRP and establish connection - std::cout << boost::format("Searching for USRP N2XX with IP address %s.\n") % ip_addr; - udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); - uint32_t hw_rev = find_usrp(udp_transport, check_rev); - - //Check validity of file locations and binaries before attempting burn - std::cout << "Searching for specified images." << std::endl << std::endl; - if(burn_fpga){ - if(use_custom_fpga){ - //Expand tilde usage if applicable - #ifndef UHD_PLATFORM_WIN32 - if(fpga_path.find("~/") == 0) fpga_path.replace(0,1,getenv("HOME")); - #endif - validate_custom_fpga_file(filename_map[hw_rev], fpga_path, check_rev); - } - else{ - std::string default_fpga_filename = str(boost::format("usrp_%s_fpga.bin") % filename_map[hw_rev]); - fpga_path = find_image_path(default_fpga_filename); - } - - fpga_image_size = read_fpga_image(fpga_path); - } - if(burn_fw){ - if(use_custom_fw){ - //Expand tilde usage if applicable - #ifndef UHD_PLATFORM_WIN32 - if(fw_path.find("~/") == 0) fw_path.replace(0,1,getenv("HOME")); - #endif - validate_custom_fw_file(filename_map[hw_rev], fw_path, check_rev); - } - else{ - std::string default_fw_filename = str(boost::format("usrp_%s_fw.bin") % erase_tail_copy(filename_map[hw_rev],3)); - fw_path = find_image_path(default_fw_filename); - } - - fw_image_size = read_fw_image(fw_path); - } - print_image_loader_warning(fw_path, fpga_path, vm); - std::cout << "Will burn the following images:" << std::endl; - if(burn_fw) std::cout << boost::format(" * Firmware: %s\n") % fw_path; - if(burn_fpga) std::cout << boost::format(" * FPGA: %s\n") % fpga_path; - std::cout << std::endl; - - uint32_t* flash_info = get_flash_info(ip_addr); - std::cout << boost::format("Querying %s for flash information.\n") % filename_map[hw_rev]; - std::cout << boost::format(" * Flash size: %3.2f\n") % flash_info[1]; - std::cout << boost::format(" * Sector size: %3.2f\n\n") % flash_info[0]; - - //Burning images - std::signal(SIGINT, &sig_int_handler); - if(burn_fpga){ - erase_image(udp_transport, false, flash_info[1], overwrite_safe); - write_image(udp_transport, false, fpga_image, flash_info[1], fpga_image_size, overwrite_safe); - verify_image(udp_transport, false, fpga_image, flash_info[1], fpga_image_size, overwrite_safe); - } - if(burn_fpga and burn_fw) std::cout << std::endl; //Formatting - if(burn_fw){ - erase_image(udp_transport, true, flash_info[1], overwrite_safe); - write_image(udp_transport, true, fw_image, flash_info[1], fw_image_size, overwrite_safe); - verify_image(udp_transport, true, fw_image, flash_info[1], fw_image_size, overwrite_safe); - } - - delete(flash_info); - - //Reset USRP N2XX - bool reset = false; - if(auto_reboot) reset = true; - else{ - std::string user_response = "foo"; - while(user_response != "y" and user_response != "" and user_response != "n"){ - std::cout << std::endl << "Image burning successful. Reset USRP (Y/n)? "; - std::getline(std::cin, user_response); - std::transform(user_response.begin(), user_response.end(), user_response.begin(), ::tolower); - reset = (user_response == "" or user_response == "y"); - } - std::cout << std::endl; //Formatting - } - if(reset) reset_usrp(udp_transport); - - return EXIT_SUCCESS; + return EXIT_FAILURE; } diff --git a/host/utils/usrp_x3xx_fpga_burner.cpp b/host/utils/usrp_x3xx_fpga_burner.cpp index e3de8aad3..8f297865b 100644 --- a/host/utils/usrp_x3xx_fpga_burner.cpp +++ b/host/utils/usrp_x3xx_fpga_burner.cpp @@ -15,188 +15,14 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include <csignal> #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 <stdint.h> -#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/paths.hpp> -#include <uhd/utils/safe_main.hpp> -#include <uhd/utils/safe_call.hpp> - -extern "C" { -#include "cdecode.h" -} - -#define X300_FPGA_BIN_SIZE_BYTES 15877916 -#define X300_FPGA_BIT_MAX_SIZE_BYTES 15878032 -#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; - -static int num_ctrl_c = 0; -void sig_int_handler(int){ - num_ctrl_c++; - if(num_ctrl_c == 1){ - std::cout << std::endl << "Are you sure you want to abort the image burning? If you do, your " - "USRP-X series device will be bricked!" << std::endl - << "Press Ctrl+C again to abort the image burning procedure." << std::endl << std::endl; - } - else{ - std::cout << std::endl << "Aborting. Your USRP X-Series device will be bricked." << std::endl - << "http://files.ettus.com/manual/page_usrp_x3x0.html#x3x0_load_fpga_imgs_jtag" << std::endl - << "for details on restoring your device." << std::endl; - exit(EXIT_FAILURE); - } -} - -typedef struct { - uint32_t flags; - uint32_t sector; - uint32_t index; - uint32_t size; - uint16_t data[128]; -} x300_fpga_update_data_t; - -uint8_t x300_data_in_mem[udp_simple::mtu]; -uint8_t intermediary_packet_data[X300_PACKET_SIZE_BYTES]; - -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"), device::USRP); - - 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, device::USRP); - - 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, device::USRP); - - 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 print_image_loader_warning(const std::string &fpga_path, const po::variables_map &vm){ - +void print_image_loader_warning(const std::string &fpga_path, const po::variables_map &vm) +{ // Newline + indent #ifdef UHD_PLATFORM_WIN32 const std::string nl = " ^\n "; @@ -210,11 +36,9 @@ void print_image_loader_warning(const std::string &fpga_path, const po::variable if(vm.count("addr") > 0){ uhd_image_loader += str(boost::format(",addr=%s") % vm["addr"].as<std::string>()); - if(vm.count("configure") > 0){ uhd_image_loader += ",configure"; } - if(vm.count("verify") > 0){ uhd_image_loader += ",verify"; } @@ -251,8 +75,7 @@ void print_image_loader_warning(const std::string &fpga_path, const po::variable } std::cout << "************************************************************************************************" << std::endl - << "WARNING: This utility will be removed in an upcoming version of UHD. In the future, use" << std::endl - << " this command:" << std::endl + << "ERROR: This utility has been removed in this version of UHD. Use this command:" << std::endl << std::endl << uhd_image_loader << std::endl << std::endl @@ -260,218 +83,17 @@ void print_image_loader_warning(const std::string &fpga_path, const po::variable << std::endl; } -void ethernet_burn(udp_simple::sptr udp_transport, std::string fpga_path, bool verify){ - 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<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."); - } - - size_t current_pos = 0; - size_t sectors = fpga_image_size / X300_FLASH_SECTOR_SIZE; - - //Each sector - for(size_t i = 0; i < fpga_image_size; i += X300_FLASH_SECTOR_SIZE){ - - //Print progress percentage at beginning of each sector - std::cout << "\rProgress: " << int(double(i)/double(fpga_image_size)*100) - << "% (" << (i / X300_FLASH_SECTOR_SIZE) << "/" - << sectors << " sectors)" << std::flush; - - //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<uint32_t>(send_packet.flags); - - send_packet.sector = htonx<uint32_t>(X300_FPGA_SECTOR_START + (i/X300_FLASH_SECTOR_SIZE)); - send_packet.index = htonx<uint32_t>((j % X300_FLASH_SECTOR_SIZE) / 2); - send_packet.size = htonx<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(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(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<uint16_t>(send_packet.data[k]); - } - - udp_transport->send(boost::asio::buffer(&send_packet, sizeof(send_packet))); - - if (udp_transport->recv(boost::asio::buffer(x300_data_in_mem), UDP_TIMEOUT) == 0) - throw std::runtime_error("Timed out waiting for ACK!"); - - 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<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))); - - if (udp_transport->recv(boost::asio::buffer(x300_data_in_mem), UDP_TIMEOUT) == 0) - throw std::runtime_error("Timed out waiting for ACK!"); - 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 << "\rProgress: " << "100% (" << sectors << "/" << sectors << " sectors)" << 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<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<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); +int main(int argc, char *argv[]){ 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.") + ("addr", po::value<std::string>(&ip_addr)->default_value("1.2.3.4"), "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).") + ("type", po::value<std::string>(&image_type)->default_value("HG"), "Specify an image type (1G, HG, XG), leave blank for current type.") + ("fpga-path", po::value<std::string>(&fpga_path)->default_value("/path/to/fpga-image.bit"), "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.") @@ -480,97 +102,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ 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."); - } - print_image_loader_warning(fpga_path, vm); - std::signal(SIGINT, &sig_int_handler); - 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") > 0)); - - 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; + return EXIT_FAILURE; } + |