From 4cc09da892141fc08c33eec6cc168bccdb6b84ac Mon Sep 17 00:00:00 2001 From: Martin Braun Date: Wed, 25 Nov 2015 10:25:54 -0800 Subject: b200: Factored ihex routines out of b200_iface --- host/lib/utils/CMakeLists.txt | 1 + host/lib/utils/ihex.cpp | 236 ++++++++++++++++++++++++++++++++++++++++++ host/lib/utils/ihex.hpp | 79 ++++++++++++++ 3 files changed, 316 insertions(+) create mode 100644 host/lib/utils/ihex.cpp create mode 100644 host/lib/utils/ihex.hpp (limited to 'host/lib/utils') 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 . +// + +#include "ihex.hpp" +#include +#include +#include +#include +#include + +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 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 output_file(boost::make_shared()); + 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 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 ihex_reader::to_vector(const size_t size_estimate) +{ + std::vector 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 . +// + +#ifndef INCLUDED_IHEX_READER_HPP +#define INCLUDED_IHEX_READER_HPP + +#include +#include +#include +#include +#include + +namespace uhd { + +class ihex_reader +{ +public: + // Arguments are: lower address bits, upper address bits, buff, length + typedef boost::function 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 to_vector(const size_t size_estimate = 0); + +private: + const std::string _ihex_filename; +}; + +}; /* namespace uhd */ + +#endif /* INCLUDED_IHEX_READER_HPP */ + -- cgit v1.2.3 From 373fec2fb177c50b40733445633a10b01fe62cc5 Mon Sep 17 00:00:00 2001 From: Nicholas Corgan Date: Mon, 14 Dec 2015 07:27:38 -0800 Subject: ihex: Windows fixes --- .../lib/usrp_clock/octoclock/octoclock_image_loader.cpp | 17 ++++++++--------- host/lib/utils/ihex.cpp | 4 ++-- host/utils/CMakeLists.txt | 1 - 3 files changed, 10 insertions(+), 12 deletions(-) (limited to 'host/lib/utils') diff --git a/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp index 1d4699f54..fdb254024 100644 --- a/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp +++ b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp @@ -79,20 +79,18 @@ static void octoclock_calculate_crc(octoclock_session_t &session){ static void octoclock_read_bin(octoclock_session_t &session) { - std::ifstream bin_file(session.image_filepath.c_str()); + 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."); } size_t filesize = fs::file_size(session.image_filepath); session.image.clear(); - session.image.reserve(filesize); - - std::copy( - std::istream_iterator(bin_file), - std::istream_iterator(), - std::back_inserter(session.image) - ); + 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(); } @@ -111,7 +109,8 @@ static void octoclock_validate_firmware_image(octoclock_session_t &session){ 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)); 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") diff --git a/host/lib/utils/ihex.cpp b/host/lib/utils/ihex.cpp index bb9b49b32..a29ac3e72 100644 --- a/host/lib/utils/ihex.cpp +++ b/host/lib/utils/ihex.cpp @@ -213,7 +213,7 @@ void ihex_reader::to_bin_file(const std::string &bin_filename) // We need a functor for the cast, a lambda would be perfect... int _vector_writer_callback( - std::vector vector, + std::vector& vector, unsigned char *buff, boost::uint16_t len ) { @@ -229,7 +229,7 @@ std::vector ihex_reader::to_vector(const size_t size_estimate) std::vector buf; buf.reserve(size_estimate == 0 ? DEFAULT_SIZE_ESTIMATE : size_estimate); - this->read(boost::bind(&_vector_writer_callback, buf, _3, _4)); + this->read(boost::bind(&_vector_writer_callback, boost::ref(buf), _3, _4)); return buf; } diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 8367c0184..28fecc895 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -87,7 +87,6 @@ IF(ENABLE_OCTOCLOCK) ${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}) -- cgit v1.2.3