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
 import argparse
19
 import argparse
20
 import sys
20
 import sys
21
 
21
 
22
+import intelhex
23
+
22
 from alpaca_isp.chips import chips
24
 from alpaca_isp.chips import chips
23
 from alpaca_isp.exceptions import *
25
 from alpaca_isp.exceptions import *
24
 from alpaca_isp.lpc import *
26
 from alpaca_isp.lpc import *
45
         lpc.close()
47
         lpc.close()
46
         if verbose:
48
         if verbose:
47
             print(" failed")
49
             print(" failed")
48
-        sys.exit(1)
50
+        raise
51
+    except KeyboardInterrupt:
52
+        lpc.close()
53
+        if verbose:
54
+            print()
55
+        raise
49
     if verbose:
56
     if verbose:
50
         print()
57
         print()
51
 
58
 
81
     parser.add_argument("--try-sync", type=int,
88
     parser.add_argument("--try-sync", type=int,
82
             help="maximum number of tries to synchronize with the "
89
             help="maximum number of tries to synchronize with the "
83
             "microcontroller")
90
             "microcontroller")
84
-    parser.add_argument("--control", action="store_true",
91
+    parser.add_argument("-n", "--control", action="store_true",
85
             help="control RS232 lines to enter ISP mode (/RST = DTR, /ISP = "
92
             help="control RS232 lines to enter ISP mode (/RST = DTR, /ISP = "
86
             "RTS)")
93
             "RTS)")
87
-    parser.add_argument("--verify", action="store_true",
94
+    parser.add_argument("-r", "--verify", action="store_true",
88
             help="verify that the data were written correctly after flashing")
95
             help="verify that the data were written correctly after flashing")
89
 
96
 
90
     # Parse arguments
97
     # Parse arguments
91
     args = parser.parse_args()
98
     args = parser.parse_args()
92
 
99
 
93
     # Open the LPC
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
     # Unlock the chip
108
     # Unlock the chip
99
     lpc.unlock()
109
     lpc.unlock()
100
 
110
 
101
     # Erase the flash if we've been asked to
111
     # Erase the flash if we've been asked to
102
     if args.erase:
112
     if args.erase:
113
+        print("Erasing all flash sectors")
103
         lpc.prepare_write()
114
         lpc.prepare_write()
104
         lpc.erase()
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
     # Start the program if we haven't been asked not to
132
     # Start the program if we haven't been asked not to
109
     if not args.no_start:
133
     if not args.no_start:

+ 11
- 4
alpaca_isp/chips.py View File

37
         "sectors"])):
37
         "sectors"])):
38
     __slots__ = ()
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
     def sectors_used(self, segments):
50
     def sectors_used(self, segments):
41
         """Returns a list of sectors used by the given memory segments
51
         """Returns a list of sectors used by the given memory segments
42
 
52
 
43
         segments: a list of (start, end) tuples representing segments of used
53
         segments: a list of (start, end) tuples representing segments of used
44
             memory
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
         s = set()
58
         s = set()
52
         for dseg in segments:
59
         for dseg in segments:

+ 44
- 3
alpaca_isp/lpc.py View File

17
 import time
17
 import time
18
 from enum import Enum
18
 from enum import Enum
19
 
19
 
20
-import intelhex
21
 import serial
20
 import serial
22
 
21
 
23
 from alpaca_isp.exceptions import *
22
 from alpaca_isp.exceptions import *
340
         self._send_command("S {} {}\r\n".format(start, count).encode("utf-8"))
339
         self._send_command("S {} {}\r\n".format(start, count).encode("utf-8"))
341
         return int(self._readline())
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
         sectors_used = self._chip.sectors_used(ihex.segments())
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
 class LPC8xx(LPC):
389
 class LPC8xx(LPC):
349
     """Interface for LPC8xx in-system programming"""
390
     """Interface for LPC8xx in-system programming"""

Loading…
Cancel
Save