changeset 244:58155c7c4a8e

Add hexutil
author Windel Bouwman
date Wed, 24 Jul 2013 19:47:13 +0200
parents ef683881c64e
children 66912720d712
files python/hexfile.py python/hexutil.py python/st-flash.py python/testhexfile.py
diffstat 4 files changed, 153 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/python/hexfile.py	Tue Jul 23 16:50:02 2013 +0200
+++ b/python/hexfile.py	Wed Jul 24 19:47:13 2013 +0200
@@ -1,4 +1,5 @@
 import os
+import struct
 
 class HexFileException(Exception):
     pass
@@ -13,11 +14,30 @@
     crc = sum(nums)
     if (crc & 0xFF) != 0:
         raise HexFileException('crc incorrect')
-    address = nums[1] * 256 + nums[2]
+    address = struct.unpack('>H', nums[1:3])[0]
     typ = nums[3]
     data = nums[4:-1]
     return (address, typ, data)
 
+def makeHexLine(address, typ, data=bytes()):
+    bytecount = len(data)
+    nums = bytearray()
+    nums.append(bytecount)
+    nums.extend(struct.pack('>H', address))
+    nums.append(typ)
+    nums.extend(data)
+    crc = sum(nums)
+    crc = ((~crc) + 1) & 0xFF
+    nums.append(crc)
+    line = ':' + ''.join(['{:02X}'.format(b) for b in nums])
+    return line
+
+def chunks(data, csize=16):
+    idx = 0
+    while idx < len(data):
+        s = min(len(data) - idx, csize)
+        yield data[idx:idx+s]
+        idx += s
 
 class HexFile:
     """ Represents an intel hexfile """
@@ -65,27 +85,48 @@
             self.regions.append(HexFileRegion(startAddress, bytes(curData)))
 
     def __repr__(self):
-        i = []
-        i.append(super().__repr__())
-        i.append('Start address {0}'.format(hex(self.startAddress)))
+        return 'Hexfile with {} regions'.format(len(self.regions))
+
+    def dump(self):
+        print(self)
         for r in self.regions:
-            i.append(str(r))
-        return os.linesep.join(i)
+            print(r)
+
+    def __eq__(self, other):
+        regions = self.regions
+        oregions = other.regions
+        if len(regions) != len(oregions):
+            return False
+        return all(rs == ro for rs, ro in zip(regions, oregions))
+
+    def addRegion(self, address, data):
+        r = HexFileRegion(address, data)
+        # TODO: check touches
+        self.regions.append(r)
+        self.regions.sort(key=lambda r: r.address)
 
     def save(self, f):
         for r in self.regions:
             offset = 0
-            while offset < len(r.data):
-                f.write('a')
-                offset += 16
-
+            for chunk in chunks(r.data):
+                print(makeHexLine(r.address + offset, 0x0, bytes(r.data[offset:offset+16])), file=f)
+                offset += len(chunk)
+        print(makeHexLine(0, 0x1, bytes()), file=f)
 
 class HexFileRegion:
-   def __init__(self, address, data = bytes()):
-      self.address = address
-      self.data = data
-   def __repr__(self):
-      return 'Region at 0x{0:X} of {1} bytes'.format(self.address, len(self.data))
+    def __init__(self, address, data = bytes()):
+        self.address = address
+        self.data = data
+
+    def __repr__(self):
+        return 'Region at 0x{0:X} of {1} bytes'.format(self.address, len(self.data))
+
+    def __eq__(self, other):
+        return (self.address, self.data) == (other.address, other.data)
+
+    @property
+    def EndAddress(self):
+        return self.address + len(self.data)
 
 
 if __name__ == '__main__':
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/python/hexutil.py	Wed Jul 24 19:47:13 2013 +0200
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+
+import sys
+import argparse
+from hexfile import HexFile
+
+def hex2int(s):
+    if s.startswith('0x'):
+        s = s[2:]
+        return int(s, 16)
+    raise ValueError('Hexadecimal value must begin with 0x')
+
+parser = argparse.ArgumentParser(
+   description='hexfile manipulation tool by Windel Bouwman')
+subparsers = parser.add_subparsers(title='commands', 
+         description='possible commands', dest='command')
+
+p = subparsers.add_parser('info', help='dump info about hexfile')
+p.add_argument('hexfile', type=argparse.FileType('r'))
+
+p = subparsers.add_parser('new', help='create empty hexfile')
+p.add_argument('hexfile', type=argparse.FileType('x'))
+
+p = subparsers.add_parser('add', help='add binary data from file to hexfile')
+p.add_argument('hexfile', type=argparse.FileType('r+'), help="the hexfile to add the data to")
+p.add_argument('address', type=hex2int, help="hex address")
+p.add_argument('datafile', type=argparse.FileType('rb'), help='binary file to add')
+
+def main(args):
+    if args.command == 'info':
+        hf = HexFile()
+        hf.load(args.hexfile)
+        print(hf)
+        for region in hf.regions:
+            print(region)
+    elif args.command == 'new':
+        hf = HexFile()
+        hf.save(args.hexfile)
+    elif args.command == 'add':
+        hf = HexFile()
+        hf.load(args.hexfile)
+        data = args.datafile.read()
+        hf.addRegion(args.address, data)
+    else:
+        raise NotImplementedError()
+
+if __name__ == '__main__':
+    args = parser.parse_args()
+    if not args.command:
+        parser.print_usage()
+        sys.exit(1)
+    main(args)
+
--- a/python/st-flash.py	Tue Jul 23 16:50:02 2013 +0200
+++ b/python/st-flash.py	Wed Jul 24 19:47:13 2013 +0200
@@ -23,6 +23,9 @@
 writeparser.add_argument('filename', type=argparse.FileType('rb'))
 writeparser.add_argument('address', type=hex2int)
 
+hexwriteparser = subparsers.add_parser('hexwrite', help='write hexfile to flash')
+hexwriteparser.add_argument('filename', type=argparse.FileType('r'))
+
 verifyparser = subparsers.add_parser('verify', help='verify flash contents')
 verifyparser.add_argument('filename', type=argparse.FileType('rb'))
 verifyparser.add_argument('address', type=hex2int)
--- a/python/testhexfile.py	Tue Jul 23 16:50:02 2013 +0200
+++ b/python/testhexfile.py	Wed Jul 24 19:47:13 2013 +0200
@@ -1,39 +1,66 @@
 import unittest
 import io
-import hexfile
+from hexfile import HexFile, HexFileException
 
 
 class testHexFile(unittest.TestCase):
-    def setUp(self):
-        pass
-
+    def saveload(self, hf):
+        f = io.StringIO()
+        hf.save(f)
+        hf2 = HexFile()
+        hf2.load(io.StringIO(f.getvalue()))
+        self.assertEqual(hf, hf2)
+        
     def testSave(self):
-        hf = hexfile.HexFile()
-        f = io.StringIO()
-        region = hexfile.HexFileRegion(0x8000, bytes.fromhex('aabbcc'))
-        hf.regions.append(region)
-        hf.save(f)
+        hf = HexFile()
+        hf.addRegion(0x8000, bytes.fromhex('aabbcc'))
+        self.saveload(hf)
+
+    def testEqual(self):
+        hf1 = HexFile()
+        hf2 = HexFile()
+        hf1.addRegion(10, bytes.fromhex('aabbcc'))
+        hf2.addRegion(10, bytes.fromhex('aabbcc'))
+        self.assertEqual(hf1, hf2)
+
+    def testNotEqual(self):
+        hf1 = HexFile()
+        hf2 = HexFile()
+        hf1.addRegion(10, bytes.fromhex('aabbcc'))
+        hf2.addRegion(10, bytes.fromhex('aabbdc'))
+        self.assertNotEqual(hf1, hf2)
+        
+    def testNotEqual2(self):
+        hf1 = HexFile()
+        hf2 = HexFile()
+        hf1.addRegion(10, bytes.fromhex('aabbcc'))
+        hf2.addRegion(10, bytes.fromhex('aabbcc'))
+        hf2.addRegion(22, bytes.fromhex('aabbcc'))
+        self.assertNotEqual(hf1, hf2)
 
     def testLoad(self):
-        hf = hexfile.HexFile()
+        hf = HexFile()
         dummyhex = """
         :01400000aa15
         """
         f = io.StringIO(dummyhex)
         hf.load(f)
+        self.assertEqual(1, len(hf.regions))
+        self.assertEqual(0x4000, hf.regions[0].address)
+        self.assertSequenceEqual(bytes.fromhex('aa'), hf.regions[0].data)
 
     def testIncorrectCrc(self):
-        hf = hexfile.HexFile()
+        hf = HexFile()
         txt = ":01400000aabb"
         f = io.StringIO(txt)
-        with self.assertRaises(hexfile.HexFileException):
+        with self.assertRaisesRegex(HexFileException, 'crc'):
             hf.load(f)
 
     def testIncorrectLength(self):
-        hf = hexfile.HexFile()
+        hf = HexFile()
         txt = ":0140002200aabb"
         f = io.StringIO(txt)
-        with self.assertRaises(hexfile.HexFileException):
+        with self.assertRaisesRegex(HexFileException, 'count'):
             hf.load(f)
 
 if __name__ == '__main__':