summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Schodet2010-06-17 00:44:56 +0200
committerNicolas Schodet2010-06-20 19:09:44 +0200
commit6691b6920cb99c6f4d7129fcd5758ab2828073e2 (patch)
tree0e996542f5ecb126d739169c16df94fcfc66bfca
parent9c9b1e87e7bca6ba0c5c101b0b76a514a820ea7c (diff)
add bootloader control program
-rw-r--r--tools/bootloader.py187
1 files changed, 187 insertions, 0 deletions
diff --git a/tools/bootloader.py b/tools/bootloader.py
new file mode 100644
index 0000000..7432191
--- /dev/null
+++ b/tools/bootloader.py
@@ -0,0 +1,187 @@
+# binwatch - Tiny binary wristwatch. {{{
+#
+# Copyright (C) 2010 Nicolas Schodet
+#
+# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Contact :
+# Web: http://ni.fr.eu.org/
+# Email: <nico at ni.fr.eu.org>
+# }}} */
+"""binwatch bootloader control."""
+import struct
+import dev2.twi
+
+class ProtocolError (RuntimeError):
+ pass
+
+class Bootloader:
+
+ INFO = 0
+ START = 1
+ READ = 2
+ PAGE_WRITE = 3
+ READ_FUSES = 4
+
+ def __init__ (self, address, serial):
+ """Initialise and set address and serial object to use."""
+ self.address = address
+ self.twi = dev2.twi.Twi (serial)
+
+ def _command (self, command, fmt, rfmt, *params):
+ """Send a command and check response.
+
+ - command: command to send.
+ - fmt: command format (see struct).
+ - rfmt: response format.
+ - params: command parameters.
+
+ Will return unpacked response or None on error."""
+ s = struct.pack ('<B' + fmt, command, *params)
+ n = self.twi.send (self.address, s)
+ if n != len (s):
+ raise ProtocolError ("send command fail")
+ rlen = struct.calcsize ('<B' + rfmt)
+ r = self.twi.recv (self.address, rlen)
+ if len (r) != rlen:
+ raise ProtocolError ("receive ack fail")
+ rl = struct.unpack ('<B' + rfmt, r)
+ if rl[0] != command:
+ raise ProtocolError ("received nack")
+ return rl[1:]
+
+ def info (self):
+ """Check the bootloader signature, return signature, page size and flash size."""
+ r = self._command (self.INFO, '', '12sBH')
+ if r[0] != 'bwbootloader' and r[0] != 'bwbootstrapp':
+ raise ProtocolError ("bad signature")
+ return r
+
+ def start (self):
+ """Start application."""
+ self._command (self.START, '', '')
+
+ def read (self, address, size):
+ """Read data from flash."""
+ r = self._command (self.READ, 'HB', 'H%ds' % size, address, size)
+ if r[0] != address:
+ raise ProtocolError ("read fail")
+ return r[1]
+
+ def page_write (self, address, size, data):
+ """Write one flash page."""
+ r = self._command (self.PAGE_WRITE, 'HB%ds' % size, 'H', address, size,
+ data)
+ if r[0] != address:
+ raise ProtocolError ("page write fail")
+
+ def read_fuses (self):
+ """Read fuses, return them in a dict."""
+ r = self._command (self.READ_FUSES, '', 'BBB')
+ return dict (lfuse = r[0], hfuse = r[1], efuse = r[2])
+
+def flash (bootloader, data, verify = True, progress = None):
+ """Flash given data using bootloader."""
+ # Get bootloader parameters.
+ _, page_size, flash_size = bootloader.info ()
+ # Pad data to page size.
+ pad = (page_size - len (data) % page_size) % page_size
+ data = data + '\xff' * pad
+ # Check binary size.
+ if len (data) > flash_size:
+ raise RuntimeError ("flash too small")
+ # Program pages.
+ for page in xrange (0, len (data), page_size):
+ bootloader.page_write (page, page_size, data[page:page + page_size])
+ if progress:
+ progress ('program', page + page_size, len (data))
+ # Verify.
+ if verify:
+ for page in xrange (0, len (data), page_size):
+ r = bootloader.read (page, page_size)
+ if page == 0:
+ match = r[2:] == data[page + 2:page + page_size]
+ else:
+ match = r == data[page:page + page_size]
+ if not match:
+ raise RuntimeError (
+ "verification mismatch at address 0x04x" % page)
+ if progress:
+ progress ('verify', page + page_size, len (data))
+
+def dump (bootloader, progress = None):
+ """Dump all flash data using bootloader."""
+ # Get bootloader parameters.
+ _, page_size, flash_size = bootloader.info ()
+ data = [ ]
+ for page in xrange (0, flash_size, page_size):
+ r = bootloader.read (page, page_size)
+ data.append (r)
+ if progress:
+ progress ('dump', page + page_size, flash_size)
+ return ''.join (data)
+
+def progress (step, current, total):
+ """Print current progress."""
+ print "\r%s: %3d%%" % (step, current * 100 / total),
+ if current == total:
+ print ''
+ import sys
+ sys.stdout.flush ()
+
+def command (args = None):
+ """Implement command line interface."""
+ # Parse options.
+ import optparse
+ parser = optparse.OptionParser ()
+ parser.add_option ('-d', '--dump',
+ help = "dump flash to FILE", metavar = 'FILE')
+ parser.add_option ('-f', '--flash',
+ help = "flash from FILE", metavar = 'FILE')
+ parser.add_option ('-s', '--start', action = 'store_true',
+ help = "start application program")
+ parser.add_option ('-i', '--info', action = 'store_true',
+ help = "get page and flash size")
+ parser.add_option ('-t', '--tty',
+ help = "tty connected to dev2", metavar = 'TTY')
+ parser.add_option ('-a', '--address', type = 'int', default = 0xb8,
+ help = "define bootloader slave address", metavar = 'ADDR')
+ (options, extra) = parser.parse_args (args = args)
+ if extra:
+ parser.error ("extra unused arguments")
+ if options.tty is None:
+ parser.error ("no tty to open")
+ # Apply.
+ import serial
+ try:
+ s = serial.Serial (options.tty)
+ bl = Bootloader (options.address, s)
+ if options.info:
+ print bl.info ()
+ if options.dump is not None:
+ f = open (options.dump, 'w')
+ f.write (dump (bl, progress = progress))
+ f.close ()
+ if options.flash is not None:
+ flash (bl, open (options.flash).read (), progress = progress)
+ if options.start:
+ bl.start ()
+ except Exception, e:
+ import sys
+ print >> sys.stderr, e.message
+ sys.exit (1)
+
+if __name__ == '__main__':
+ command ()