Browse Source

Support flashing MCUs from the command line

The command-line tool is now mostly functional.  It can't verify yet
though.
Clara Hobbs 6 years ago
parent
commit
80046ef07c
3 changed files with 86 additions and 14 deletions
  1. 31
    7
      alpaca_isp/__init__.py
  2. 11
    4
      alpaca_isp/chips.py
  3. 44
    3
      alpaca_isp/lpc.py

+ 31
- 7
alpaca_isp/__init__.py View File

@@ -19,6 +19,8 @@ __version__ = "0"
19 19
 import argparse
20 20
 import sys
21 21
 
22
+import intelhex
23
+
22 24
 from alpaca_isp.chips import chips
23 25
 from alpaca_isp.exceptions import *
24 26
 from alpaca_isp.lpc import *
@@ -45,7 +47,12 @@ def create_lpc(tty, baudrate=DEFAULT_BAUDRATE, clock=DEFAULT_CLOCK,
45 47
         lpc.close()
46 48
         if verbose:
47 49
             print(" failed")
48
-        sys.exit(1)
50
+        raise
51
+    except KeyboardInterrupt:
52
+        lpc.close()
53
+        if verbose:
54
+            print()
55
+        raise
49 56
     if verbose:
50 57
         print()
51 58
 
@@ -81,29 +88,46 @@ def main():
81 88
     parser.add_argument("--try-sync", type=int,
82 89
             help="maximum number of tries to synchronize with the "
83 90
             "microcontroller")
84
-    parser.add_argument("--control", action="store_true",
91
+    parser.add_argument("-n", "--control", action="store_true",
85 92
             help="control RS232 lines to enter ISP mode (/RST = DTR, /ISP = "
86 93
             "RTS)")
87
-    parser.add_argument("--verify", action="store_true",
94
+    parser.add_argument("-r", "--verify", action="store_true",
88 95
             help="verify that the data were written correctly after flashing")
89 96
 
90 97
     # Parse arguments
91 98
     args = parser.parse_args()
92 99
 
93 100
     # Open the LPC
94
-    lpc = create_lpc(args.tty, baudrate=args.baudrate, clock=args.clock_khz,
95
-            timeout=args.timeout, control=args.control, try_sync=args.try_sync,
96
-            verbose=True)
101
+    try:
102
+        lpc = create_lpc(args.tty, baudrate=args.baudrate,
103
+                clock=args.clock_khz, timeout=args.timeout,
104
+                control=args.control, try_sync=args.try_sync, verbose=True)
105
+    except (RecvTimeout, KeyboardInterrupt):
106
+        sys.exit(1)
97 107
 
98 108
     # Unlock the chip
99 109
     lpc.unlock()
100 110
 
101 111
     # Erase the flash if we've been asked to
102 112
     if args.erase:
113
+        print("Erasing all flash sectors")
103 114
         lpc.prepare_write()
104 115
         lpc.erase()
105 116
 
106
-    # TODO: write the file to flash
117
+    # Write the files to flash
118
+    ih = intelhex.IntelHex()
119
+    for f in args.file:
120
+        ih.fromfile(f, format="hex")
121
+
122
+    try:
123
+        lpc.flash_hex(ih, verbose=True)
124
+    except ISPError as e:
125
+        if e.args[0] == ReturnCode.CODE_READ_PROTECTION_ENABLED:
126
+            print("Error: code read protection is enabled")
127
+        sys.exit(2)
128
+
129
+    # TODO: Verify that the flash has been written correctly if we've been
130
+    # asked to
107 131
 
108 132
     # Start the program if we haven't been asked not to
109 133
     if not args.no_start:

+ 11
- 4
alpaca_isp/chips.py View File

@@ -37,16 +37,23 @@ class Chip(namedtuple("Chip", [
37 37
         "sectors"])):
38 38
     __slots__ = ()
39 39
 
40
+    @property
41
+    def sector_segments(self):
42
+        """Returns a list of (start, end) tuples for each flash sector"""
43
+        sector_segments = []
44
+        for i in range(len(self.sectors)):
45
+            sector_segments.append((sum(self.sectors[:i]),
46
+                sum(self.sectors[:i+1])))
47
+
48
+        return sector_segments
49
+
40 50
     def sectors_used(self, segments):
41 51
         """Returns a list of sectors used by the given memory segments
42 52
 
43 53
         segments: a list of (start, end) tuples representing segments of used
44 54
             memory
45 55
         """
46
-        sector_segments = []
47
-        for i in range(len(self.sectors)):
48
-            sector_segments.append((sum(self.sectors[:i]),
49
-                sum(self.sectors[:i+1])))
56
+        sector_segments = self.sector_segments
50 57
 
51 58
         s = set()
52 59
         for dseg in segments:

+ 44
- 3
alpaca_isp/lpc.py View File

@@ -17,7 +17,6 @@ import struct
17 17
 import time
18 18
 from enum import Enum
19 19
 
20
-import intelhex
21 20
 import serial
22 21
 
23 22
 from alpaca_isp.exceptions import *
@@ -340,10 +339,52 @@ class LPC:
340 339
         self._send_command("S {} {}\r\n".format(start, count).encode("utf-8"))
341 340
         return int(self._readline())
342 341
 
343
-    def flash_hex(self, ihex):
344
-        """Write an IntelHex object to flash"""
342
+    def flash_hex(self, ihex, verbose=False):
343
+        """Write an IntelHex object to flash
344
+
345
+        Only the sectors that have any data in ihex are changed.  These sectors
346
+        are erased and written with the new data, destroying anything that was
347
+        in the sector before.
348
+        """
349
+        # Assert that we have enough RAM to hold the largest flash sector
350
+        assert (((self._chip.ram_start + 0x400*self._chip.ram)
351
+                - self._chip.ram_base) >= max(self._chip.sectors))
352
+        # Assert that the largest sector is no larger than max_copy
353
+        assert (self._chip.max_copy >= max(self._chip.sectors))
354
+        # NOTE: of course both of these assertions wouldn't be necessary if we
355
+        # were more careful below, but for the chips I'm using they hold, so
356
+        # I'm in no rush to make this perfect.
357
+
358
+        # Get the sectors we're rewriting
345 359
         sectors_used = self._chip.sectors_used(ihex.segments())
346 360
 
361
+        # Erase the sectors that aren't already blank
362
+        for sector in sectors_used:
363
+            if self.blank_check(sector) is not None:
364
+                if verbose:
365
+                    print("Erasing sector {}".format(sector))
366
+                self.prepare_write(sector)
367
+                self.erase(sector)
368
+
369
+        # Write sectors in reversed order to make sure sector 0 is invalid
370
+        # until the end
371
+        ss = self._chip.sector_segments
372
+        for sector in reversed(sectors_used):
373
+            if verbose:
374
+                print("Writing sector {}".format(sector))
375
+            # Get the data we'll be writing
376
+            secdat = ihex.tobinstr(start=ss[sector][0], end=ss[sector][1]-1)
377
+
378
+            # Write data to RAM
379
+            bs = 0x100
380
+            for i in range(0, len(secdat), bs):
381
+                self.write_ram(self._chip.ram_base+i, secdat[i:i+bs])
382
+
383
+            # Copy RAM to flash
384
+            self.prepare_write(sector)
385
+            self.copy_ram_to_flash(ss[sector][0], self._chip.ram_base,
386
+                    len(secdat))
387
+
347 388
 
348 389
 class LPC8xx(LPC):
349 390
     """Interface for LPC8xx in-system programming"""

Loading…
Cancel
Save