aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2015-11-25 10:25:54 -0800
committerMartin Braun <martin.braun@ettus.com>2015-12-14 13:26:44 -0800
commit4cc09da892141fc08c33eec6cc168bccdb6b84ac (patch)
treeb113ae103bcc3fd6a235c1c60cd1bd8c2606f8b4
parentac1db2c4605427fa7e8929422c647004a4333e59 (diff)
downloaduhd-4cc09da892141fc08c33eec6cc168bccdb6b84ac.tar.gz
uhd-4cc09da892141fc08c33eec6cc168bccdb6b84ac.tar.bz2
uhd-4cc09da892141fc08c33eec6cc168bccdb6b84ac.zip
b200: Factored ihex routines out of b200_iface
-rw-r--r--host/lib/usrp/b200/b200_iface.cpp183
-rw-r--r--host/lib/utils/CMakeLists.txt1
-rw-r--r--host/lib/utils/ihex.cpp236
-rw-r--r--host/lib/utils/ihex.hpp79
4 files changed, 339 insertions, 160 deletions
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/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..bb9b49b32
--- /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, 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 */
+