diff options
author | Ashish Chaudhari <ashish@ettus.com> | 2015-12-15 15:17:36 -0800 |
---|---|---|
committer | Ashish Chaudhari <ashish@ettus.com> | 2015-12-15 15:17:36 -0800 |
commit | f44f7f2ca7dbbd1a72f5b3d239856a1954569884 (patch) | |
tree | 310055d734a28063359b26f7cdc4d4704c517b9e | |
parent | 75ea07bd6d19494e018cd5c3109e1f85969b7ac6 (diff) | |
parent | 10178875a154e58af6c14774621776d13e7e3daa (diff) | |
download | uhd-f44f7f2ca7dbbd1a72f5b3d239856a1954569884.tar.gz uhd-f44f7f2ca7dbbd1a72f5b3d239856a1954569884.tar.bz2 uhd-f44f7f2ca7dbbd1a72f5b3d239856a1954569884.zip |
Merge branch 'maint'
Conflicts:
host/lib/usrp/b200/b200_impl.hpp
host/lib/usrp/e300/e300_fpga_defs.hpp
host/lib/usrp/x300/x300_fw_common.h
30 files changed, 701 insertions, 911 deletions
diff --git a/host/cmake/msvc/erllc_uhd_b205mini.inf b/host/cmake/msvc/erllc_uhd_b205mini.inf new file mode 100644 index 000000000..1e5852656 --- /dev/null +++ b/host/cmake/msvc/erllc_uhd_b205mini.inf @@ -0,0 +1,175 @@ +; ======== libusb 1.0 (WinUSB) device driver ========== +; +; To customize this inf file for your own device +; +; 1. Change "DeviceName" with the name you want your device to appear with +; on your system. +; +; 2. Change "VendorID" and "ProductID" according to those of your device. +; If your device is plugged in, you can retrieve these values through the +; Device Manager (regardless of whether the driver is installed or not). +; +; 3. Change "DeviceGUID" to a value that is unique on your system. For more +; information and tools to help you generate your own GUIDs, see +; http://en.wikipedia.org/wiki/Universally_Unique_Identifier. +; +; 4. Change "DeviceClassGUID" to reflect your USB Device Class. +; The following Device Classes are listed for reference: +; {745a17a0-74d3-11d0-b6fe-00a0c90f57da} : HID device +; {78a1c341-4539-11d3-b88d-00c04fad5171} : Generic WinUSB device +; +; 5. (Optional) Change the "Date" string. +; +; Note 1: if you need to create a matching cat file for this inf, you can use +; the inf2cat utility from the WinDDK, with the the following command: +; inf2cat /driver:"path_to_your inf" /os:7_X86,7_X64,Vista_X86,Vista_X64 +; +; Note 2: The co-installers provided in these files are version 1.9. +; Please refer to: +; http://blogs.msdn.com/iliast/archive/2008/03/10/why-do-we-need-wdf-coinstallers.aspx and +; http://blogs.msdn.com/iliast/archive/2009/08/13/wdf-logo-requirements-regarding-coinstallers.aspx +; for more information about co-installers and their versioning + +; ===================== Strings ======================= + +[Strings] + +; ===================================================== +; ========= START USER CONFIGURABLE SECTION =========== +; ===================================================== + +DeviceName = "Ettus Research B205mini" +; Make sure "VID_" and "PID_" are always part of the strings below +VendorID = "VID_2500" +ProductID = "PID_0022" +DeviceGUID = "{211d89c8-92d7-11e5-be76-3c970eb8a18b}" +DeviceClassGUID = "{78a1c341-4539-11d3-b88d-00c04fad5171}" +; Date MUST be in MM/DD/YYYY format +Date = "11/24/2015" + +; ===================================================== +; ========== END USER CONFIGURABLE SECTION ============ +; ===================================================== + +ProviderName = "libusb 1.0" +WinUSB_SvcDesc = "WinUSB Driver Service" +DiskName = "libusb (WinUSB) Device Install Disk" +ClassName = "libusb (WinUSB) devices" + +; ====================== Version ====================== + +[Version] +DriverVer = %Date% +Signature = "$Windows NT$" +Class = %ClassName% +ClassGuid = %DeviceClassGUID% +Provider = %ProviderName% +CatalogFile = libusb_device.cat + +; =================== Class section =================== + +; Since the device is not a standard USB device, we define a new class for it. +[ClassInstall32] +Addreg = WinUSBDeviceClassReg + +[WinUSBDeviceClassReg] +HKR,,,0,%ClassName% +; -20 is for the USB icon +HKR,,Icon,,-20 + +; =========== Manufacturer/Models sections ============ + +[Manufacturer] +%ProviderName% = libusbDevice_WinUSB,NTx86,NTamd64 + +[libusbDevice_WinUSB.NTx86] +%DeviceName% = USB_Install, USB\%VendorID%&%ProductID% + +[libusbDevice_WinUSB.NTamd64] +%DeviceName% = USB_Install, USB\%VendorID%&%ProductID% + +; ==================== Installation =================== + +; The Include and Needs directives in the USB_Install section are required for +; installing WinUSB on Windows Vista systems. Windows XP systems ignore these +; directives. These directives should not be modified. +[USB_Install] +Include=winusb.inf +Needs=WINUSB.NT + +; The Include directive in the USB_Install.Services section includes the system- +; supplied INF for WinUSB. This INF is installed by the WinUSB co-installer if +; it is not already on the target system. The AddService directive specifies +; WinUsb.sys as the device’s function driver. These directives should not be +; modified. +[USB_Install.Services] +Include=winusb.inf +AddService=WinUSB,0x00000002,WinUSB_ServiceInstall + +; The WinUSB_ServiceInstall section contains the data for installing WinUsb.sys +; as a service. This section should not be modified. +[WinUSB_ServiceInstall] +DisplayName = %WinUSB_SvcDesc% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WinUSB.sys + +; The KmdfService directive installs WinUsb.sys as a kernel-mode service. The +; referenced WinUSB_Install section specifies the KMDF library version. +; Usually, the version can be derived from the WdfCoInstallerxxyyy.dll with +; xx = major, yyy = minor +[USB_Install.Wdf] +KmdfService=WINUSB, WinUsb_Install + +[WinUSB_Install] +KmdfLibraryVersion=1.9 + +; USB_Install.HW is the key section in the INF. It specifies the device +; interface globally unique identifier (GUID) for your device. The AddReg +; directive puts the interface GUID in a standard registry value. When +; WinUsb.sys is loaded as the device’s function driver, it reads the registry +; value and uses the specified GUID to represent the device interface. You +; should replace the GUID in this example with one that you create specifically +; for your device. If the protocols for the device change, you should create a +; new device interface GUID. +[USB_Install.HW] +AddReg=Dev_AddReg + +[Dev_AddReg] +HKR,,DeviceInterfaceGUIDs,0x10000,%DeviceGUID% + +; The USB_Install.CoInstallers section, including the referenced AddReg and +; CopyFiles sections, contains data and instructions to install the WinUSB and +; KMDF co installers and associate them with the device. Most USB devices can +; use these sections and directives without modification. +[USB_Install.CoInstallers] +AddReg=CoInstallers_AddReg +CopyFiles=CoInstallers_CopyFiles + +[CoInstallers_AddReg] +HKR,,CoInstallers32,0x00010000,"WdfCoInstaller01009.dll,WdfCoInstaller","WinUSBCoInstaller2.dll" + +[CoInstallers_CopyFiles] +WinUSBCoInstaller2.dll +WdfCoInstaller01009.dll + +[DestinationDirs] +CoInstallers_CopyFiles=11 + +; =============== Source Media Section ================ + +; The x86 and x64 versions of Windows have separate co installers. This example +; stores them on the installation disk in folders that are named x86 and amd64 +[SourceDisksNames] +1 = %DiskName%,,,\x86 +2 = %DiskName%,,,\amd64 + +[SourceDisksFiles.x86] +WinUSBCoInstaller2.dll=1 +WdfCoInstaller01009.dll=1 + +[SourceDisksFiles.amd64] +WinUSBCoInstaller2.dll=2 +WdfCoInstaller01009.dll=2 + diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp index 4754a6357..207c418fc 100644 --- a/host/lib/usrp/b200/b200_iface.cpp +++ b/host/lib/usrp/b200/b200_iface.cpp @@ -17,6 +17,7 @@ #include "b200_iface.hpp" +#include "../../utils/ihex.hpp" #include <uhd/config.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/log.hpp> @@ -87,7 +88,7 @@ typedef boost::uint32_t hash_type; **********************************************************************/ /*! * Create a file hash - * The hash will be used to identify the loaded firmware and fpga image + * The hash will be used to identify the loaded fpga image * \param filename file used to generate hash value * \return hash value in a uint32_t type */ @@ -125,66 +126,6 @@ static hash_type generate_hash(const char *filename) } -/*! - * Verify checksum of a Intel HEX record - * \param record a line from an Intel HEX file - * \return true if record is valid, false otherwise - */ -bool checksum(const std::string& record) { - - size_t len = record.length(); - unsigned int i; - unsigned char sum = 0; - unsigned int val; - - for (i = 1; i < len; i += 2) { - std::istringstream(record.substr(i, 2)) >> std::hex >> val; - sum += val; - } - - if (sum == 0) - return true; - else - return false; -} - - -/*! - * Parse Intel HEX record - * - * \param record a line from an Intel HEX file - * \param len output length of record - * \param addr output address - * \param type output type - * \param data output data - * \return true if record is sucessfully read, false on error - */ -bool parse_record(const std::string& record, boost::uint16_t &len, \ - boost::uint16_t &addr, boost::uint16_t &type, unsigned char* data) { - - unsigned int i; - std::string _data; - unsigned int val; - - if (record.substr(0, 1) != ":") - return false; - - std::istringstream(record.substr(1, 2)) >> std::hex >> len; - std::istringstream(record.substr(3, 4)) >> std::hex >> addr; - std::istringstream(record.substr(7, 2)) >> std::hex >> type; - - if (len > (2 * (record.length() - 9))) // sanity check to prevent buffer overrun - return false; - - for (i = 0; i < len; i++) { - std::istringstream(record.substr(9 + 2 * i, 2)) >> std::hex >> val; - data[i] = (unsigned char) val; - } - - return true; -} - - /*********************************************************************** * The implementation class **********************************************************************/ @@ -270,109 +211,31 @@ public: void load_firmware(const std::string filestring, UHD_UNUSED(bool force) = false) { - const char *filename = filestring.c_str(); - - /* Fields used in each USB control transfer. */ - boost::uint16_t len = 0; - boost::uint16_t type = 0; - boost::uint16_t lower_address_bits = 0x0000; - unsigned char data[512]; - - /* Can be set by the Intel HEX record 0x04, used for all 0x00 records - * thereafter. Note this field takes the place of the 'index' parameter in - * libusb calls, and is necessary for FX3's 32-bit addressing. */ - boost::uint16_t upper_address_bits = 0x0000; - - std::ifstream file; - file.open(filename, std::ifstream::in); - - if(!file.good()) { - throw uhd::io_error("fx3_load_firmware: cannot open firmware input file"); + if (load_img_msg) + UHD_MSG(status) << "Loading firmware image: " + << filestring << "..." << std::flush; + + ihex_reader file_reader(filestring); + try { + file_reader.read( + boost::bind( + &b200_iface_impl::fx3_control_write, this, + FX3_FIRMWARE_LOAD, _1, _2, _3, _4, 0 + ) + ); + } catch (const uhd::io_error &e) { + throw uhd::io_error(str(boost::format("Could not load firmware: \n%s") % e.what())); } - if (load_img_msg) UHD_MSG(status) << "Loading firmware image: " \ - << filestring << "..." << std::flush; - - while (!file.eof()) { - boost::int32_t ret = 0; - std::string record; - file >> record; - - if (!(record.length() > 0)) - continue; - - /* Check for valid Intel HEX record. */ - if (!checksum(record) || !parse_record(record, len, \ - lower_address_bits, type, data)) { - throw uhd::io_error("fx3_load_firmware: bad intel hex record checksum"); - } - - /* Type 0x00: Data. */ - if (type == 0x00) { - ret = fx3_control_write(FX3_FIRMWARE_LOAD, \ - lower_address_bits, upper_address_bits, data, len); - - if (ret < 0) { - throw uhd::io_error("usrp_load_firmware: usrp_control_write failed"); - } - } - - /* Type 0x01: EOF. */ - else if (type == 0x01) { - if (lower_address_bits != 0x0000 || len != 0 ) { - throw uhd::io_error("fx3_load_firmware: For EOF record, address must be 0, length must be 0."); - } - - //TODO - //usrp_set_firmware_hash(hash); //set hash before reset - - /* Successful termination! */ - file.close(); + UHD_MSG(status) << std::endl; - /* Let the system settle. */ - boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); - return; - } - - /* Type 0x04: Extended Linear Address Record. */ - else if (type == 0x04) { - if (lower_address_bits != 0x0000 || len != 2 ) { - throw uhd::io_error("fx3_load_firmware: For ELA record, address must be 0, length must be 2."); - } - - upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\ - + ((boost::uint16_t)(data[1] & 0x00FF)); - } - - /* Type 0x05: Start Linear Address Record. */ - else if (type == 0x05) { - if (lower_address_bits != 0x0000 || len != 4 ) { - throw uhd::io_error("fx3_load_firmware: For SLA record, address must be 0, length must be 4."); - } - - /* The firmware load is complete. We now need to tell the CPU - * to jump to an execution address start point, now contained within - * the data field. Parse these address bits out, and then push the - * instruction. */ - upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\ - + ((boost::uint16_t)(data[1] & 0x00FF)); - lower_address_bits = ((boost::uint16_t)((data[2] & 0x00FF) << 8))\ - + ((boost::uint16_t)(data[3] & 0x00FF)); - - fx3_control_write(FX3_FIRMWARE_LOAD, lower_address_bits, \ - upper_address_bits, 0, 0); - - if (load_img_msg) UHD_MSG(status) << " done" << std::endl; - } - - /* If we receive an unknown record type, error out. */ - else { - throw uhd::io_error("fx3_load_firmware: unsupported record type."); - } - } + //TODO + //usrp_set_firmware_hash(hash); //set hash before reset - /* There was no valid EOF. */ - throw uhd::io_error("fx3_load_firmware: No EOF record found."); + /* Success! Let the system settle. */ + // TODO: Replace this with a polling loop in the FX3, or find out + // what the actual, correct timeout value is. + boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); } void reset_fx3(void) { diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp index 19ec561fa..e45c78d49 100644 --- a/host/lib/usrp/b200/b200_iface.hpp +++ b/host/lib/usrp/b200/b200_iface.hpp @@ -30,25 +30,28 @@ enum b200_product_t { B200, B210, - B205 + B200MINI, + B205MINI }; // These are actual USB PIDs (not Ettus Product IDs) -const static boost::uint16_t B200_VENDOR_ID = 0x2500; -const static boost::uint16_t B200_VENDOR_NI_ID = 0x3923; -const static boost::uint16_t B200_PRODUCT_ID = 0x0020; -const static boost::uint16_t B205_PRODUCT_ID = 0x0021; -const static boost::uint16_t B200_PRODUCT_NI_ID = 0x7813; -const static boost::uint16_t B210_PRODUCT_NI_ID = 0x7814; -const static boost::uint16_t FX3_VID = 0x04b4; -const static boost::uint16_t FX3_DEFAULT_PID = 0x00f3; -const static boost::uint16_t FX3_REENUM_PID = 0x00f0; +const static boost::uint16_t B200_VENDOR_ID = 0x2500; +const static boost::uint16_t B200_VENDOR_NI_ID = 0x3923; +const static boost::uint16_t B200_PRODUCT_ID = 0x0020; +const static boost::uint16_t B200MINI_PRODUCT_ID = 0x0021; +const static boost::uint16_t B205MINI_PRODUCT_ID = 0x0022; +const static boost::uint16_t B200_PRODUCT_NI_ID = 0x7813; +const static boost::uint16_t B210_PRODUCT_NI_ID = 0x7814; +const static boost::uint16_t FX3_VID = 0x04b4; +const static boost::uint16_t FX3_DEFAULT_PID = 0x00f3; +const static boost::uint16_t FX3_REENUM_PID = 0x00f0; //! Map the USB PID to the product (only for PIDs that map to a single product) static const uhd::dict<boost::uint16_t, b200_product_t> B2XX_PID_TO_PRODUCT = boost::assign::map_list_of - (B200_PRODUCT_NI_ID, B200) - (B210_PRODUCT_NI_ID, B210) - (B205_PRODUCT_ID, B205) + (B200_PRODUCT_NI_ID, B200) + (B210_PRODUCT_NI_ID, B210) + (B200MINI_PRODUCT_ID, B200MINI) + (B205MINI_PRODUCT_ID, B205MINI) ; static const std::string B200_FW_FILE_NAME = "usrp_b200_fw.hex"; @@ -61,21 +64,25 @@ static const uhd::dict<boost::uint16_t, b200_product_t> B2XX_PRODUCT_ID = boost: (0x0002, B210) (0x7738, B210) (B210_PRODUCT_NI_ID, B210) - (0x0003, B205) - (0x7739, B205) + (0x0003, B200MINI) + (0x7739, B200MINI) + (0x0004, B205MINI) + (0x773a, B205MINI) ; static const uhd::dict<b200_product_t, std::string> B2XX_STR_NAMES = boost::assign::map_list_of - (B200, "B200") - (B210, "B210") - (B205, "B200mini") + (B200, "B200") + (B210, "B210") + (B200MINI, "B200mini") + (B205MINI, "B205mini") ; static const uhd::dict<b200_product_t, std::string> B2XX_FPGA_FILE_NAME = boost::assign::map_list_of (B200, "usrp_b200_fpga.bin") (B210, "usrp_b210_fpga.bin") - (B205, "usrp_b200mini_fpga.bin") + (B200MINI, "usrp_b200mini_fpga.bin") + (B205MINI, "usrp_b205mini_fpga.bin") ; diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index 3aaf28ab3..62690f09f 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -75,9 +75,9 @@ public: }; // B205 -class b205_ad9361_client_t : public ad9361_params { +class b2xxmini_ad9361_client_t : public ad9361_params { public: - ~b205_ad9361_client_t() {} + ~b2xxmini_ad9361_client_t() {} double get_band_edge(frequency_band_t band) { switch (band) { case AD9361_RX_BAND0: return 0; // Set these all to @@ -319,7 +319,8 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s else if (specified_vid) { vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid, B200_PRODUCT_ID)); - vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid, B205_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid, B200MINI_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid, B205MINI_PRODUCT_ID)); vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid, B200_PRODUCT_NI_ID)); vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid, B210_PRODUCT_NI_ID)); } @@ -333,7 +334,8 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s else { vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200_PRODUCT_ID)); - vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B205_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200MINI_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B205MINI_PRODUCT_ID)); vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID)); vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID)); } @@ -387,7 +389,7 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s UHD_MSG(status) << "Detected Device: " << B2XX_STR_NAMES[_product] << std::endl; - _gpsdo_capable = (_product != B205); + _gpsdo_capable = (not (_product == B200MINI or _product == B205MINI)); //////////////////////////////////////////////////////////////////// // Set up frontend mapping @@ -406,7 +408,7 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s _fe2 = 0; _gpio_state.swap_atr = 1; // Unswapped setup: - if (_product == B205 or (_product == B200 and _revision >= 5)) { + if (_product == B200MINI or _product == B205MINI or (_product == B200 and _revision >= 5)) { _fe1 = 0; //map radio0 to FE1 _fe2 = 1; //map radio1 to FE2 _gpio_state.swap_atr = 0; // ATRs for radio0 are mapped to FE1 @@ -513,7 +515,7 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s //////////////////////////////////////////////////////////////////// _tree->create<std::string>("/name").set("B-Series Device"); _tree->create<std::string>(mb_path / "name").set(product_name); - _tree->create<std::string>(mb_path / "codename").set((_product == B205) ? "Pixie" : "Sasquatch"); + _tree->create<std::string>(mb_path / "codename").set((_product == B200MINI or _product == B205MINI) ? "Pixie" : "Sasquatch"); //////////////////////////////////////////////////////////////////// // Create data transport @@ -541,7 +543,7 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s // create time and clock control objects //////////////////////////////////////////////////////////////////// _spi_iface = b200_local_spi_core::make(_local_ctrl); - if (_product != B205) { + if (not (_product == B200MINI or _product == B205MINI)) { _adf4001_iface = boost::make_shared<b200_ref_pll_ctrl>(_spi_iface); } @@ -550,13 +552,13 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s //////////////////////////////////////////////////////////////////// UHD_MSG(status) << "Initialize CODEC control..." << std::endl; ad9361_params::sptr client_settings; - if (_product == B205) { - client_settings = boost::make_shared<b205_ad9361_client_t>(); + if (_product == B200MINI or _product == B205MINI) { + client_settings = boost::make_shared<b2xxmini_ad9361_client_t>(); } else { client_settings = boost::make_shared<b200_ad9361_client_t>(); } _codec_ctrl = ad9361_ctrl::make_spi(client_settings, _spi_iface, AD9361_SLAVENO); - + //////////////////////////////////////////////////////////////////// // create codec control objects //////////////////////////////////////////////////////////////////// @@ -624,15 +626,18 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s //register time now and pps onto available radio cores _tree->create<time_spec_t>(mb_path / "time" / "now") - .publish(boost::bind(&time_core_3000::get_time_now, _radio_perifs[0].time64)); + .publish(boost::bind(&time_core_3000::get_time_now, _radio_perifs[0].time64)) + .subscribe(boost::bind(&b200_impl::set_time, this, _1)) + .set(0.0); + //re-sync the times when the tick rate changes + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&b200_impl::sync_times, this)); _tree->create<time_spec_t>(mb_path / "time" / "pps") .publish(boost::bind(&time_core_3000::get_time_last_pps, _radio_perifs[0].time64)); - for (size_t i = 0; i < _radio_perifs.size(); i++) + BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) { - _tree->access<time_spec_t>(mb_path / "time" / "now") - .subscribe(boost::bind(&time_core_3000::set_time_now, _radio_perifs[i].time64, _1)); _tree->access<time_spec_t>(mb_path / "time" / "pps") - .subscribe(boost::bind(&time_core_3000::set_time_next_pps, _radio_perifs[i].time64, _1)); + .subscribe(boost::bind(&time_core_3000::set_time_next_pps, perif.time64, _1)); } //setup time source props @@ -945,7 +950,7 @@ void b200_impl::check_fpga_compat(void) if (signature != 0xACE0BA5E) throw uhd::runtime_error( "b200::check_fpga_compat signature register readback failed"); - const boost::uint16_t expected = (_product == B205 ? B205_FPGA_COMPAT_NUM : B200_FPGA_COMPAT_NUM); + const boost::uint16_t expected = ((_product == B200MINI or _product == B205MINI) ? B205_FPGA_COMPAT_NUM : B200_FPGA_COMPAT_NUM); if (compat_major != expected) { throw uhd::runtime_error(str(boost::format( @@ -970,7 +975,7 @@ void b200_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom) void b200_impl::update_clock_source(const std::string &source) { // For B205, ref_sel selects whether or not to lock to the external clock source - if (_product == B205) + if (_product == B200MINI or _product == B205MINI) { if (source == "external" and _time_source == EXTERNAL) { @@ -1032,7 +1037,7 @@ void b200_impl::update_clock_source(const std::string &source) void b200_impl::update_time_source(const std::string &source) { - if (_product == B205 and source == "external" and _gpio_state.ref_sel == 1) + if ((_product == B200MINI or _product == B205MINI) and source == "external" and _gpio_state.ref_sel == 1) { throw uhd::value_error("external reference cannot be both a time source and a clock source"); } @@ -1052,11 +1057,24 @@ void b200_impl::update_time_source(const std::string &source) throw uhd::key_error("update_time_source: unknown source: " + source); if (_time_source != value) { - _local_ctrl->poke32(TOREG(SR_CORE_PPS_SEL), value); + _local_ctrl->poke32(TOREG(SR_CORE_SYNC), value); _time_source = value; } } +void b200_impl::set_time(const uhd::time_spec_t& t) +{ + BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) + perif.time64->set_time_sync(t); + _local_ctrl->poke32(TOREG(SR_CORE_SYNC), 1 << 2 | boost::uint32_t(_time_source)); + _local_ctrl->poke32(TOREG(SR_CORE_SYNC), _time_source); +} + +void b200_impl::sync_times() +{ + set_time(_radio_perifs[0].time64->get_time_now()); +} + /*********************************************************************** * GPIO setup **********************************************************************/ @@ -1064,7 +1082,7 @@ void b200_impl::update_time_source(const std::string &source) void b200_impl::update_bandsel(const std::string& which, double freq) { // B205 does not have bandsels - if (_product == B205) { + if (_product == B200MINI or _product == B205MINI) { return; } diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index b406572fb..08ae68e9a 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -49,8 +49,8 @@ #include "recv_packet_demuxer_3000.hpp" static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 8; static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0; -static const boost::uint16_t B200_FPGA_COMPAT_NUM = 12; -static const boost::uint16_t B205_FPGA_COMPAT_NUM = 3; +static const boost::uint16_t B200_FPGA_COMPAT_NUM = 13; +static const boost::uint16_t B205_FPGA_COMPAT_NUM = 4; static const double B200_BUS_CLOCK_RATE = 100e6; static const boost::uint32_t B200_GPSDO_ST_NONE = 0x83; static const size_t B200_MAX_RATE_USB2 = 53248000; // bytes/s @@ -95,7 +95,8 @@ static const unsigned char B200_USB_DATA_SEND_ENDPOINT = 2; static std::vector<uhd::transport::usb_device_handle::vid_pid_pair_t> b200_vid_pid_pairs = boost::assign::list_of (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200_PRODUCT_ID)) - (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B205_PRODUCT_ID)) + (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200MINI_PRODUCT_ID)) + (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B205MINI_PRODUCT_ID)) (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID)) (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID)) ; @@ -167,6 +168,8 @@ private: uhd::usrp::subdev_spec_t coerce_subdev_spec(const uhd::usrp::subdev_spec_t &); void update_subdev_spec(const std::string &tx_rx, const uhd::usrp::subdev_spec_t &); void update_time_source(const std::string &); + void set_time(const uhd::time_spec_t&); + void sync_times(void); void update_clock_source(const std::string &); void update_bandsel(const std::string& which, double freq); void update_antenna_sel(const size_t which, const std::string &ant); diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index 41b4b8a74..279901208 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -227,7 +227,8 @@ uhd::usrp::subdev_spec_t b200_impl::coerce_subdev_spec(const uhd::usrp::subdev_s // // Any other spec is probably illegal and will be caught by // validate_subdev_spec(). - if (spec.size() and (_product == B200 or _product == B205) and spec[0].sd_name == "B") { + if (spec.size() and (_product == B200 or _product == B200MINI or _product == B205MINI) and spec[0].sd_name == "B") + { spec[0].sd_name = "A"; } return spec; diff --git a/host/lib/usrp/b200/b200_regs.hpp b/host/lib/usrp/b200/b200_regs.hpp index 8f2dd03f3..e9ab81bae 100644 --- a/host/lib/usrp/b200/b200_regs.hpp +++ b/host/lib/usrp/b200/b200_regs.hpp @@ -28,7 +28,7 @@ localparam SR_CORE_SPI = 8; localparam SR_CORE_MISC = 16; localparam SR_CORE_COMPAT = 24; localparam SR_CORE_GPSDO_ST = 40; -localparam SR_CORE_PPS_SEL = 48; +localparam SR_CORE_SYNC = 48; localparam RB32_CORE_SPI = 8; localparam RB32_CORE_MISC = 16; localparam RB32_CORE_STATUS = 20; diff --git a/host/lib/usrp/common/adf4001_ctrl.cpp b/host/lib/usrp/common/adf4001_ctrl.cpp index a7510c272..001b68b7a 100644 --- a/host/lib/usrp/common/adf4001_ctrl.cpp +++ b/host/lib/usrp/common/adf4001_ctrl.cpp @@ -81,7 +81,7 @@ boost::uint32_t adf4001_regs_t::get_reg(boost::uint8_t addr) { reg |= (boost::uint32_t(timer_counter_control) & 0x00000F) << 11; reg |= (boost::uint32_t(charge_pump_current_1) & 0x000007) << 15; reg |= (boost::uint32_t(charge_pump_current_2) & 0x000007) << 18; - reg |= (boost::uint32_t(power_down) & 0x000002) << 21; + reg |= (boost::uint32_t(power_down) & 0x000002) << 20; break; default: break; @@ -128,15 +128,15 @@ void adf4001_ctrl::set_lock_to_ext_ref(bool external) { void adf4001_ctrl::program_regs(void) { //no control over CE, only LE, therefore we use the initialization latch method write_reg(3); - boost::this_thread::sleep(boost::posix_time::microseconds(1)); + + //conduct a function latch (2) + write_reg(2); //write R counter latch (0) write_reg(0); - boost::this_thread::sleep(boost::posix_time::microseconds(1)); //write N counter latch (1) write_reg(1); - boost::this_thread::sleep(boost::posix_time::microseconds(1)); } diff --git a/host/lib/usrp/cores/time_core_3000.cpp b/host/lib/usrp/cores/time_core_3000.cpp index ffae5dc0d..694edf31c 100644 --- a/host/lib/usrp/cores/time_core_3000.cpp +++ b/host/lib/usrp/cores/time_core_3000.cpp @@ -24,8 +24,9 @@ #define REG_TIME_LO _base + 4 #define REG_TIME_CTRL _base + 8 -#define CTRL_LATCH_TIME_PPS (1 << 1) -#define CTRL_LATCH_TIME_NOW (1 << 0) +#define CTRL_LATCH_TIME_NOW (1 << 0) +#define CTRL_LATCH_TIME_PPS (1 << 1) +#define CTRL_LATCH_TIME_SYNC (1 << 2) using namespace uhd; @@ -99,6 +100,14 @@ struct time_core_3000_impl : time_core_3000 _iface->poke32(REG_TIME_CTRL, CTRL_LATCH_TIME_NOW); } + void set_time_sync(const uhd::time_spec_t &time) + { + const boost::uint64_t ticks = time.to_ticks(_tick_rate); + _iface->poke32(REG_TIME_HI, boost::uint32_t(ticks >> 32)); + _iface->poke32(REG_TIME_LO, boost::uint32_t(ticks >> 0)); + _iface->poke32(REG_TIME_CTRL, CTRL_LATCH_TIME_SYNC); + } + void set_time_next_pps(const uhd::time_spec_t &time) { const boost::uint64_t ticks = time.to_ticks(_tick_rate); diff --git a/host/lib/usrp/cores/time_core_3000.hpp b/host/lib/usrp/cores/time_core_3000.hpp index 7463386ba..7dde4e797 100644 --- a/host/lib/usrp/cores/time_core_3000.hpp +++ b/host/lib/usrp/cores/time_core_3000.hpp @@ -53,6 +53,8 @@ public: virtual void set_time_now(const uhd::time_spec_t &time) = 0; + virtual void set_time_sync(const uhd::time_spec_t &time) = 0; + virtual void set_time_next_pps(const uhd::time_spec_t &time) = 0; }; diff --git a/host/lib/usrp/e300/e300_fpga_defs.hpp b/host/lib/usrp/e300/e300_fpga_defs.hpp index dcfb05021..594461518 100644 --- a/host/lib/usrp/e300/e300_fpga_defs.hpp +++ b/host/lib/usrp/e300/e300_fpga_defs.hpp @@ -21,7 +21,7 @@ namespace uhd { namespace usrp { namespace e300 { namespace fpga { static const size_t NUM_RADIOS = 2; -static const boost::uint32_t COMPAT_MAJOR = 13; +static const boost::uint32_t COMPAT_MAJOR = 14; static const boost::uint32_t COMPAT_MINOR = 0; }}}} // namespace diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp index c82ab3d0e..3242a7fe8 100644 --- a/host/lib/usrp/e300/e300_impl.cpp +++ b/host/lib/usrp/e300/e300_impl.cpp @@ -175,7 +175,7 @@ device_addrs_t e300_find(const device_addr_t &multi_dev_hint) device_addr_t new_hint = hint; new_hint["addr"] = if_addrs.bcast; - // call discover with the new hint ad append results + // call discover with the new hint and append results device_addrs_t new_e300_addrs = e300_find(new_hint); e300_addrs.insert(e300_addrs.begin(), new_e300_addrs.begin(), new_e300_addrs.end()); @@ -529,8 +529,11 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) //////////////////////////////////////////////////////////////////// _tree->create<time_spec_t>(mb_path / "time" / "now") .publish(boost::bind(&time_core_3000::get_time_now, _radio_perifs[0].time64)) - .subscribe(boost::bind(&time_core_3000::set_time_now, _radio_perifs[0].time64, _1)) - .subscribe(boost::bind(&time_core_3000::set_time_now, _radio_perifs[1].time64, _1)); + .subscribe(boost::bind(&e300_impl::_set_time, this, _1)) + .set(0.0); + //re-sync the times when the tick rate changes + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&e300_impl::_sync_times, this)); _tree->create<time_spec_t>(mb_path / "time" / "pps") .publish(boost::bind(&time_core_3000::get_time_last_pps, _radio_perifs[0].time64)) .subscribe(boost::bind(&time_core_3000::set_time_next_pps, _radio_perifs[0].time64, _1)) @@ -796,6 +799,21 @@ void e300_impl::_update_time_source(const std::string &source) _update_gpio_state(); } +void e300_impl::_set_time(const uhd::time_spec_t& t) +{ + BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) + perif.time64->set_time_sync(t); + _misc.time_sync = 1; + _update_gpio_state(); + _misc.time_sync = 0; + _update_gpio_state(); +} + +void e300_impl::_sync_times() +{ + _set_time(_radio_perifs[0].time64->get_time_now()); +} + size_t e300_impl::_get_axi_dma_channel( boost::uint8_t destination, boost::uint8_t prefix) @@ -1081,7 +1099,8 @@ void e300_impl::_update_gpio_state(void) | (_misc.tx_bandsels << gpio_t::TX_BANDSEL) | (_misc.rx_bandsel_a << gpio_t::RX_BANDSELA) | (_misc.rx_bandsel_b << gpio_t::RX_BANDSELB) - | (_misc.rx_bandsel_c << gpio_t::RX_BANDSELC); + | (_misc.rx_bandsel_c << gpio_t::RX_BANDSELC) + | (_misc.time_sync << gpio_t::TIME_SYNC); _global_regs->poke32(global_regs::SR_CORE_MISC, misc_reg); } diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp index 9b39c7468..1bf5cb950 100644 --- a/host/lib/usrp/e300/e300_impl.hpp +++ b/host/lib/usrp/e300/e300_impl.hpp @@ -200,6 +200,8 @@ private: // types boost::uint32_t rx_bandsel_b; boost::uint32_t rx_bandsel_c; + boost::uint32_t time_sync; + static const size_t PPS_SEL = 0; static const size_t MIMO = 2; static const size_t CODEC_ARST = 3; @@ -207,6 +209,7 @@ private: // types static const size_t RX_BANDSELA = 7; static const size_t RX_BANDSELB = 13; static const size_t RX_BANDSELC = 17; + static const size_t TIME_SYNC = 21; }; private: // methods @@ -257,6 +260,8 @@ private: // methods void _update_time_source(const std::string &source); void _update_clock_source(const std::string &); + void _set_time(const uhd::time_spec_t&); + void _sync_times(void); void _update_subdev_spec( const std::string &txrx, diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h index 4c5051eaa..549fc9dfa 100644 --- a/host/lib/usrp/x300/x300_fw_common.h +++ b/host/lib/usrp/x300/x300_fw_common.h @@ -33,7 +33,7 @@ extern "C" { #define X300_REVISION_MIN 2 #define X300_FW_COMPAT_MAJOR 4 #define X300_FW_COMPAT_MINOR 0 -#define X300_FPGA_COMPAT_MAJOR 18 +#define X300_FPGA_COMPAT_MAJOR 19 //shared memory sections - in between the stack and the program space #define X300_FW_SHMEM_BASE 0x6000 diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index 0483ecf11..37461e2e5 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -809,8 +809,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) //////////////////////////////////////////////////////////////////// _tree->create<time_spec_t>(mb_path / "time" / "now") .publish(boost::bind(&time_core_3000::get_time_now, mb.radio_perifs[0].time64)) - .subscribe(boost::bind(&time_core_3000::set_time_now, mb.radio_perifs[0].time64, _1)) - .subscribe(boost::bind(&time_core_3000::set_time_now, mb.radio_perifs[1].time64, _1)); + .subscribe(boost::bind(&x300_impl::sync_times, this, mb, _1)) + .set(0.0); _tree->create<time_spec_t>(mb_path / "time" / "pps") .publish(boost::bind(&time_core_3000::get_time_last_pps, mb.radio_perifs[0].time64)) .subscribe(boost::bind(&time_core_3000::set_time_next_pps, mb.radio_perifs[0].time64, _1)) @@ -1505,6 +1505,14 @@ void x300_impl::update_time_source(mboard_members_t &mb, const std::string &sour */ } +void x300_impl::sync_times(mboard_members_t &mb, const uhd::time_spec_t& t) +{ + BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs) + perif.time64->set_time_sync(t); + mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::TIME_SYNC, 1); + mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::TIME_SYNC, 0); +} + bool x300_impl::wait_for_clk_locked(mboard_members_t& mb, boost::uint32_t which, double timeout) { boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::milliseconds(timeout * 1000.0); diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 521ce8a0e..4de0344bf 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -362,6 +362,7 @@ private: void set_time_source_out(mboard_members_t&, const bool); void update_clock_source(mboard_members_t&, const std::string &); void update_time_source(mboard_members_t&, const std::string &); + void sync_times(mboard_members_t&, const uhd::time_spec_t&); uhd::sensor_value_t get_ref_locked(mboard_members_t& mb); bool wait_for_clk_locked(mboard_members_t& mb, boost::uint32_t which, double timeout); diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp index 489d249ba..de3a3161a 100644 --- a/host/lib/usrp/x300/x300_regs.hpp +++ b/host/lib/usrp/x300/x300_regs.hpp @@ -213,6 +213,7 @@ namespace uhd { namespace usrp { namespace x300 { UHD_DEFINE_SOFT_REG_FIELD(PPS_OUT_EN, /*width*/ 1, /*shift*/ 4); //[4] UHD_DEFINE_SOFT_REG_FIELD(TCXO_EN, /*width*/ 1, /*shift*/ 5); //[5] UHD_DEFINE_SOFT_REG_FIELD(GPSDO_PWR_EN, /*width*/ 1, /*shift*/ 6); //[6] + UHD_DEFINE_SOFT_REG_FIELD(TIME_SYNC, /*width*/ 1, /*shift*/ 7); //[7] static const boost::uint32_t SRC_EXTERNAL = 0x0; static const boost::uint32_t SRC_INTERNAL = 0x2; @@ -225,6 +226,7 @@ namespace uhd { namespace usrp { namespace x300 { set(PPS_OUT_EN, 0); set(TCXO_EN, 1); set(GPSDO_PWR_EN, 1); //GPSDO power always ON + set(TIME_SYNC, 0); } } clock_ctrl_reg; diff --git a/host/lib/usrp_clock/octoclock/CMakeLists.txt b/host/lib/usrp_clock/octoclock/CMakeLists.txt index d3728344e..d2b70e356 100644 --- a/host/lib/usrp_clock/octoclock/CMakeLists.txt +++ b/host/lib/usrp_clock/octoclock/CMakeLists.txt @@ -19,10 +19,7 @@ # Conditionally configure the OctoClock support ######################################################################## IF(ENABLE_OCTOCLOCK) - ADD_DEFINITIONS(-DIHEX_USE_STDBOOL) - LIBUHD_APPEND_SOURCES( - ${CMAKE_CURRENT_SOURCE_DIR}/kk_ihex_read.c ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_eeprom.cpp ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_image_loader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_impl.cpp diff --git a/host/lib/usrp_clock/octoclock/kk_ihex.h b/host/lib/usrp_clock/octoclock/kk_ihex.h deleted file mode 100644 index 20eba43cc..000000000 --- a/host/lib/usrp_clock/octoclock/kk_ihex.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * kk_ihex.h: A simple library for reading and writing the Intel HEX - * or IHEX format. Intended mainly for embedded systems, and thus - * somewhat optimised for size at the expense of error handling and - * generality. - * - * USAGE - * ----- - * - * The library has been split into read and write parts, which use a - * common data structure (`struct ihex_state`), but each can be used - * independently. Include the header `kk_ihex_read.h` for reading, and/or - * the header `kk_ihex_write.h` for writing (and link with their respective - * object files). Both can be used simultaneously - this header defines - * the shared data structures and definitions. - * - * - * READING INTEL HEX DATA - * ---------------------- - * - * To read data in the Intel HEX format, you must perform the actual reading - * of bytes using other means (e.g., stdio). The bytes read must then be - * passed to `ihex_read_byte` and/or `ihex_read_bytes`. The reading functions - * will then call `ihex_data_read`, at which stage the `struct ihex_state` - * structure will contain the data along with its address. See the header - * `kk_ihex_read.h` for details and example implementation of `ihex_data_read`. - * - * The sequence to read data in IHEX format is: - * struct ihex_state ihex; - * ihex_begin_read(&ihex); - * ihex_read_bytes(&ihex, my_input_bytes, length_of_my_input_bytes); - * ihex_end_read(&ihex); - * - * - * WRITING BINARY DATA AS INTEL HEX - * -------------------------------- - * - * In order to write out data, the `ihex_write_at_address` or - * `ihex_write_at_segment` functions are used to set the data location, - * and then the binary bytes are written with `ihex_write_byte` and/or - * `ihex_write_bytes`. The writing functions will then call the function - * `ihex_flush_buffer` whenever the internal write buffer needs to be - * cleared - it is up to the caller to provide an implementation of - * `ihex_flush_buffer` to do the actual writing. See the header - * `kk_ihex_write.h` for details and an example implementation. - * - * See the declaration further down for an example implementation. - * - * The sequence to write data in IHEX format is: - * struct ihex_state ihex; - * ihex_init(&ihex); - * ihex_write_at_address(&ihex, 0); - * ihex_write_bytes(&ihex, my_data, length_of_my_data); - * ihex_end_write(&ihex); - * - * For outputs larger than 64KiB, 32-bit linear addresses are output. Normally - * the initial linear extended address record of zero is NOT written - it can - * be forced by setting `ihex->flags |= IHEX_FLAG_ADDRESS_OVERFLOW` before - * writing the first byte. - * - * Gaps in the data may be created by calling `ihex_write_at_address` with the - * new starting address without calling `ihex_end_write` in between. - * - * - * The same `struct ihex_state` may be used either for reading or writing, - * but NOT both at the same time. Furthermore, a global output buffer is - * used for writing, i.e., multiple threads must not write simultaneously - * (but multiple writes may be interleaved). - * - * - * CONSERVING MEMORY - * ----------------- - * - * For memory-critical use, you can save additional memory by defining - * `IHEX_LINE_MAX_LENGTH` as something less than 255. Note, however, that - * this limit affects both reading and writing, so the resulting library - * will be unable to read lines with more than this number of data bytes. - * That said, I haven't encountered any IHEX files with more than 32 - * data bytes per line. For write only there is no reason to define the - * maximum as greater than the line length you'll actually be writing, - * e.g., 32 or 16. - * - * If the write functionality is only occasionally used, you can provide - * your own buffer for the duration by defining `IHEX_EXTERNAL_WRITE_BUFFER` - * and providing a `char *ihex_write_buffer` which points to valid storage - * for at least `IHEX_WRITE_BUFFER_LENGTH` characters from before the first - * call to any IHEX write function to until after the last. - * - * If you are doing both reading and writing, you can define the maximum - * output length separately as `IHEX_MAX_OUTPUT_LINE_LENGTH` - this will - * decrease the write buffer size, but `struct ihex_state` will still - * use the larger `IHEX_LINE_MAX_LENGTH` for its data storage. - * - * You can also save a few additional bytes by disabling support for - * segmented addresses, by defining `IHEX_DISABLE_SEGMENTS`. Both the - * read and write modules need to be build with the same option, as the - * resulting data structures will not be compatible otherwise. To be honest, - * this is a fairly pointless optimisation. - * - * - * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/ - * Provided with absolutely no warranty, use at your own risk only. - * Use and distribute freely, mark modified copies as such. - */ - -#ifndef KK_IHEX_H -#define KK_IHEX_H - -#define KK_IHEX_VERSION "2015-08-10" - -#include <stdint.h> - -#ifdef IHEX_USE_STDBOOL -#include <stdbool.h> -typedef bool ihex_bool_t; -#else -typedef uint_fast8_t ihex_bool_t; -#endif - -typedef uint_least32_t ihex_address_t; -typedef uint_least16_t ihex_segment_t; -typedef int ihex_count_t; - -// Maximum number of data bytes per line (applies to both reading and -// writing!); specify 255 to support reading all possible lengths. Less -// can be used to limit memory footprint on embedded systems, e.g., -// most programs with IHEX output use 32. -#ifndef IHEX_LINE_MAX_LENGTH -#define IHEX_LINE_MAX_LENGTH 255 -#endif - -enum ihex_flags { - IHEX_FLAG_ADDRESS_OVERFLOW = 0x80 // 16-bit address overflow -}; -typedef uint8_t ihex_flags_t; - -typedef struct ihex_state { - ihex_address_t address; -#ifndef IHEX_DISABLE_SEGMENTS - ihex_segment_t segment; -#endif - ihex_flags_t flags; - uint8_t line_length; - uint8_t length; - uint8_t data[IHEX_LINE_MAX_LENGTH + 1]; -} kk_ihex_t; - -enum ihex_record_type { - IHEX_DATA_RECORD, - IHEX_END_OF_FILE_RECORD, - IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD, - IHEX_START_SEGMENT_ADDRESS_RECORD, - IHEX_EXTENDED_LINEAR_ADDRESS_RECORD, - IHEX_START_LINEAR_ADDRESS_RECORD -}; -typedef uint8_t ihex_record_type_t; - -#ifndef IHEX_DISABLE_SEGMENTS - -// Resolve segmented address (if any). It is the author's recommendation that -// segmented addressing not be used (and indeed the write function of this -// library uses linear 32-bit addressing unless manually overridden). -// -#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address + (((ihex_address_t)((ihex)->segment)) << 4)) -// -// Note that segmented addressing with the above macro is not strictly adherent -// to the IHEX specification, which mandates that the lowest 16 bits of the -// address and the index of the data byte must be added modulo 64K (i.e., -// at 16 bits precision with wraparound) and the segment address only added -// afterwards. -// -// To implement fully correct segmented addressing, compute the address -// of _each byte_ with its index in `data` as follows: -// -#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((((ihex)->address + (byte_index)) & 0xFFFFU) + (((ihex_address_t)((ihex)->segment)) << 4)) - -#else // IHEX_DISABLE_SEGMENTS: - -#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address) -#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((ihex)->address + (byte_index)) - -#endif - -// The newline string (appended to every output line, e.g., "\r\n") -#ifndef IHEX_NEWLINE_STRING -#define IHEX_NEWLINE_STRING "\n" -#endif - -// See kk_ihex_read.h and kk_ihex_write.h for function declarations! - -#endif // !KK_IHEX_H diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_license.txt b/host/lib/usrp_clock/octoclock/kk_ihex_license.txt deleted file mode 100644 index 530f413e3..000000000 --- a/host/lib/usrp_clock/octoclock/kk_ihex_license.txt +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013-2015 Kimmo Kulovesi - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_read.c b/host/lib/usrp_clock/octoclock/kk_ihex_read.c deleted file mode 100644 index 964cdd165..000000000 --- a/host/lib/usrp_clock/octoclock/kk_ihex_read.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * kk_ihex_read.c: A simple library for reading the Intel HEX (IHEX) format. - * - * See the header `kk_ihex.h` for instructions. - * - * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/ - * Provided with absolutely no warranty, use at your own risk only. - * Use and distribute freely, mark modified copies as such. - * - * Modifications Copyright (c) 2015 National Instruments Corp. - */ - -#include "kk_ihex_read.h" - -#include <stdio.h> -#include <stdlib.h> - -#define IHEX_START ':' - -#define AUTODETECT_ADDRESS (~0UL) - -#define ADDRESS_HIGH_MASK ((ihex_address_t) 0xFFFF0000U) - -enum ihex_read_state { - READ_WAIT_FOR_START = 0, - READ_COUNT_HIGH = 1, - READ_COUNT_LOW, - READ_ADDRESS_MSB_HIGH, - READ_ADDRESS_MSB_LOW, - READ_ADDRESS_LSB_HIGH, - READ_ADDRESS_LSB_LOW, - READ_RECORD_TYPE_HIGH, - READ_RECORD_TYPE_LOW, - READ_DATA_HIGH, - READ_DATA_LOW -}; - -#define IHEX_READ_RECORD_TYPE_MASK 0x07 -#define IHEX_READ_STATE_MASK 0x78 -#define IHEX_READ_STATE_OFFSET 3 - -void -ihex_begin_read (struct ihex_state * const ihex) { - ihex->address = 0; -#ifndef IHEX_DISABLE_SEGMENTS - ihex->segment = 0; -#endif - ihex->flags = 0; - ihex->line_length = 0; - ihex->length = 0; -} - -void -ihex_read_at_address (struct ihex_state * const ihex, ihex_address_t address) { - ihex_begin_read(ihex); - ihex->address = address; -} - -#ifndef IHEX_DISABLE_SEGMENTS -void -ihex_read_at_segment (struct ihex_state * const ihex, ihex_segment_t segment) { - ihex_begin_read(ihex); - ihex->segment = segment; -} -#endif - -void -ihex_end_read (struct ihex_state * const ihex, FILE* outfile) { - uint_fast8_t type = ihex->flags & IHEX_READ_RECORD_TYPE_MASK; - uint_fast8_t sum; - if ((sum = ihex->length) == 0 && type == IHEX_DATA_RECORD) { - return; - } - { - // compute and validate checksum - const uint8_t * const eptr = ihex->data + sum; - const uint8_t *r = ihex->data; - sum += type + (ihex->address & 0xFFU) + ((ihex->address >> 8) & 0xFFU); - while (r != eptr) { - sum += *r++; - } - sum = (~sum + 1U) ^ *eptr; // *eptr is the received checksum - } - if (ihex_data_read(ihex, type, sum, outfile)) { - if (type == IHEX_EXTENDED_LINEAR_ADDRESS_RECORD) { - ihex->address &= 0xFFFFU; - ihex->address |= (((ihex_address_t) ihex->data[0]) << 24) | - (((ihex_address_t) ihex->data[1]) << 16); -#ifndef IHEX_DISABLE_SEGMENTS - } else if (type == IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD) { - ihex->segment = (ihex_segment_t) ((ihex->data[0] << 8) | ihex->data[1]); -#endif - } - } - ihex->length = 0; - ihex->flags = 0; -} - -void -ihex_read_byte (struct ihex_state * const ihex, const char byte, FILE* outfile) { - uint_fast8_t b = (uint_fast8_t) byte; - uint_fast8_t len = ihex->length; - uint_fast8_t state = (ihex->flags & IHEX_READ_STATE_MASK); - ihex->flags ^= state; // turn off the old state - state >>= IHEX_READ_STATE_OFFSET; - - if (b >= '0' && b <= '9') { - b -= '0'; - } else if (b >= 'A' && b <= 'F') { - b -= 'A' - 10; - } else if (b >= 'a' && b <= 'f') { - b -= 'a' - 10; - } else if (b == IHEX_START) { - // sync to a new record at any state - state = READ_COUNT_HIGH; - goto end_read; - } else { - // ignore unknown characters (e.g., extra whitespace) - goto save_read_state; - } - - if (!(++state & 1)) { - // high nybble, store temporarily at end of data: - b <<= 4; - ihex->data[len] = b; - } else { - // low nybble, combine with stored high nybble: - b = (ihex->data[len] |= b); - switch (state >> 1) { - default: - // remain in initial state while waiting for : - return; - case (READ_COUNT_LOW >> 1): - // data length - ihex->line_length = b; -#if IHEX_LINE_MAX_LENGTH < 255 - if (b > IHEX_LINE_MAX_LENGTH) { - ihex_end_read(ihex); - return; - } -#endif - break; - case (READ_ADDRESS_MSB_LOW >> 1): - // high byte of 16-bit address - ihex->address &= ADDRESS_HIGH_MASK; // clear the 16-bit address - ihex->address |= ((ihex_address_t) b) << 8U; - break; - case (READ_ADDRESS_LSB_LOW >> 1): - // low byte of 16-bit address - ihex->address |= (ihex_address_t) b; - break; - case (READ_RECORD_TYPE_LOW >> 1): - // record type - if (b & ~IHEX_READ_RECORD_TYPE_MASK) { - // skip unknown record types silently - return; - } - ihex->flags = (ihex->flags & ~IHEX_READ_RECORD_TYPE_MASK) | b; - break; - case (READ_DATA_LOW >> 1): - if (len < ihex->line_length) { - // data byte - ihex->length = len + 1; - state = READ_DATA_HIGH; - goto save_read_state; - } - // end of line (last "data" byte is checksum) - state = READ_WAIT_FOR_START; - end_read: - ihex_end_read(ihex, outfile); - } - } -save_read_state: - ihex->flags |= state << IHEX_READ_STATE_OFFSET; -} - -void -ihex_read_bytes (struct ihex_state * ihex, - const char * data, - ihex_count_t count, - FILE* outfile) { - while (count > 0) { - ihex_read_byte(ihex, *data++, outfile); - --count; - } -} - -ihex_bool_t -ihex_data_read (struct ihex_state *ihex, - ihex_record_type_t type, - ihex_bool_t error, - FILE* outfile) { - unsigned long line_number = 1L; - unsigned long file_position = 0L; - unsigned long address_offset = 0L; - bool debug_enabled = false; - - if (error) { - (void) fprintf(stderr, "Checksum error on line %lu\n", line_number); - return false; - } - if ((error = (ihex->length < ihex->line_length))) { - (void) fprintf(stderr, "Line length error on line %lu\n", line_number); - return false; - } - if (!outfile) { - (void) fprintf(stderr, "Excess data after end of file record\n"); - return false; - } - if (type == IHEX_DATA_RECORD) { - unsigned long address = (unsigned long) IHEX_LINEAR_ADDRESS(ihex); - if (address < address_offset) { - if (address_offset == AUTODETECT_ADDRESS) { - // autodetect initial address - address_offset = address; - if (debug_enabled) { - (void) fprintf(stderr, "Address offset: 0x%lx\n", - address_offset); - } - } else { - (void) fprintf(stderr, "Address underflow on line %lu\n", - line_number); - return false; - } - } - address -= address_offset; - if (address != file_position) { - if (debug_enabled) { - (void) fprintf(stderr, - "Seeking from 0x%lx to 0x%lx on line %lu\n", - file_position, address, line_number); - } - if (outfile == stdout || fseek(outfile, (long) address, SEEK_SET)) { - if (file_position < address) { - // "seek" forward in stdout by writing NUL bytes - do { - (void) fputc('\0', outfile); - } while (++file_position < address); - } else { - perror("fseek"); - return false; - } - } - file_position = address; - } - if (!fwrite(ihex->data, ihex->length, 1, outfile)) { - perror("fwrite"); - return false; - } - file_position += ihex->length; - } else if (type == IHEX_END_OF_FILE_RECORD) { - if (debug_enabled) { - (void) fprintf(stderr, "%lu bytes written\n", file_position); - } - if (outfile != stdout) { - (void) fclose(outfile); - } - outfile = NULL; - } - return true; -} diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_read.h b/host/lib/usrp_clock/octoclock/kk_ihex_read.h deleted file mode 100644 index 303622b18..000000000 --- a/host/lib/usrp_clock/octoclock/kk_ihex_read.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * kk_ihex_read.h: A simple library for reading Intel HEX data. See - * the accompanying kk_ihex_write.h for IHEX write support. - * - * - * READING INTEL HEX DATA - * ---------------------- - * - * To read data in the Intel HEX format, you must perform the actual reading - * of bytes using other means (e.g., stdio). The bytes read must then be - * passed to `ihex_read_byte` and/or `ihex_read_bytes`. The reading functions - * will then call `ihex_data_read`, at which stage the `struct ihex_state` - * structure will contain the data along with its address. See below for - * details and example implementation of `ihex_data_read`. - * - * The sequence to read data in IHEX format is: - * struct ihex_state ihex; - * ihex_begin_read(&ihex); - * ihex_read_bytes(&ihex, my_input_bytes, length_of_my_input_bytes); - * ihex_end_read(&ihex); - * - * - * CONSERVING MEMORY - * ----------------- - * - * For memory-critical use, you can save additional memory by defining - * `IHEX_LINE_MAX_LENGTH` as something less than 255. Note, however, that - * this limit affects both reading and writing, so the resulting library - * will be unable to read lines with more than this number of data bytes. - * That said, I haven't encountered any IHEX files with more than 32 - * data bytes per line. - * - * - * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/ - * Provided with absolutely no warranty, use at your own risk only. - * Use and distribute freely, mark modified copies as such. - * - * Modifications Copyright (c) 2015 National Instruments Corp. - */ - -#ifndef KK_IHEX_READ_H -#define KK_IHEX_READ_H -#ifdef __cplusplus -extern "C" { -#endif - -#include "kk_ihex.h" - -#include <stdio.h> - -// Begin reading at address 0 -void ihex_begin_read(struct ihex_state * const ihex); - -// Begin reading at `address` (the lowest 16 bits of which will be ignored); -// this is required only if the high bytes of the 32-bit starting address -// are not specified in the input data and they are non-zero -void ihex_read_at_address(struct ihex_state * const ihex, - ihex_address_t address); - -// Read a single character -void ihex_read_byte(struct ihex_state * const ihex, const char byte, FILE* outfile); - -// Read `count` bytes from `data` -void ihex_read_bytes(struct ihex_state * ihex, - const char * data, - ihex_count_t count, - FILE* outfile); - -// End reading (may call `ihex_data_read` if there is data waiting) -void ihex_end_read(struct ihex_state * const ihex, FILE* outfile); - -// Called when a complete line has been read, the record type of which is -// passed as `type`. The `ihex` structure will have its fields `data`, -// `line_length`, `address`, and `segment` set appropriately. In case -// of reading an `IHEX_EXTENDED_LINEAR_ADDRESS_RECORD` or an -// `IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD` the record's data is not -// yet parsed - it will be parsed into the `address` or `segment` field -// only if `ihex_data_read` returns `true`. This allows manual handling -// of extended addresses by parsing the `ihex->data` bytes. -// -// Possible error cases include checksum mismatch (which is indicated -// as an argument), and excessive line length (in case this has been -// compiled with `IHEX_LINE_MAX_LENGTH` less than 255) which is indicated -// by `line_length` greater than `length`. Unknown record types and -// other erroneous data is usually silently ignored by this minimalistic -// parser. (It is recommended to compute a hash over the complete data -// once received and verify that against the source.) -// -// Example implementation: -// -// ihex_bool_t ihex_data_read(struct ihex_state *ihex, -// ihex_record_type_t type, -// ihex_bool_t error) { -// error = error || (ihex->length < ihex->line_length); -// if (type == IHEX_DATA_RECORD && !error) { -// (void) fseek(outfile, IHEX_LINEAR_ADDRESS(ihex), SEEK_SET); -// (void) fwrite(ihex->data, 1, ihex->length, outfile); -// } else if (type == IHEX_END_OF_FILE_RECORD) { -// (void) fclose(outfile); -// } -// return !error; -// } -// -ihex_bool_t ihex_data_read(struct ihex_state *ihex, - ihex_record_type_t type, - ihex_bool_t checksum_mismatch, - FILE* outfile); - -// Begin reading at `segment`; this is required only if the initial segment -// is not specified in the input data and it is non-zero. -// -#ifndef IHEX_DISABLE_SEGMENTS -void ihex_read_at_segment(struct ihex_state *ihex, ihex_segment_t segment); -#endif - -#ifdef __cplusplus -} -#endif -#endif // !KK_IHEX_READ_H diff --git a/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp index 8b47da7e5..fdb254024 100644 --- a/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp +++ b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp @@ -17,8 +17,8 @@ #include "octoclock_impl.hpp" #include "common.h" -#include "kk_ihex_read.h" +#include "../../utils/ihex.hpp" #include <uhd/device.hpp> #include <uhd/image_loader.hpp> #include <uhd/transport/udp_simple.hpp> @@ -34,6 +34,8 @@ #include <boost/lexical_cast.hpp> #include <boost/thread.hpp> +#include <algorithm> +#include <iterator> #include <cstdio> #include <cstring> #include <fstream> @@ -54,83 +56,70 @@ using namespace uhd::transport; typedef struct { bool found; uhd::device_addr_t dev_addr; - std::string given_filepath; - std::string actual_filepath; // If using a .hex, this is the converted .bin - bool from_hex; - boost::uint32_t size; + std::string image_filepath; boost::uint16_t crc; boost::uint16_t num_blocks; udp_simple::sptr ctrl_xport; udp_simple::sptr fw_xport; boost::uint8_t data_in[udp_simple::mtu]; + std::vector<boost::uint8_t> image; } octoclock_session_t; static void octoclock_calculate_crc(octoclock_session_t &session){ - std::ifstream ifile(session.actual_filepath.c_str()); - boost::uint8_t temp_image[OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES]; - ifile.read((char*)temp_image, session.size); - session.crc = 0xFFFF; - for(size_t i = 0; i < session.size; i++){ - session.crc ^= temp_image[i]; + for(size_t i = 0; i < session.image.size(); i++) + { + session.crc ^= session.image[i]; for(boost::uint8_t j = 0; j < 8; ++j){ if(session.crc & 1) session.crc = (session.crc >> 1) ^ 0xA001; else session.crc = (session.crc >> 1); } } - - ifile.close(); } -static void octoclock_convert_ihex(octoclock_session_t &session){ - struct ihex_state ihex; - ihex_count_t count; - char buf[256]; - FILE* infile = fopen(session.given_filepath.c_str(), "r"); - FILE* outfile = fopen(session.actual_filepath.c_str(), "w"); - uint64_t line_number = 1; - - ihex_begin_read(&ihex); - while(fgets(buf, 256, infile)){ - count = ihex_count_t(strlen(buf)); - ihex_read_bytes(&ihex, buf, count, outfile); - line_number += (count && buf[count - 1] == '\n'); +static void octoclock_read_bin(octoclock_session_t &session) +{ + std::ifstream bin_file(session.image_filepath.c_str(), std::ios::in | std::ios::binary); + if (not bin_file.is_open()) { + throw uhd::io_error("Could not read image file."); } - ihex_end_read(&ihex, outfile); // Closes outfile - (void)fclose(infile); + size_t filesize = fs::file_size(session.image_filepath); + session.image.clear(); + session.image.resize(filesize); + bin_file.read((char*)&session.image[0], filesize); + if(size_t(bin_file.gcount()) != filesize) { + throw uhd::io_error("Failed to read firmware image."); + } + + bin_file.close(); } static void octoclock_validate_firmware_image(octoclock_session_t &session){ - if(not fs::exists(session.given_filepath)){ + if(not fs::exists(session.image_filepath)){ throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\"") - % session.given_filepath)); + % session.image_filepath)); } - std::string extension = fs::extension(session.given_filepath); + std::string extension = fs::extension(session.image_filepath); if(extension == ".bin"){ - session.actual_filepath = session.given_filepath; - session.from_hex = false; + octoclock_read_bin(session); } else if(extension == ".hex"){ - session.actual_filepath = fs::path(fs::path(uhd::get_tmp_path()) / - str(boost::format("octoclock_fw_%d.bin") - % time_spec_t::get_system_time().get_full_secs()) - ).string(); - - octoclock_convert_ihex(session); - session.from_hex = true; + ihex_reader hex_reader(session.image_filepath); + session.image = hex_reader.to_vector(OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES); } - else throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .hex or .bin."))); + else throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .hex or .bin.") + % extension)); - session.size = fs::file_size(session.actual_filepath); - if(session.size > OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES){ + if(session.image.size() > OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES){ throw uhd::runtime_error(str(boost::format("The specified firmware image is too large: %d vs. %d") - % session.size % OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES)); + % session.image.size() % OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES)); } - session.num_blocks = (session.size % OCTOCLOCK_BLOCK_SIZE) ? ((session.size / OCTOCLOCK_BLOCK_SIZE) + 1) - : (session.size / OCTOCLOCK_BLOCK_SIZE); + session.num_blocks = (session.image.size() % OCTOCLOCK_BLOCK_SIZE) + ? ((session.image.size() / OCTOCLOCK_BLOCK_SIZE) + 1) + : (session.image.size() / OCTOCLOCK_BLOCK_SIZE); octoclock_calculate_crc(session); } @@ -164,14 +153,15 @@ static void octoclock_setup_session(octoclock_session_t &session, } session.dev_addr = devs[0]; + session.found = true; // If no filepath is given, use the default if(filepath == ""){ - session.given_filepath = find_image_path(str(boost::format("octoclock_r%s_fw.hex") + session.image_filepath = find_image_path(str(boost::format("octoclock_r%s_fw.hex") % session.dev_addr.get("revision","4") )); } - else session.given_filepath = filepath; + else session.image_filepath = filepath; octoclock_validate_firmware_image(session); @@ -231,10 +221,10 @@ static void octoclock_burn(octoclock_session_t &session){ const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in); // Tell OctoClock to prepare for burn - pkt_out.len = htonx<boost::uint16_t>(session.size); + pkt_out.len = htonx<boost::uint16_t>(session.image.size()); size_t len = 0; std::cout << " -- Preparing OctoClock for firmware load..." << std::flush; - pkt_out.len = session.size; + pkt_out.len = session.image.size(); pkt_out.crc = session.crc; UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, PREPARE_FW_BURN_CMD, pkt_out, len, session.data_in); if(UHD_OCTOCLOCK_PACKET_MATCHES(FW_BURN_READY_ACK, pkt_out, pkt_in, len)){ @@ -242,14 +232,10 @@ static void octoclock_burn(octoclock_session_t &session){ } else{ std::cout << "failed." << std::endl; - if(session.from_hex){ - fs::remove(session.actual_filepath); - } throw uhd::runtime_error("Failed to prepare OctoClock for firmware load."); } // Start burning - std::ifstream image(session.actual_filepath.c_str(), std::ios::binary); for(size_t i = 0; i < session.num_blocks; i++){ pkt_out.sequence++; pkt_out.addr = i * OCTOCLOCK_BLOCK_SIZE; @@ -260,14 +246,10 @@ static void octoclock_burn(octoclock_session_t &session){ << std::flush; memset(pkt_out.data, 0, OCTOCLOCK_BLOCK_SIZE); - image.read((char*)pkt_out.data, OCTOCLOCK_BLOCK_SIZE); + memcpy((char*)pkt_out.data, &session.image[pkt_out.addr], OCTOCLOCK_BLOCK_SIZE); UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, FILE_TRANSFER_CMD, pkt_out, len, session.data_in); if(not UHD_OCTOCLOCK_PACKET_MATCHES(FILE_TRANSFER_ACK, pkt_out, pkt_in, len)){ - image.close(); std::cout << std::endl; - if(session.from_hex){ - fs::remove(session.actual_filepath); - } throw uhd::runtime_error("Failed to load firmware."); } } @@ -275,7 +257,6 @@ static void octoclock_burn(octoclock_session_t &session){ std::cout << str(boost::format("\r -- Loading firmware: 100%% (%d/%d blocks)") % session.num_blocks % session.num_blocks) << std::endl; - image.close(); } static void octoclock_verify(octoclock_session_t &session){ @@ -285,7 +266,6 @@ static void octoclock_verify(octoclock_session_t &session){ const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in); size_t len = 0; - std::ifstream image(session.actual_filepath.c_str(), std::ios::binary); boost::uint8_t image_part[OCTOCLOCK_BLOCK_SIZE]; boost::uint16_t cmp_len = 0; for(size_t i = 0; i < session.num_blocks; i++){ @@ -298,34 +278,22 @@ static void octoclock_verify(octoclock_session_t &session){ << std::flush; memset(image_part, 0, OCTOCLOCK_BLOCK_SIZE); - image.read((char*)image_part, OCTOCLOCK_BLOCK_SIZE); - cmp_len = image.gcount(); + memcpy((char*)image_part, &session.image[pkt_out.addr], OCTOCLOCK_BLOCK_SIZE); + cmp_len = std::min<size_t>(OCTOCLOCK_BLOCK_SIZE, session.image.size() - size_t(pkt_out.addr)); UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, READ_FW_CMD, pkt_out, len, session.data_in); if(UHD_OCTOCLOCK_PACKET_MATCHES(READ_FW_ACK, pkt_out, pkt_in, len)){ if(memcmp(pkt_in->data, image_part, cmp_len)){ std::cout << std::endl; - image.close(); - if(session.from_hex){ - fs::remove(session.actual_filepath); - } throw uhd::runtime_error("Failed to verify OctoClock firmware."); } } else{ std::cout << std::endl; - image.close(); - if(session.from_hex){ - fs::remove(session.actual_filepath); - } throw uhd::runtime_error("Failed to verify OctoClock firmware."); } } - image.close(); - if(session.from_hex){ - fs::remove(session.actual_filepath); - } std::cout << str(boost::format("\r -- Verifying firmware load: 100%% (%d/%d blocks)") % session.num_blocks % session.num_blocks) << std::endl; @@ -360,7 +328,7 @@ bool octoclock_image_loader(const image_loader::image_loader_args_t &image_loade std::cout << boost::format("Unit: OctoClock (%s)") % session.dev_addr["addr"] << std::endl; - std::cout << "Firmware: " << session.given_filepath << std::endl; + std::cout << "Firmware: " << session.image_filepath << std::endl; octoclock_burn(session); octoclock_verify(session); diff --git a/host/lib/utils/CMakeLists.txt b/host/lib/utils/CMakeLists.txt index c5c975dfa..72e2f3f50 100644 --- a/host/lib/utils/CMakeLists.txt +++ b/host/lib/utils/CMakeLists.txt @@ -132,6 +132,7 @@ SET_SOURCE_FILES_PROPERTIES( LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/csv.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gain_group.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ihex.cpp ${CMAKE_CURRENT_SOURCE_DIR}/load_modules.cpp ${CMAKE_CURRENT_SOURCE_DIR}/log.cpp ${CMAKE_CURRENT_SOURCE_DIR}/msg.cpp diff --git a/host/lib/utils/ihex.cpp b/host/lib/utils/ihex.cpp new file mode 100644 index 000000000..a29ac3e72 --- /dev/null +++ b/host/lib/utils/ihex.cpp @@ -0,0 +1,236 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "ihex.hpp" +#include <uhd/exception.hpp> +#include <boost/format.hpp> +#include <boost/make_shared.hpp> +#include <sstream> +#include <fstream> + +using namespace uhd; + +/*! + * Verify checksum of a Intel HEX record + * \param record a line from an Intel HEX file + * \return true if record is valid, false otherwise + */ +static bool checksum(const std::string& record) +{ + size_t len = record.length(); + unsigned char sum = 0; + unsigned int val; + + for (size_t i = 1; i < len; i += 2) { + std::istringstream(record.substr(i, 2)) >> std::hex >> val; + sum += val; + } + + if (sum == 0) + return true; + else + return false; +} + + +/*! + * Parse Intel HEX record + * + * \param record a line from an Intel HEX file + * \param len output length of record + * \param addr output address + * \param type output type + * \param data output data + * \return true if record is sucessfully read, false on error + */ +static bool parse_record( + const std::string& record, + boost::uint16_t &len, + boost::uint16_t &addr, + boost::uint16_t &type, + unsigned char* data +) { + unsigned int i; + unsigned int val; + + if (record.substr(0, 1) != ":") + return false; + + std::istringstream(record.substr(1, 2)) >> std::hex >> len; + std::istringstream(record.substr(3, 4)) >> std::hex >> addr; + std::istringstream(record.substr(7, 2)) >> std::hex >> type; + + if (len > (2 * (record.length() - 9))) // sanity check to prevent buffer overrun + return false; + + for (i = 0; i < len; i++) { + std::istringstream(record.substr(9 + 2 * i, 2)) >> std::hex >> val; + data[i] = (unsigned char) val; + } + + return true; +} + + +ihex_reader::ihex_reader(const std::string &ihex_filename) + : _ihex_filename(ihex_filename) +{ + // nop +} + + +void ihex_reader::read(ihex_reader::record_handle_type record_handler) +{ + const char *filename = _ihex_filename.c_str(); + + /* Fields used in every record. */ + boost::uint16_t len = 0; + boost::uint16_t type = 0; + boost::uint16_t lower_address_bits = 0x0000; + static const int MAX_RECORD_LENGTH = 255; + unsigned char data[MAX_RECORD_LENGTH]; + + /* Can be set by the Intel HEX record 0x04, used for all 0x00 records + * thereafter. Note this field takes the place of the 'index' parameter in + * libusb calls, and is necessary for FX3's 32-bit addressing. */ + boost::uint16_t upper_address_bits = 0x0000; + + std::ifstream file; + file.open(filename, std::ifstream::in); + + if(!file.good()) { + throw uhd::io_error("ihex_reader::read(): cannot open firmware input file"); + } + + while (!file.eof()) { + boost::int32_t ret = 0; + std::string record; + file >> record; + + if (!(record.length() > 0)) + continue; + + /* Check for valid Intel HEX record. */ + if (!checksum(record) + || !parse_record(record, len, lower_address_bits, type, data)) { + throw uhd::io_error("ihex_reader::read(): bad intel hex record checksum"); + } + + /* Type 0x00: Data. */ + if (type == 0x00) { + ret = record_handler(lower_address_bits, upper_address_bits, data, len); + + if (ret < 0) { + throw uhd::io_error("ihex_reader::read(): record hander returned failure code"); + } + } + + /* Type 0x01: EOF. */ + else if (type == 0x01) { + if (lower_address_bits != 0x0000 || len != 0 ) { + throw uhd::io_error("ihex_reader::read(): For EOF record, address must be 0, length must be 0."); + } + + /* Successful termination! */ + file.close(); + return; + } + + /* Type 0x04: Extended Linear Address Record. */ + else if (type == 0x04) { + if (lower_address_bits != 0x0000 || len != 2 ) { + throw uhd::io_error("ihex_reader::read(): For ELA record, address must be 0, length must be 2."); + } + + upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\ + + ((boost::uint16_t)(data[1] & 0x00FF)); + } + + /* Type 0x05: Start Linear Address Record. */ + else if (type == 0x05) { + if (lower_address_bits != 0x0000 || len != 4 ) { + throw uhd::io_error("ihex_reader::read(): For SLA record, address must be 0, length must be 4."); + } + + /* The firmware load is complete. We now need to tell the CPU + * to jump to an execution address start point, now contained within + * the data field. Parse these address bits out, and then push the + * instruction. */ + upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\ + + ((boost::uint16_t)(data[1] & 0x00FF)); + lower_address_bits = ((boost::uint16_t)((data[2] & 0x00FF) << 8))\ + + ((boost::uint16_t)(data[3] & 0x00FF)); + + record_handler(lower_address_bits, upper_address_bits, 0, 0); + } + + /* If we receive an unknown record type, error out. */ + else { + throw uhd::io_error(str(boost::format("ihex_reader::read(): unsupported record type: %X.") % type)); + } + } + + /* There was no valid EOF. */ + throw uhd::io_error("ihex_reader::read(): No EOF record found."); +} + +// We need a functor for the cast, a lambda would be perfect... +int _file_writer_callback( + boost::shared_ptr<std::ofstream> output_file, + unsigned char *buff, + boost::uint16_t len +) { + output_file->write((const char *) buff, len); + return 0; +} + +void ihex_reader::to_bin_file(const std::string &bin_filename) +{ + boost::shared_ptr<std::ofstream> output_file(boost::make_shared<std::ofstream>()); + output_file->open(bin_filename.c_str(), std::ios::out | std::ios::binary); + if (not output_file->is_open()) { + throw uhd::io_error(str(boost::format("Could not open file for writing: %s") % bin_filename)); + } + + this->read(boost::bind(&_file_writer_callback, output_file, _3, _4)); + + output_file->close(); +} + +// We need a functor for the cast, a lambda would be perfect... +int _vector_writer_callback( + std::vector<boost::uint8_t>& vector, + unsigned char *buff, + boost::uint16_t len +) { + for (size_t i = 0; i < len; i++) { + vector.push_back(buff[i]); + } + return 0; +} + +#define DEFAULT_SIZE_ESTIMATE 8000000 +std::vector<boost::uint8_t> ihex_reader::to_vector(const size_t size_estimate) +{ + std::vector<boost::uint8_t> buf; + buf.reserve(size_estimate == 0 ? DEFAULT_SIZE_ESTIMATE : size_estimate); + + this->read(boost::bind(&_vector_writer_callback, boost::ref(buf), _3, _4)); + + return buf; +} + diff --git a/host/lib/utils/ihex.hpp b/host/lib/utils/ihex.hpp new file mode 100644 index 000000000..9df1fcbc3 --- /dev/null +++ b/host/lib/utils/ihex.hpp @@ -0,0 +1,79 @@ +// +// Copyright 2015 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/>. +// + +#ifndef INCLUDED_IHEX_READER_HPP +#define INCLUDED_IHEX_READER_HPP + +#include <boost/bind.hpp> +#include <boost/function.hpp> +#include <boost/cstdint.hpp> +#include <string> +#include <vector> + +namespace uhd { + +class ihex_reader +{ +public: + // Arguments are: lower address bits, upper address bits, buff, length + typedef boost::function<int(boost::uint16_t,boost::uint16_t,unsigned char*,boost::uint16_t)> record_handle_type; + + /* + * \param ihex_filename Path to the *.ihx file + */ + ihex_reader(const std::string &ihex_filename); + + /*! Read an Intel HEX file and handle it record by record. + * + * Every record is individually passed off to a record handler function. + * + * \param record_handler The functor that will handle the records. + * + * \throws uhd::io_error if the HEX file is corrupted or unreadable. + */ + void read(record_handle_type record_handler); + + /* Convert the ihex file to a bin file. + * + * *Note:* This function makes the assumption that the hex file is + * contiguous, and starts at address zero. + * + * \param bin_filename Output filename. + * + * \throws uhd::io_error if the HEX file is corrupted or unreadable. + */ + void to_bin_file(const std::string &bin_filename); + + /*! Copy the ihex file into a buffer. + * + * Very similar functionality as to_bin_file(). + * + * *Note:* This function makes the assumption that the hex file is + * contiguous, and starts at address zero. + * + * \throws uhd::io_error if the HEX file is corrupted or unreadable. + */ + std::vector<boost::uint8_t> to_vector(const size_t size_estimate = 0); + +private: + const std::string _ihex_filename; +}; + +}; /* namespace uhd */ + +#endif /* INCLUDED_IHEX_READER_HPP */ + diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 1ea86f2cc..9ab95596d 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -91,10 +91,9 @@ IF(ENABLE_OCTOCLOCK) SET(octoclock_burner_sources octoclock_firmware_burner.cpp - ${CMAKE_SOURCE_DIR}/lib/usrp_clock/octoclock/kk_ihex_read.c + ${CMAKE_SOURCE_DIR}/lib/utils/ihex.cpp ) - ADD_DEFINITIONS(-DIHEX_USE_STDBOOL) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp_clock/octoclock) ADD_EXECUTABLE(octoclock_firmware_burner ${octoclock_burner_sources}) TARGET_LINK_LIBRARIES(octoclock_firmware_burner uhd ${Boost_LIBRARIES}) diff --git a/host/utils/b2xx_fx3_utils.cpp b/host/utils/b2xx_fx3_utils.cpp index a3eaf862a..a6896c868 100644 --- a/host/utils/b2xx_fx3_utils.cpp +++ b/host/utils/b2xx_fx3_utils.cpp @@ -52,7 +52,8 @@ const static vid_pid_t known_vid_pids[] = { {FX3_VID, FX3_DEFAULT_PID}, {FX3_VID, FX3_REENUM_PID}, {B200_VENDOR_ID, B200_PRODUCT_ID}, - {B200_VENDOR_ID, B205_PRODUCT_ID}, + {B200_VENDOR_ID, B200MINI_PRODUCT_ID}, + {B200_VENDOR_ID, B205MINI_PRODUCT_ID}, {B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID}, {B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID} }; @@ -175,7 +176,7 @@ uhd::transport::usb_device_handle::sptr open_device(const boost::uint16_t vid, c vp = known_vid_pid_vector[i]; handles = uhd::transport::usb_device_handle::get_device_list(vp.vid, vp.pid); } - + } if (handles.size() > 0) diff --git a/host/utils/octoclock_firmware_burner.cpp b/host/utils/octoclock_firmware_burner.cpp index c338cd818..326985df5 100644 --- a/host/utils/octoclock_firmware_burner.cpp +++ b/host/utils/octoclock_firmware_burner.cpp @@ -42,7 +42,7 @@ #include <uhd/utils/safe_main.hpp> #include "../lib/usrp_clock/octoclock/common.h" -#include "kk_ihex_read.h" +#include "../lib/utils/ihex.hpp" #define MAX_FIRMWARE_SIZE 1024*120 #define BLOCK_SIZE 256 @@ -142,7 +142,7 @@ device_addrs_t bootloader_find(const std::string &ip_addr){ 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_QUERY_CMD, pkt_out, len, octoclock_data); if(UHD_OCTOCLOCK_PACKET_MATCHES(OCTOCLOCK_QUERY_ACK, pkt_out, pkt_in, len) and @@ -292,22 +292,8 @@ void finalize(udp_simple::sptr udp_transport){ } void octoclock_convert_ihex(const std::string &hex_path, const std::string &bin_path){ - struct ihex_state ihex; - ihex_count_t count; - char buf[256]; - FILE* infile = fopen(hex_path.c_str(), "r"); - FILE* outfile = fopen(bin_path.c_str(), "w"); - uint64_t line_number = 1; - - ihex_begin_read(&ihex); - while(fgets(buf, 256, infile)){ - count = ihex_count_t(strlen(buf)); - ihex_read_bytes(&ihex, buf, count, outfile); - line_number += (count && buf[count - 1] == '\n'); - } - ihex_end_read(&ihex, outfile); // Closes outfile - - (void)fclose(infile); + 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[])){ diff --git a/host/utils/uhd-usrp.rules b/host/utils/uhd-usrp.rules index e10c86f13..76bccfefa 100644 --- a/host/utils/uhd-usrp.rules +++ b/host/utils/uhd-usrp.rules @@ -24,5 +24,6 @@ SUBSYSTEMS=="usb", ATTRS{idVendor}=="2500", ATTRS{idProduct}=="0002", MODE:="066 #B200 SUBSYSTEMS=="usb", ATTRS{idVendor}=="2500", ATTRS{idProduct}=="0020", MODE:="0666" SUBSYSTEMS=="usb", ATTRS{idVendor}=="2500", ATTRS{idProduct}=="0021", MODE:="0666" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="2500", ATTRS{idProduct}=="0022", MODE:="0666" SUBSYSTEMS=="usb", ATTRS{idVendor}=="3923", ATTRS{idProduct}=="7813", MODE:="0666" SUBSYSTEMS=="usb", ATTRS{idVendor}=="3923", ATTRS{idProduct}=="7814", MODE:="0666" |