aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/utils/ihex.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/utils/ihex.cpp')
-rw-r--r--host/lib/utils/ihex.cpp236
1 files changed, 236 insertions, 0 deletions
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;
+}
+