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 /host/lib/utils | |
| 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
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 */ + | 
