diff options
Diffstat (limited to 'host/lib/utils')
-rw-r--r-- | host/lib/utils/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/lib/utils/ihex.cpp | 236 | ||||
-rw-r--r-- | host/lib/utils/ihex.hpp | 79 |
3 files changed, 316 insertions, 0 deletions
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 */ + |