diff options
author | Nicholas Corgan <nick.corgan@ettus.com> | 2015-08-14 07:42:01 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2015-08-14 12:30:53 -0700 |
commit | d35a0e28b6af67e4500b51c4739886f7544bca35 (patch) | |
tree | 64c04cd3b879d4898ecfcf4a52260ab8fa312d37 | |
parent | 8d6b63e1e86ae0f1ee8b084ea2db6d5b5b705179 (diff) | |
download | uhd-d35a0e28b6af67e4500b51c4739886f7544bca35.tar.gz uhd-d35a0e28b6af67e4500b51c4739886f7544bca35.tar.bz2 uhd-d35a0e28b6af67e4500b51c4739886f7544bca35.zip |
octoclock: replaced Intel hex -> binary converter
-rw-r--r-- | host/lib/usrp_clock/octoclock/CMakeLists.txt | 4 | ||||
-rw-r--r-- | host/lib/usrp_clock/octoclock/ihexcvt.cpp | 250 | ||||
-rw-r--r-- | host/lib/usrp_clock/octoclock/ihexcvt.hpp | 22 | ||||
-rw-r--r-- | host/lib/usrp_clock/octoclock/kk_ihex.h | 191 | ||||
-rw-r--r-- | host/lib/usrp_clock/octoclock/kk_ihex_license.txt | 20 | ||||
-rw-r--r-- | host/lib/usrp_clock/octoclock/kk_ihex_read.c | 261 | ||||
-rw-r--r-- | host/lib/usrp_clock/octoclock/kk_ihex_read.h | 119 | ||||
-rw-r--r-- | host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp | 54 | ||||
-rw-r--r-- | host/utils/CMakeLists.txt | 4 | ||||
-rw-r--r-- | host/utils/ihexcvt.cpp | 250 | ||||
-rw-r--r-- | host/utils/ihexcvt.hpp | 22 | ||||
-rw-r--r-- | host/utils/octoclock_firmware_burner.cpp | 23 |
12 files changed, 655 insertions, 565 deletions
diff --git a/host/lib/usrp_clock/octoclock/CMakeLists.txt b/host/lib/usrp_clock/octoclock/CMakeLists.txt index 96b670115..a74cb034f 100644 --- a/host/lib/usrp_clock/octoclock/CMakeLists.txt +++ b/host/lib/usrp_clock/octoclock/CMakeLists.txt @@ -21,8 +21,10 @@ LIBUHD_REGISTER_COMPONENT("OctoClock" ENABLE_OCTOCLOCK ON "ENABLE_LIBUHD" OFF OFF) IF(ENABLE_OCTOCLOCK) + ADD_DEFINITIONS(-DIHEX_USE_STDBOOL) + LIBUHD_APPEND_SOURCES( - ${CMAKE_CURRENT_SOURCE_DIR}/ihexcvt.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/kk_ihex_read.c ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_eeprom.cpp ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_image_loader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_impl.cpp diff --git a/host/lib/usrp_clock/octoclock/ihexcvt.cpp b/host/lib/usrp_clock/octoclock/ihexcvt.cpp deleted file mode 100644 index 0605ee61c..000000000 --- a/host/lib/usrp_clock/octoclock/ihexcvt.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* IHexCvt - Intel HEX File <=> Binary Converter (C++) - Copyright (C) 2014 Ali Nakisaee - -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 2 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, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.*/ - -//Include needed stuff from C++ -#include <iostream> -#include <fstream> -#include <string> - -//... and also from C -#include <stdio.h> - -#include <boost/filesystem.hpp> - -#include <uhd/exception.hpp> -#include "ihexcvt.hpp" - -//Avoid repeating 'std::': -using namespace std; - -//The following function reads a hexadecimal number from a text file. -template <class T> -static bool ReadValueFromHex(ifstream& InputFile, T& outCh, unsigned char* ApplyChecksum) -{ - char V, L; - T X = 0; - outCh = 0; - - //Get the characters one by one. - //Remember: These values are big-endian. - //Remember: Every two hex characters (0-9/A-F) indicate ONE byte. - for (size_t i = 0; i < 2 * sizeof(T); i++) - { - InputFile.get( V ); - if (InputFile.fail()) - return false; - - X <<= 4; - if (V >= '0' && V <= '9') - L = (V - '0'); - else if (V >= 'a' && V <= 'f') - L = (V - 'a' + 10); - else if (V >= 'A' && V <= 'F') - L = (V - 'A' + 10); - else - return false; - X |= L; - - //Apply this character to the checksum - if (ApplyChecksum && i % 2 == 1) *ApplyChecksum += X & 0xFF; - } - - //Return... - outCh = X; - return true; -} - -//The following function writes a hexadecimal number from a text file. -template <class T> -static bool WriteHexValue(ofstream& OutFile, T Value, unsigned char* CalcChecksum) -{ - unsigned char V0 = 0; - char C; - - //Remember: These values are big-endian. - for (size_t i = 0; i < sizeof(T); i++) - { - //Get byte #i from the value. - V0 = (Value >> ((sizeof(T) - i - 1) * 8)) & 0xFF; - - //Extract the high nibble (4-bits) - if ((V0 & 0xF0) <= 0x90) - C = (V0 >> 4) + '0'; - else - C = (V0 >> 4) + ('A' - 10); - OutFile.put( C ); - - //Extract the low nibble (4-bits) - if ((V0 & 0xF) <= 0x9) - C = (V0 & 0xF) + '0'; - else - C = (V0 & 0xF) + ('A' - 10); - OutFile.put( C ); - - //Calculate the checksum - if (CalcChecksum) *CalcChecksum += V0; - } - return true; -} - -//Skip any incoming whitespaces -static void SkipWhitespace(ifstream& InputFile) -{ - for (;;) - { - char C; - InputFile.get(C); - if (InputFile.eof() || InputFile.fail()) break; - if (!(C == '\n' || C == '\r' || C == ' ' || C == '\t' || C == '\v')) - { - InputFile.putback(C); - break; - } - } -} - -//The function responsible for conversion from HEX files to BINary. -void Hex2Bin(const char* SrcName, const char* DstName, bool IgnoreChecksum) -{ - ifstream Src(SrcName); - if (Src.bad()) - { - throw uhd::runtime_error("Could not convert Intel .hex file to binary."); - } - - ofstream Dst(DstName, ios_base::binary); - if (Dst.bad()) - { - throw uhd::runtime_error("Could not convert Intel .hex file to binary."); - } - - char Ch; - int LineIdx = 1; - - unsigned char ByteCount; - unsigned short AddressLow; - unsigned short Extra; - unsigned long ExtraL; - unsigned long AddressOffset = 0; - unsigned char RecordType; - unsigned char Data[255]; - unsigned char CurChecksum; - unsigned char FileChecksum; - bool EOFMarker = false; - bool EOFWarn = false; - - for ( ;; ) - { - Src.get(Ch); - if (Src.eof()) - break; - if (EOFMarker && !EOFWarn) - { - throw uhd::runtime_error("Could not convert Intel .hex file to binary."); - } - if (Ch != ':') goto genericErr; - - CurChecksum = 0; - if (!ReadValueFromHex( Src, ByteCount, &CurChecksum )) goto genericErr; - if (!ReadValueFromHex( Src, AddressLow, &CurChecksum )) goto genericErr; - if (!ReadValueFromHex( Src, RecordType, &CurChecksum )) goto genericErr; - - switch (RecordType) - { - case 0x00: //Data record - for (int i = 0; i < ByteCount; i++) - if (!ReadValueFromHex( Src, Data[i], &CurChecksum )) goto genericErr; - break; - case 0x01: //End Marker - if ( ByteCount != 0 ) - { - goto onErrExit; - } - EOFMarker = true; - break; - case 0x02: //Extended Segment Address - if ( ByteCount != 2 || AddressLow != 0 ) - { - goto onErrExit; - } - if (!ReadValueFromHex( Src, Extra, &CurChecksum )) goto genericErr; - AddressOffset = (unsigned long)Extra << 4; - break; - case 0x03: //Start Segment Address - if ( ByteCount != 4 || AddressLow != 0 ) - { - goto onErrExit; - } - if (!ReadValueFromHex( Src, ExtraL, &CurChecksum )) goto genericErr; - break; - case 0x04: //Extended Linear Address - if ( ByteCount != 2 || AddressLow != 0 ) - { - goto onErrExit; - } - if (!ReadValueFromHex( Src, Extra, &CurChecksum )) goto genericErr; - AddressOffset = (unsigned long)Extra << 16; - break; - case 0x05: //Start Linear Address - if ( ByteCount != 4 || AddressLow != 0 ) - { - goto onErrExit; - } - if (!ReadValueFromHex( Src, ExtraL, &CurChecksum )) goto genericErr; - break; - } - - //Verify checksum - CurChecksum = (~(CurChecksum & 0xFF) + 1) & 0xFF; - if (!ReadValueFromHex( Src, FileChecksum, NULL )) goto genericErr; - if (CurChecksum != FileChecksum) - { - if (!IgnoreChecksum) goto onErrExit; - } - - //Put Data - if (RecordType == 0x00) - { - Dst.seekp( AddressLow + AddressOffset ); - for (int i = 0; i < ByteCount; i++) - { - Dst.put( Data[i] ); - } - } - - //Skip any white space - SkipWhitespace( Src ); - - LineIdx++; - } - - Dst << flush; - Dst.close(); - - return; - -genericErr: - throw uhd::runtime_error("Invalid Intel .hex file detected."); - -onErrExit: - Dst.close(); - Src.close(); - boost::filesystem::remove(DstName); - throw uhd::runtime_error("Could not convert Intel .hex file to binary."); -} diff --git a/host/lib/usrp_clock/octoclock/ihexcvt.hpp b/host/lib/usrp_clock/octoclock/ihexcvt.hpp deleted file mode 100644 index d577ece1f..000000000 --- a/host/lib/usrp_clock/octoclock/ihexcvt.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright 2014 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 _IHEXCVT_HPP_ -#define _IHEXCVT_HPP_ - -void Hex2Bin(const char* SrcName, const char* DstName, bool IgnoreChecksum); - -#endif /* _IHEXCVT_HPP_ */ diff --git a/host/lib/usrp_clock/octoclock/kk_ihex.h b/host/lib/usrp_clock/octoclock/kk_ihex.h new file mode 100644 index 000000000..20eba43cc --- /dev/null +++ b/host/lib/usrp_clock/octoclock/kk_ihex.h @@ -0,0 +1,191 @@ +/* + * kk_ihex.h: A simple library for reading and writing the Intel HEX + * or IHEX format. Intended mainly for embedded systems, and thus + * somewhat optimised for size at the expense of error handling and + * generality. + * + * USAGE + * ----- + * + * The library has been split into read and write parts, which use a + * common data structure (`struct ihex_state`), but each can be used + * independently. Include the header `kk_ihex_read.h` for reading, and/or + * the header `kk_ihex_write.h` for writing (and link with their respective + * object files). Both can be used simultaneously - this header defines + * the shared data structures and definitions. + * + * + * READING INTEL HEX DATA + * ---------------------- + * + * To read data in the Intel HEX format, you must perform the actual reading + * of bytes using other means (e.g., stdio). The bytes read must then be + * passed to `ihex_read_byte` and/or `ihex_read_bytes`. The reading functions + * will then call `ihex_data_read`, at which stage the `struct ihex_state` + * structure will contain the data along with its address. See the header + * `kk_ihex_read.h` for details and example implementation of `ihex_data_read`. + * + * The sequence to read data in IHEX format is: + * struct ihex_state ihex; + * ihex_begin_read(&ihex); + * ihex_read_bytes(&ihex, my_input_bytes, length_of_my_input_bytes); + * ihex_end_read(&ihex); + * + * + * WRITING BINARY DATA AS INTEL HEX + * -------------------------------- + * + * In order to write out data, the `ihex_write_at_address` or + * `ihex_write_at_segment` functions are used to set the data location, + * and then the binary bytes are written with `ihex_write_byte` and/or + * `ihex_write_bytes`. The writing functions will then call the function + * `ihex_flush_buffer` whenever the internal write buffer needs to be + * cleared - it is up to the caller to provide an implementation of + * `ihex_flush_buffer` to do the actual writing. See the header + * `kk_ihex_write.h` for details and an example implementation. + * + * See the declaration further down for an example implementation. + * + * The sequence to write data in IHEX format is: + * struct ihex_state ihex; + * ihex_init(&ihex); + * ihex_write_at_address(&ihex, 0); + * ihex_write_bytes(&ihex, my_data, length_of_my_data); + * ihex_end_write(&ihex); + * + * For outputs larger than 64KiB, 32-bit linear addresses are output. Normally + * the initial linear extended address record of zero is NOT written - it can + * be forced by setting `ihex->flags |= IHEX_FLAG_ADDRESS_OVERFLOW` before + * writing the first byte. + * + * Gaps in the data may be created by calling `ihex_write_at_address` with the + * new starting address without calling `ihex_end_write` in between. + * + * + * The same `struct ihex_state` may be used either for reading or writing, + * but NOT both at the same time. Furthermore, a global output buffer is + * used for writing, i.e., multiple threads must not write simultaneously + * (but multiple writes may be interleaved). + * + * + * CONSERVING MEMORY + * ----------------- + * + * For memory-critical use, you can save additional memory by defining + * `IHEX_LINE_MAX_LENGTH` as something less than 255. Note, however, that + * this limit affects both reading and writing, so the resulting library + * will be unable to read lines with more than this number of data bytes. + * That said, I haven't encountered any IHEX files with more than 32 + * data bytes per line. For write only there is no reason to define the + * maximum as greater than the line length you'll actually be writing, + * e.g., 32 or 16. + * + * If the write functionality is only occasionally used, you can provide + * your own buffer for the duration by defining `IHEX_EXTERNAL_WRITE_BUFFER` + * and providing a `char *ihex_write_buffer` which points to valid storage + * for at least `IHEX_WRITE_BUFFER_LENGTH` characters from before the first + * call to any IHEX write function to until after the last. + * + * If you are doing both reading and writing, you can define the maximum + * output length separately as `IHEX_MAX_OUTPUT_LINE_LENGTH` - this will + * decrease the write buffer size, but `struct ihex_state` will still + * use the larger `IHEX_LINE_MAX_LENGTH` for its data storage. + * + * You can also save a few additional bytes by disabling support for + * segmented addresses, by defining `IHEX_DISABLE_SEGMENTS`. Both the + * read and write modules need to be build with the same option, as the + * resulting data structures will not be compatible otherwise. To be honest, + * this is a fairly pointless optimisation. + * + * + * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/ + * Provided with absolutely no warranty, use at your own risk only. + * Use and distribute freely, mark modified copies as such. + */ + +#ifndef KK_IHEX_H +#define KK_IHEX_H + +#define KK_IHEX_VERSION "2015-08-10" + +#include <stdint.h> + +#ifdef IHEX_USE_STDBOOL +#include <stdbool.h> +typedef bool ihex_bool_t; +#else +typedef uint_fast8_t ihex_bool_t; +#endif + +typedef uint_least32_t ihex_address_t; +typedef uint_least16_t ihex_segment_t; +typedef int ihex_count_t; + +// Maximum number of data bytes per line (applies to both reading and +// writing!); specify 255 to support reading all possible lengths. Less +// can be used to limit memory footprint on embedded systems, e.g., +// most programs with IHEX output use 32. +#ifndef IHEX_LINE_MAX_LENGTH +#define IHEX_LINE_MAX_LENGTH 255 +#endif + +enum ihex_flags { + IHEX_FLAG_ADDRESS_OVERFLOW = 0x80 // 16-bit address overflow +}; +typedef uint8_t ihex_flags_t; + +typedef struct ihex_state { + ihex_address_t address; +#ifndef IHEX_DISABLE_SEGMENTS + ihex_segment_t segment; +#endif + ihex_flags_t flags; + uint8_t line_length; + uint8_t length; + uint8_t data[IHEX_LINE_MAX_LENGTH + 1]; +} kk_ihex_t; + +enum ihex_record_type { + IHEX_DATA_RECORD, + IHEX_END_OF_FILE_RECORD, + IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD, + IHEX_START_SEGMENT_ADDRESS_RECORD, + IHEX_EXTENDED_LINEAR_ADDRESS_RECORD, + IHEX_START_LINEAR_ADDRESS_RECORD +}; +typedef uint8_t ihex_record_type_t; + +#ifndef IHEX_DISABLE_SEGMENTS + +// Resolve segmented address (if any). It is the author's recommendation that +// segmented addressing not be used (and indeed the write function of this +// library uses linear 32-bit addressing unless manually overridden). +// +#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address + (((ihex_address_t)((ihex)->segment)) << 4)) +// +// Note that segmented addressing with the above macro is not strictly adherent +// to the IHEX specification, which mandates that the lowest 16 bits of the +// address and the index of the data byte must be added modulo 64K (i.e., +// at 16 bits precision with wraparound) and the segment address only added +// afterwards. +// +// To implement fully correct segmented addressing, compute the address +// of _each byte_ with its index in `data` as follows: +// +#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((((ihex)->address + (byte_index)) & 0xFFFFU) + (((ihex_address_t)((ihex)->segment)) << 4)) + +#else // IHEX_DISABLE_SEGMENTS: + +#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address) +#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((ihex)->address + (byte_index)) + +#endif + +// The newline string (appended to every output line, e.g., "\r\n") +#ifndef IHEX_NEWLINE_STRING +#define IHEX_NEWLINE_STRING "\n" +#endif + +// See kk_ihex_read.h and kk_ihex_write.h for function declarations! + +#endif // !KK_IHEX_H diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_license.txt b/host/lib/usrp_clock/octoclock/kk_ihex_license.txt new file mode 100644 index 000000000..530f413e3 --- /dev/null +++ b/host/lib/usrp_clock/octoclock/kk_ihex_license.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013-2015 Kimmo Kulovesi + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_read.c b/host/lib/usrp_clock/octoclock/kk_ihex_read.c new file mode 100644 index 000000000..964cdd165 --- /dev/null +++ b/host/lib/usrp_clock/octoclock/kk_ihex_read.c @@ -0,0 +1,261 @@ +/* + * kk_ihex_read.c: A simple library for reading the Intel HEX (IHEX) format. + * + * See the header `kk_ihex.h` for instructions. + * + * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/ + * Provided with absolutely no warranty, use at your own risk only. + * Use and distribute freely, mark modified copies as such. + * + * Modifications Copyright (c) 2015 National Instruments Corp. + */ + +#include "kk_ihex_read.h" + +#include <stdio.h> +#include <stdlib.h> + +#define IHEX_START ':' + +#define AUTODETECT_ADDRESS (~0UL) + +#define ADDRESS_HIGH_MASK ((ihex_address_t) 0xFFFF0000U) + +enum ihex_read_state { + READ_WAIT_FOR_START = 0, + READ_COUNT_HIGH = 1, + READ_COUNT_LOW, + READ_ADDRESS_MSB_HIGH, + READ_ADDRESS_MSB_LOW, + READ_ADDRESS_LSB_HIGH, + READ_ADDRESS_LSB_LOW, + READ_RECORD_TYPE_HIGH, + READ_RECORD_TYPE_LOW, + READ_DATA_HIGH, + READ_DATA_LOW +}; + +#define IHEX_READ_RECORD_TYPE_MASK 0x07 +#define IHEX_READ_STATE_MASK 0x78 +#define IHEX_READ_STATE_OFFSET 3 + +void +ihex_begin_read (struct ihex_state * const ihex) { + ihex->address = 0; +#ifndef IHEX_DISABLE_SEGMENTS + ihex->segment = 0; +#endif + ihex->flags = 0; + ihex->line_length = 0; + ihex->length = 0; +} + +void +ihex_read_at_address (struct ihex_state * const ihex, ihex_address_t address) { + ihex_begin_read(ihex); + ihex->address = address; +} + +#ifndef IHEX_DISABLE_SEGMENTS +void +ihex_read_at_segment (struct ihex_state * const ihex, ihex_segment_t segment) { + ihex_begin_read(ihex); + ihex->segment = segment; +} +#endif + +void +ihex_end_read (struct ihex_state * const ihex, FILE* outfile) { + uint_fast8_t type = ihex->flags & IHEX_READ_RECORD_TYPE_MASK; + uint_fast8_t sum; + if ((sum = ihex->length) == 0 && type == IHEX_DATA_RECORD) { + return; + } + { + // compute and validate checksum + const uint8_t * const eptr = ihex->data + sum; + const uint8_t *r = ihex->data; + sum += type + (ihex->address & 0xFFU) + ((ihex->address >> 8) & 0xFFU); + while (r != eptr) { + sum += *r++; + } + sum = (~sum + 1U) ^ *eptr; // *eptr is the received checksum + } + if (ihex_data_read(ihex, type, sum, outfile)) { + if (type == IHEX_EXTENDED_LINEAR_ADDRESS_RECORD) { + ihex->address &= 0xFFFFU; + ihex->address |= (((ihex_address_t) ihex->data[0]) << 24) | + (((ihex_address_t) ihex->data[1]) << 16); +#ifndef IHEX_DISABLE_SEGMENTS + } else if (type == IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD) { + ihex->segment = (ihex_segment_t) ((ihex->data[0] << 8) | ihex->data[1]); +#endif + } + } + ihex->length = 0; + ihex->flags = 0; +} + +void +ihex_read_byte (struct ihex_state * const ihex, const char byte, FILE* outfile) { + uint_fast8_t b = (uint_fast8_t) byte; + uint_fast8_t len = ihex->length; + uint_fast8_t state = (ihex->flags & IHEX_READ_STATE_MASK); + ihex->flags ^= state; // turn off the old state + state >>= IHEX_READ_STATE_OFFSET; + + if (b >= '0' && b <= '9') { + b -= '0'; + } else if (b >= 'A' && b <= 'F') { + b -= 'A' - 10; + } else if (b >= 'a' && b <= 'f') { + b -= 'a' - 10; + } else if (b == IHEX_START) { + // sync to a new record at any state + state = READ_COUNT_HIGH; + goto end_read; + } else { + // ignore unknown characters (e.g., extra whitespace) + goto save_read_state; + } + + if (!(++state & 1)) { + // high nybble, store temporarily at end of data: + b <<= 4; + ihex->data[len] = b; + } else { + // low nybble, combine with stored high nybble: + b = (ihex->data[len] |= b); + switch (state >> 1) { + default: + // remain in initial state while waiting for : + return; + case (READ_COUNT_LOW >> 1): + // data length + ihex->line_length = b; +#if IHEX_LINE_MAX_LENGTH < 255 + if (b > IHEX_LINE_MAX_LENGTH) { + ihex_end_read(ihex); + return; + } +#endif + break; + case (READ_ADDRESS_MSB_LOW >> 1): + // high byte of 16-bit address + ihex->address &= ADDRESS_HIGH_MASK; // clear the 16-bit address + ihex->address |= ((ihex_address_t) b) << 8U; + break; + case (READ_ADDRESS_LSB_LOW >> 1): + // low byte of 16-bit address + ihex->address |= (ihex_address_t) b; + break; + case (READ_RECORD_TYPE_LOW >> 1): + // record type + if (b & ~IHEX_READ_RECORD_TYPE_MASK) { + // skip unknown record types silently + return; + } + ihex->flags = (ihex->flags & ~IHEX_READ_RECORD_TYPE_MASK) | b; + break; + case (READ_DATA_LOW >> 1): + if (len < ihex->line_length) { + // data byte + ihex->length = len + 1; + state = READ_DATA_HIGH; + goto save_read_state; + } + // end of line (last "data" byte is checksum) + state = READ_WAIT_FOR_START; + end_read: + ihex_end_read(ihex, outfile); + } + } +save_read_state: + ihex->flags |= state << IHEX_READ_STATE_OFFSET; +} + +void +ihex_read_bytes (struct ihex_state * ihex, + const char * data, + ihex_count_t count, + FILE* outfile) { + while (count > 0) { + ihex_read_byte(ihex, *data++, outfile); + --count; + } +} + +ihex_bool_t +ihex_data_read (struct ihex_state *ihex, + ihex_record_type_t type, + ihex_bool_t error, + FILE* outfile) { + unsigned long line_number = 1L; + unsigned long file_position = 0L; + unsigned long address_offset = 0L; + bool debug_enabled = false; + + if (error) { + (void) fprintf(stderr, "Checksum error on line %lu\n", line_number); + return false; + } + if ((error = (ihex->length < ihex->line_length))) { + (void) fprintf(stderr, "Line length error on line %lu\n", line_number); + return false; + } + if (!outfile) { + (void) fprintf(stderr, "Excess data after end of file record\n"); + return false; + } + if (type == IHEX_DATA_RECORD) { + unsigned long address = (unsigned long) IHEX_LINEAR_ADDRESS(ihex); + if (address < address_offset) { + if (address_offset == AUTODETECT_ADDRESS) { + // autodetect initial address + address_offset = address; + if (debug_enabled) { + (void) fprintf(stderr, "Address offset: 0x%lx\n", + address_offset); + } + } else { + (void) fprintf(stderr, "Address underflow on line %lu\n", + line_number); + return false; + } + } + address -= address_offset; + if (address != file_position) { + if (debug_enabled) { + (void) fprintf(stderr, + "Seeking from 0x%lx to 0x%lx on line %lu\n", + file_position, address, line_number); + } + if (outfile == stdout || fseek(outfile, (long) address, SEEK_SET)) { + if (file_position < address) { + // "seek" forward in stdout by writing NUL bytes + do { + (void) fputc('\0', outfile); + } while (++file_position < address); + } else { + perror("fseek"); + return false; + } + } + file_position = address; + } + if (!fwrite(ihex->data, ihex->length, 1, outfile)) { + perror("fwrite"); + return false; + } + file_position += ihex->length; + } else if (type == IHEX_END_OF_FILE_RECORD) { + if (debug_enabled) { + (void) fprintf(stderr, "%lu bytes written\n", file_position); + } + if (outfile != stdout) { + (void) fclose(outfile); + } + outfile = NULL; + } + return true; +} diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_read.h b/host/lib/usrp_clock/octoclock/kk_ihex_read.h new file mode 100644 index 000000000..5e210fddb --- /dev/null +++ b/host/lib/usrp_clock/octoclock/kk_ihex_read.h @@ -0,0 +1,119 @@ +/* + * kk_ihex_read.h: A simple library for reading Intel HEX data. See + * the accompanying kk_ihex_write.h for IHEX write support. + * + * + * READING INTEL HEX DATA + * ---------------------- + * + * To read data in the Intel HEX format, you must perform the actual reading + * of bytes using other means (e.g., stdio). The bytes read must then be + * passed to `ihex_read_byte` and/or `ihex_read_bytes`. The reading functions + * will then call `ihex_data_read`, at which stage the `struct ihex_state` + * structure will contain the data along with its address. See below for + * details and example implementation of `ihex_data_read`. + * + * The sequence to read data in IHEX format is: + * struct ihex_state ihex; + * ihex_begin_read(&ihex); + * ihex_read_bytes(&ihex, my_input_bytes, length_of_my_input_bytes); + * ihex_end_read(&ihex); + * + * + * CONSERVING MEMORY + * ----------------- + * + * For memory-critical use, you can save additional memory by defining + * `IHEX_LINE_MAX_LENGTH` as something less than 255. Note, however, that + * this limit affects both reading and writing, so the resulting library + * will be unable to read lines with more than this number of data bytes. + * That said, I haven't encountered any IHEX files with more than 32 + * data bytes per line. + * + * + * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/ + * Provided with absolutely no warranty, use at your own risk only. + * Use and distribute freely, mark modified copies as such. + * + * Modifications Copyright (c) 2015 National Instruments Corp. + */ + +#ifndef KK_IHEX_READ_H +#define KK_IHEX_READ_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "kk_ihex.h" + +#include <stdio.h> + +// Begin reading at address 0 +void ihex_begin_read(struct ihex_state * const ihex); + +// Begin reading at `address` (the lowest 16 bits of which will be ignored); +// this is required only if the high bytes of the 32-bit starting address +// are not specified in the input data and they are non-zero +void ihex_read_at_address(struct ihex_state *ihex, + ihex_address_t address); + +// Read a single character +void ihex_read_byte(struct ihex_state *ihex, char chr, FILE* outfile); + +// Read `count` bytes from `data` +void ihex_read_bytes(struct ihex_state * ihex, + const char * data, + ihex_count_t count, + FILE* outfile); + +// End reading (may call `ihex_data_read` if there is data waiting) +void ihex_end_read(struct ihex_state *ihex, FILE* outfile); + +// Called when a complete line has been read, the record type of which is +// passed as `type`. The `ihex` structure will have its fields `data`, +// `line_length`, `address`, and `segment` set appropriately. In case +// of reading an `IHEX_EXTENDED_LINEAR_ADDRESS_RECORD` or an +// `IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD` the record's data is not +// yet parsed - it will be parsed into the `address` or `segment` field +// only if `ihex_data_read` returns `true`. This allows manual handling +// of extended addresses by parsing the `ihex->data` bytes. +// +// Possible error cases include checksum mismatch (which is indicated +// as an argument), and excessive line length (in case this has been +// compiled with `IHEX_LINE_MAX_LENGTH` less than 255) which is indicated +// by `line_length` greater than `length`. Unknown record types and +// other erroneous data is usually silently ignored by this minimalistic +// parser. (It is recommended to compute a hash over the complete data +// once received and verify that against the source.) +// +// Example implementation: +// +// ihex_bool_t ihex_data_read(struct ihex_state *ihex, +// ihex_record_type_t type, +// ihex_bool_t error) { +// error = error || (ihex->length < ihex->line_length); +// if (type == IHEX_DATA_RECORD && !error) { +// (void) fseek(outfile, IHEX_LINEAR_ADDRESS(ihex), SEEK_SET); +// (void) fwrite(ihex->data, 1, ihex->length, outfile); +// } else if (type == IHEX_END_OF_FILE_RECORD) { +// (void) fclose(outfile); +// } +// return !error; +// } +// +ihex_bool_t ihex_data_read(struct ihex_state *ihex, + ihex_record_type_t type, + ihex_bool_t checksum_mismatch, + FILE* outfile); + +// Begin reading at `segment`; this is required only if the initial segment +// is not specified in the input data and it is non-zero. +// +#ifndef IHEX_DISABLE_SEGMENTS +void ihex_read_at_segment(struct ihex_state *ihex, ihex_segment_t segment); +#endif + +#ifdef __cplusplus +} +#endif +#endif // !KK_IHEX_READ_H diff --git a/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp index e8c50e029..8b47da7e5 100644 --- a/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp +++ b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp @@ -15,16 +15,9 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include <cstring> -#include <fstream> -#include <iostream> -#include <string> - -#include <boost/cstdint.hpp> -#include <boost/filesystem.hpp> -#include <boost/format.hpp> -#include <boost/lexical_cast.hpp> -#include <boost/thread.hpp> +#include "octoclock_impl.hpp" +#include "common.h" +#include "kk_ihex_read.h" #include <uhd/device.hpp> #include <uhd/image_loader.hpp> @@ -35,9 +28,17 @@ #include <uhd/utils/paths.hpp> #include <uhd/utils/static.hpp> -#include "octoclock_impl.hpp" -#include "common.h" -#include "ihexcvt.hpp" +#include <boost/cstdint.hpp> +#include <boost/filesystem.hpp> +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/thread.hpp> + +#include <cstdio> +#include <cstring> +#include <fstream> +#include <iostream> +#include <string> namespace fs = boost::filesystem; using namespace uhd; @@ -74,13 +75,32 @@ static void octoclock_calculate_crc(octoclock_session_t &session){ session.crc ^= temp_image[i]; for(boost::uint8_t j = 0; j < 8; ++j){ if(session.crc & 1) session.crc = (session.crc >> 1) ^ 0xA001; - else session.crc = (session.crc >> 1); - } - } + else session.crc = (session.crc >> 1); + } + } ifile.close(); } +static void octoclock_convert_ihex(octoclock_session_t &session){ + struct ihex_state ihex; + ihex_count_t count; + char buf[256]; + FILE* infile = fopen(session.given_filepath.c_str(), "r"); + FILE* outfile = fopen(session.actual_filepath.c_str(), "w"); + uint64_t line_number = 1; + + ihex_begin_read(&ihex); + while(fgets(buf, 256, infile)){ + count = ihex_count_t(strlen(buf)); + ihex_read_bytes(&ihex, buf, count, outfile); + line_number += (count && buf[count - 1] == '\n'); + } + ihex_end_read(&ihex, outfile); // Closes outfile + + (void)fclose(infile); +} + static void octoclock_validate_firmware_image(octoclock_session_t &session){ if(not fs::exists(session.given_filepath)){ throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\"") @@ -98,7 +118,7 @@ static void octoclock_validate_firmware_image(octoclock_session_t &session){ % time_spec_t::get_system_time().get_full_secs()) ).string(); - Hex2Bin(session.given_filepath.c_str(), session.actual_filepath.c_str(), false); + octoclock_convert_ihex(session); session.from_hex = true; } else throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .hex or .bin."))); diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 06bee48cd..4f56dad0d 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -84,9 +84,11 @@ IF(ENABLE_OCTOCLOCK) SET(octoclock_burner_sources octoclock_firmware_burner.cpp - ihexcvt.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp_clock/octoclock/kk_ihex_read.c ) + 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}) UHD_INSTALL(TARGETS octoclock_firmware_burner RUNTIME DESTINATION ${RUNTIME_DIR} COMPONENT utilities) diff --git a/host/utils/ihexcvt.cpp b/host/utils/ihexcvt.cpp deleted file mode 100644 index 0605ee61c..000000000 --- a/host/utils/ihexcvt.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* IHexCvt - Intel HEX File <=> Binary Converter (C++) - Copyright (C) 2014 Ali Nakisaee - -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 2 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, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.*/ - -//Include needed stuff from C++ -#include <iostream> -#include <fstream> -#include <string> - -//... and also from C -#include <stdio.h> - -#include <boost/filesystem.hpp> - -#include <uhd/exception.hpp> -#include "ihexcvt.hpp" - -//Avoid repeating 'std::': -using namespace std; - -//The following function reads a hexadecimal number from a text file. -template <class T> -static bool ReadValueFromHex(ifstream& InputFile, T& outCh, unsigned char* ApplyChecksum) -{ - char V, L; - T X = 0; - outCh = 0; - - //Get the characters one by one. - //Remember: These values are big-endian. - //Remember: Every two hex characters (0-9/A-F) indicate ONE byte. - for (size_t i = 0; i < 2 * sizeof(T); i++) - { - InputFile.get( V ); - if (InputFile.fail()) - return false; - - X <<= 4; - if (V >= '0' && V <= '9') - L = (V - '0'); - else if (V >= 'a' && V <= 'f') - L = (V - 'a' + 10); - else if (V >= 'A' && V <= 'F') - L = (V - 'A' + 10); - else - return false; - X |= L; - - //Apply this character to the checksum - if (ApplyChecksum && i % 2 == 1) *ApplyChecksum += X & 0xFF; - } - - //Return... - outCh = X; - return true; -} - -//The following function writes a hexadecimal number from a text file. -template <class T> -static bool WriteHexValue(ofstream& OutFile, T Value, unsigned char* CalcChecksum) -{ - unsigned char V0 = 0; - char C; - - //Remember: These values are big-endian. - for (size_t i = 0; i < sizeof(T); i++) - { - //Get byte #i from the value. - V0 = (Value >> ((sizeof(T) - i - 1) * 8)) & 0xFF; - - //Extract the high nibble (4-bits) - if ((V0 & 0xF0) <= 0x90) - C = (V0 >> 4) + '0'; - else - C = (V0 >> 4) + ('A' - 10); - OutFile.put( C ); - - //Extract the low nibble (4-bits) - if ((V0 & 0xF) <= 0x9) - C = (V0 & 0xF) + '0'; - else - C = (V0 & 0xF) + ('A' - 10); - OutFile.put( C ); - - //Calculate the checksum - if (CalcChecksum) *CalcChecksum += V0; - } - return true; -} - -//Skip any incoming whitespaces -static void SkipWhitespace(ifstream& InputFile) -{ - for (;;) - { - char C; - InputFile.get(C); - if (InputFile.eof() || InputFile.fail()) break; - if (!(C == '\n' || C == '\r' || C == ' ' || C == '\t' || C == '\v')) - { - InputFile.putback(C); - break; - } - } -} - -//The function responsible for conversion from HEX files to BINary. -void Hex2Bin(const char* SrcName, const char* DstName, bool IgnoreChecksum) -{ - ifstream Src(SrcName); - if (Src.bad()) - { - throw uhd::runtime_error("Could not convert Intel .hex file to binary."); - } - - ofstream Dst(DstName, ios_base::binary); - if (Dst.bad()) - { - throw uhd::runtime_error("Could not convert Intel .hex file to binary."); - } - - char Ch; - int LineIdx = 1; - - unsigned char ByteCount; - unsigned short AddressLow; - unsigned short Extra; - unsigned long ExtraL; - unsigned long AddressOffset = 0; - unsigned char RecordType; - unsigned char Data[255]; - unsigned char CurChecksum; - unsigned char FileChecksum; - bool EOFMarker = false; - bool EOFWarn = false; - - for ( ;; ) - { - Src.get(Ch); - if (Src.eof()) - break; - if (EOFMarker && !EOFWarn) - { - throw uhd::runtime_error("Could not convert Intel .hex file to binary."); - } - if (Ch != ':') goto genericErr; - - CurChecksum = 0; - if (!ReadValueFromHex( Src, ByteCount, &CurChecksum )) goto genericErr; - if (!ReadValueFromHex( Src, AddressLow, &CurChecksum )) goto genericErr; - if (!ReadValueFromHex( Src, RecordType, &CurChecksum )) goto genericErr; - - switch (RecordType) - { - case 0x00: //Data record - for (int i = 0; i < ByteCount; i++) - if (!ReadValueFromHex( Src, Data[i], &CurChecksum )) goto genericErr; - break; - case 0x01: //End Marker - if ( ByteCount != 0 ) - { - goto onErrExit; - } - EOFMarker = true; - break; - case 0x02: //Extended Segment Address - if ( ByteCount != 2 || AddressLow != 0 ) - { - goto onErrExit; - } - if (!ReadValueFromHex( Src, Extra, &CurChecksum )) goto genericErr; - AddressOffset = (unsigned long)Extra << 4; - break; - case 0x03: //Start Segment Address - if ( ByteCount != 4 || AddressLow != 0 ) - { - goto onErrExit; - } - if (!ReadValueFromHex( Src, ExtraL, &CurChecksum )) goto genericErr; - break; - case 0x04: //Extended Linear Address - if ( ByteCount != 2 || AddressLow != 0 ) - { - goto onErrExit; - } - if (!ReadValueFromHex( Src, Extra, &CurChecksum )) goto genericErr; - AddressOffset = (unsigned long)Extra << 16; - break; - case 0x05: //Start Linear Address - if ( ByteCount != 4 || AddressLow != 0 ) - { - goto onErrExit; - } - if (!ReadValueFromHex( Src, ExtraL, &CurChecksum )) goto genericErr; - break; - } - - //Verify checksum - CurChecksum = (~(CurChecksum & 0xFF) + 1) & 0xFF; - if (!ReadValueFromHex( Src, FileChecksum, NULL )) goto genericErr; - if (CurChecksum != FileChecksum) - { - if (!IgnoreChecksum) goto onErrExit; - } - - //Put Data - if (RecordType == 0x00) - { - Dst.seekp( AddressLow + AddressOffset ); - for (int i = 0; i < ByteCount; i++) - { - Dst.put( Data[i] ); - } - } - - //Skip any white space - SkipWhitespace( Src ); - - LineIdx++; - } - - Dst << flush; - Dst.close(); - - return; - -genericErr: - throw uhd::runtime_error("Invalid Intel .hex file detected."); - -onErrExit: - Dst.close(); - Src.close(); - boost::filesystem::remove(DstName); - throw uhd::runtime_error("Could not convert Intel .hex file to binary."); -} diff --git a/host/utils/ihexcvt.hpp b/host/utils/ihexcvt.hpp deleted file mode 100644 index d577ece1f..000000000 --- a/host/utils/ihexcvt.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright 2014 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 _IHEXCVT_HPP_ -#define _IHEXCVT_HPP_ - -void Hex2Bin(const char* SrcName, const char* DstName, bool IgnoreChecksum); - -#endif /* _IHEXCVT_HPP_ */ diff --git a/host/utils/octoclock_firmware_burner.cpp b/host/utils/octoclock_firmware_burner.cpp index 1ec77d0c8..272394860 100644 --- a/host/utils/octoclock_firmware_burner.cpp +++ b/host/utils/octoclock_firmware_burner.cpp @@ -42,7 +42,7 @@ #include <uhd/utils/safe_main.hpp> #include "../lib/usrp_clock/octoclock/common.h" -#include "ihexcvt.hpp" +#include "kk_ihex_read.h" #define MAX_FIRMWARE_SIZE 1024*120 #define BLOCK_SIZE 256 @@ -291,6 +291,25 @@ void finalize(udp_simple::sptr udp_transport){ } } +void octoclock_convert_ihex(const std::string &hex_path, const std::string &bin_path){ + struct ihex_state ihex; + ihex_count_t count; + char buf[256]; + FILE* infile = fopen(hex_path.c_str(), "r"); + FILE* outfile = fopen(bin_path.c_str(), "w"); + uint64_t line_number = 1; + + ihex_begin_read(&ihex); + while(fgets(buf, 256, infile)){ + count = ihex_count_t(strlen(buf)); + ihex_read_bytes(&ihex, buf, count, outfile); + line_number += (count && buf[count - 1] == '\n'); + } + ihex_end_read(&ihex, outfile); // Closes outfile + + (void)fclose(infile); +} + int UHD_SAFE_MAIN(UHD_UNUSED(int argc), UHD_UNUSED(char *argv[])){ std::string ip_addr; @@ -352,7 +371,7 @@ int UHD_SAFE_MAIN(UHD_UNUSED(int argc), UHD_UNUSED(char *argv[])){ //Write firmware .bin file to temporary directory fs::path temp_bin = fs::path(fs::path(get_tmp_path()) / str(boost::format("octoclock_fw_%d.bin") % time_spec_t::get_system_time().get_full_secs())); - Hex2Bin(firmware_path.c_str(), temp_bin.string().c_str(), false); + octoclock_convert_ihex(firmware_path, temp_bin.string()); actual_firmware_path = temp_bin.string(); } |