tinyocd 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tinyocd/tinyocd.py ADDED
@@ -0,0 +1,2467 @@
1
+ #!/usr/bin/env python3
2
+ # ===================================================================================
3
+ # Project: tinyOCD - Programming Tool for STM32, PY32 and SAMD Microcontrollers
4
+ # via CMSIS-DAP (DAPLink), ST-Link or J-Link
5
+ # Version: v1.0
6
+ # Year: 2026
7
+ # Author: Stefan Wagner
8
+ # Github: https://github.com/wagiminator
9
+ # License: MIT License
10
+ # ===================================================================================
11
+ #
12
+ # Description:
13
+ # ------------
14
+ # Simple Python tool for flashing ARM Cortex-M microcontrollers via SWD using a
15
+ # CMSIS-DAP v1/v2 (DAPLink), ST-Link V2/V2-1/V3 or SEGGER J-Link programmer.
16
+ # Programmer and chip are detected automatically. The flash memory is programmed
17
+ # by a small generic flash loader that runs in the RAM of the target.
18
+ # Currently supports:
19
+ # - STM32: C0, F0, F1, F2, F3, F4, F7, G0, G4, H5, H7, L0, L1, L4, L4+, L5, U0,
20
+ # U3, U5, WB, WBA, WL
21
+ # - PUYA: PY32F002A, PY32F003, PY32F030, PY32F002B, PY32F071, PY32F072
22
+ # (and compatible, e.g. AIR001)
23
+ # - ATMEL: ATSAMD09, ATSAMD10, ATSAMD11, ATSAMD20, ATSAMD21, ATSAMDA1, ATSAML21,
24
+ # ATSAML22, ATSAMC20, ATSAMC21, ATSAMD51, ATSAME51, ATSAME53, ATSAME54
25
+ #
26
+ # References:
27
+ # -----------
28
+ # tinyOCD is inspired by and uses information from:
29
+ # - pyOCD: https://github.com/pyocd/pyOCD
30
+ # - OpenOCD: https://openocd.org
31
+ # - Black Magic Debug: https://github.com/blackmagic-debug/blackmagic
32
+ # - probe-rs: https://github.com/probe-rs/probe-rs
33
+ # - PUYA PY32 SDK: https://github.com/IOsetting/py32f0-template
34
+ #
35
+ # Dependencies:
36
+ # -------------
37
+ # - pyusb
38
+ # - libusb-package (optional, recommended on Windows)
39
+ # - hidapi (optional, for CMSIS-DAP v1 HID probes on Windows and macOS)
40
+ #
41
+ # Operating Instructions:
42
+ # -----------------------
43
+ # You need to install PyUSB to use tinyOCD. Install it via "pip install pyusb".
44
+ #
45
+ # Linux users need permission to access the programmer. Run:
46
+ # echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="0483", MODE="666"' | sudo tee /etc/udev/rules.d/99-tinyocd.rules
47
+ # echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1366", MODE="666"' | sudo tee -a /etc/udev/rules.d/99-tinyocd.rules
48
+ # echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="0d28", MODE="666"' | sudo tee -a /etc/udev/rules.d/99-tinyocd.rules
49
+ # echo 'KERNEL=="hidraw*", ATTRS{idVendor}=="0d28", MODE="666"' | sudo tee -a /etc/udev/rules.d/99-tinyocd.rules
50
+ # sudo udevadm control --reload-rules
51
+ # (add further lines for other CMSIS-DAP vendor IDs, e.g. 2e8a, c251, 1a86)
52
+ #
53
+ # On Windows, ST-Link (with ST driver) and CMSIS-DAP v2 (WinUSB) normally work out
54
+ # of the box with libusb. A J-Link must use the WinUSB driver (J-Link Configurator:
55
+ # "Use WinUSB") or a libusb driver installed with Zadig (https://zadig.akeo.ie/).
56
+ #
57
+ # Connect the programmer to your PC and to the SWD interface of your MCU
58
+ # (SWDIO, SWCLK, GND, optionally NRST and VCC).
59
+ # Run:
60
+ # - tinyocd.py [-h] [-p {auto,cmsis-dap,stlink,jlink}] [-s SPEED] [-u] [-l] [-e]
61
+ # [-B] [-G] [-R] [-f FLASH] [-t]
62
+ # -h, --help show help message and exit
63
+ # -p, --probe select programmer type (default: auto)
64
+ # -s SPEED, --speed SPEED SWD clock in kHz (default: 1000)
65
+ # -u, --unlock unlock chip (remove read protection, erases chip)
66
+ # -l, --lock lock chip (set read protection)
67
+ # -e, --erase perform a whole chip erase (implied with -f)
68
+ # -B, --bootpin make BOOT0 pin usable for embedded bootloader
69
+ # -G, --pingpio make NRST pin a GPIO pin
70
+ # -R, --pinreset make NRST pin a reset pin
71
+ # -f FLASH, --flash FLASH write BIN file to flash and verify
72
+ # -t, --list list supported chips
73
+ #
74
+ # - Example:
75
+ # python3 tinyocd.py -f firmware.bin
76
+
77
+
78
+ import os
79
+ import sys
80
+ import time
81
+ import struct
82
+ import argparse
83
+
84
+ try:
85
+ import usb.core
86
+ import usb.util
87
+ except ImportError:
88
+ usb = None
89
+
90
+
91
+ # ===================================================================================
92
+ # Main Function
93
+ # ===================================================================================
94
+
95
+ def _main():
96
+ # Parse command line arguments
97
+ parser = argparse.ArgumentParser(description='Minimal command line interface for tinyOCD')
98
+ parser.add_argument('-p', '--probe', default='auto', choices=['auto', 'cmsis-dap', 'stlink', 'jlink'],
99
+ help='select programmer type (default: auto)')
100
+ parser.add_argument('-s', '--speed', type=int, default=1000, help='SWD clock in kHz (default: 1000)')
101
+ parser.add_argument('-u', '--unlock', action='store_true', help='unlock chip (remove read protection, erases chip)')
102
+ parser.add_argument('-l', '--lock', action='store_true', help='lock chip (set read protection)')
103
+ parser.add_argument('-e', '--erase', action='store_true', help='perform a whole chip erase (implied with -f)')
104
+ parser.add_argument('-B', '--bootpin', action='store_true', help='make BOOT0 pin usable for embedded bootloader')
105
+ parser.add_argument('-G', '--pingpio', action='store_true', help='make NRST pin a GPIO pin')
106
+ parser.add_argument('-R', '--pinreset', action='store_true', help='make NRST pin a reset pin')
107
+ parser.add_argument('-f', '--flash', help='write BIN file to flash and verify')
108
+ parser.add_argument('-t', '--list', action='store_true', help='list supported chips')
109
+ args = parser.parse_args(sys.argv[1:])
110
+
111
+ # List supported chips
112
+ if args.list:
113
+ print_supported_chips()
114
+ sys.exit(0)
115
+
116
+ # Check arguments
117
+ if not any( (args.unlock, args.lock, args.erase, args.bootpin, args.pingpio, args.pinreset, args.flash) ):
118
+ print('No arguments - no action!')
119
+ sys.exit(0)
120
+ if args.pingpio and args.pinreset:
121
+ sys.stderr.write('ERROR: -G and -R cannot be used together!\n')
122
+ sys.exit(1)
123
+ if args.speed < 1 or args.speed > 50000:
124
+ sys.stderr.write('ERROR: SWD clock must be 1 - 50000 kHz!\n')
125
+ sys.exit(1)
126
+
127
+ # Read binary file
128
+ data = None
129
+ if args.flash is not None:
130
+ try:
131
+ with open(args.flash, 'rb') as f: data = f.read()
132
+ except Exception as ex:
133
+ sys.stderr.write('ERROR: %s!\n' % str(ex))
134
+ sys.exit(1)
135
+ if len(data) == 0:
136
+ sys.stderr.write('ERROR: BIN file is empty!\n')
137
+ sys.exit(1)
138
+
139
+ # Establish connection to programmer
140
+ try:
141
+ print('Searching for programmer ...')
142
+ probe = find_probe(args.probe)
143
+ print('SUCCESS: Found %s.' % probe.name)
144
+ if probe.voltage:
145
+ print('Target voltage: %.2f V' % probe.voltage)
146
+ except Exception as ex:
147
+ sys.stderr.write('ERROR: %s!\n' % str(ex))
148
+ sys.exit(1)
149
+
150
+ # Performing actions
151
+ ocd = None
152
+ try:
153
+ print('Connecting to MCU ...')
154
+ ocd = Target(probe, args.speed)
155
+ ocd.connect()
156
+ if ocd.chip.flash_size:
157
+ print('SUCCESS: Connected to %s with %d bytes of flash.' % (ocd.chip.name, ocd.chip.flash_size))
158
+ else:
159
+ print('SUCCESS: Connected to %s.' % ocd.chip.name)
160
+ if ocd.locked:
161
+ print('Chip is read protected (locked).')
162
+
163
+ # Check option support before doing anything
164
+ if args.bootpin and not ocd.chip.has_bootpin:
165
+ raise Exception('BOOT0 pin configuration is not supported on this chip')
166
+ if (args.pingpio or args.pinreset) and not ocd.chip.has_nrst:
167
+ raise Exception('NRST pin configuration is not supported on this chip')
168
+ if args.lock and not ocd.chip.has_lock:
169
+ raise Exception('Read protection is not supported on this chip')
170
+ if ocd.locked and not args.unlock and (args.erase or data is not None or args.bootpin or args.pingpio or args.pinreset):
171
+ raise Exception('Chip is locked, use -u to unlock it (erases the chip)')
172
+ if data is not None and not ocd.locked:
173
+ ocd.check_image(data)
174
+
175
+ # Unlock chip
176
+ if args.unlock:
177
+ print('Unlocking chip ...')
178
+ if ocd.unlock():
179
+ print('SUCCESS: Chip is unlocked.')
180
+ if data is not None:
181
+ ocd.check_image(data)
182
+
183
+ # Perform chip erase
184
+ if args.erase or data is not None:
185
+ print('Performing whole chip erase ...')
186
+ ocd.erase()
187
+ print('SUCCESS: Chip is erased.')
188
+
189
+ # Flash binary file
190
+ if data is not None:
191
+ print('Flashing %s to %s ...' % (args.flash, ocd.chip.name))
192
+ ocd.flash_data(data)
193
+ print('Verifying ...')
194
+ ocd.verify_data(data)
195
+ print('SUCCESS: %d bytes written and verified.' % len(data))
196
+
197
+ # Configure option bytes
198
+ if args.bootpin:
199
+ print('Making BOOT0 pin usable for embedded bootloader ...')
200
+ ocd.set_bootpin()
201
+ print('SUCCESS: BOOT0 pin is now usable.')
202
+ if args.pingpio:
203
+ print('Configuring NRST pin as GPIO pin ...')
204
+ ocd.set_nrst(gpio=True)
205
+ print('SUCCESS: NRST is now a GPIO pin.')
206
+ if args.pinreset:
207
+ print('Configuring NRST pin as reset pin ...')
208
+ ocd.set_nrst(gpio=False)
209
+ print('SUCCESS: NRST is now a reset pin.')
210
+
211
+ # Lock chip
212
+ if args.lock:
213
+ print('Locking chip ...')
214
+ ocd.lock()
215
+ print('SUCCESS: Chip is locked.')
216
+
217
+ ocd.exit()
218
+
219
+ except Exception as ex:
220
+ sys.stderr.write('ERROR: %s!\n' % str(ex))
221
+ try:
222
+ if ocd is not None:
223
+ ocd.exit(run=False)
224
+ else:
225
+ probe.close()
226
+ except:
227
+ pass
228
+ sys.exit(1)
229
+
230
+ print('DONE.')
231
+ sys.exit(0)
232
+
233
+
234
+ # ===================================================================================
235
+ # Error Classes
236
+ # ===================================================================================
237
+
238
+ class ProbeError(Exception):
239
+ pass
240
+
241
+ class TransferFault(Exception):
242
+ pass
243
+
244
+
245
+ # ===================================================================================
246
+ # USB Helper Functions
247
+ # ===================================================================================
248
+
249
+ # Get libusb backend (libusb-package on Windows if available)
250
+ def usb_backend():
251
+ try:
252
+ import libusb_package
253
+ return libusb_package.get_libusb1_backend()
254
+ except Exception:
255
+ return None
256
+
257
+ # Find all USB devices
258
+ def usb_devices():
259
+ if usb is None:
260
+ raise ProbeError('PyUSB is not installed, run "pip install pyusb"')
261
+ try:
262
+ return list(usb.core.find(find_all=True, backend=usb_backend()))
263
+ except usb.core.NoBackendError:
264
+ raise ProbeError('No libusb backend found, install libusb (or "pip install libusb-package")')
265
+
266
+ # Get USB string descriptor (None if not readable)
267
+ def usb_string(dev, index):
268
+ if not index:
269
+ return None
270
+ try:
271
+ return usb.util.get_string(dev, index)
272
+ except Exception:
273
+ return None
274
+
275
+ # Find programmer
276
+ def find_probe(kind='auto'):
277
+ errors = list()
278
+ devices = usb_devices()
279
+ for dev in devices:
280
+ candidates = list()
281
+ if kind in ('auto', 'stlink') and dev.idVendor == STLINK_VID and dev.idProduct in STLINK_PIDS:
282
+ candidates.append(STLink)
283
+ if kind in ('auto', 'jlink') and dev.idVendor == JLINK_VID:
284
+ candidates.append(JLink)
285
+ if kind in ('auto', 'cmsis-dap'):
286
+ candidates.append(CMSISDAP.from_usb)
287
+ for probe_type in candidates:
288
+ try:
289
+ probe = probe_type(dev)
290
+ if probe is not None:
291
+ return probe
292
+ except usb.core.USBError as ex:
293
+ errors.append('%04x:%04x: %s' % (dev.idVendor, dev.idProduct, ex))
294
+ except ProbeError as ex:
295
+ errors.append(str(ex))
296
+ if kind in ('auto', 'cmsis-dap'):
297
+ probe = CMSISDAP.from_hidapi()
298
+ if probe is not None:
299
+ return probe
300
+ if errors:
301
+ raise ProbeError('No usable programmer found (%s)' % '; '.join(errors))
302
+ raise ProbeError('No programmer found (check connection, driver and USB access permissions)')
303
+
304
+
305
+ # ===================================================================================
306
+ # Probe Base Class (memory access interface)
307
+ # ===================================================================================
308
+
309
+ class Probe:
310
+ name = 'probe'
311
+ voltage = None
312
+
313
+ # Must be implemented: connect(speed_khz) -> DPIDR, reconnect(), close(), set_reset(asserted),
314
+ # read_mem(addr, size) -> bytes, write_mem(addr, data), read16/write16/read8/write8,
315
+ # read_ap(ap, reg), read_dp(reg)
316
+
317
+ def read32(self, addr):
318
+ return struct.unpack('<I', self.read_mem(addr, 4))[0]
319
+
320
+ def write32(self, addr, value):
321
+ self.write_mem(addr, struct.pack('<I', value & 0xFFFFFFFF))
322
+
323
+ # Split transfers at 1 KB boundaries (TAR auto-increment limit)
324
+ @staticmethod
325
+ def _chunks(addr, size, maxsize):
326
+ while size:
327
+ n = min(size, maxsize, 0x400 - (addr & 0x3FF))
328
+ yield addr, n
329
+ addr += n
330
+ size -= n
331
+
332
+
333
+ # ===================================================================================
334
+ # Raw SWD Probe (ADIv5 DP/MEM-AP access on top of DAP transfers)
335
+ # ===================================================================================
336
+
337
+ DP_DPIDR = 0x00
338
+ DP_ABORT = 0x00
339
+ DP_CTRLSTAT = 0x04
340
+ DP_SELECT = 0x08
341
+ DP_RDBUFF = 0x0C
342
+ AP_CSW = 0x00
343
+ AP_TAR = 0x04
344
+ AP_DRW = 0x0C
345
+ AP_BASE = 0xF8
346
+ AP_IDR = 0xFC
347
+
348
+ ABORT_CLEAR = 0x1E # ORUNERRCLR | WDERRCLR | STKERRCLR | STKCMPCLR
349
+ CSW_DEFAULT = 0x23000000 # HPROT[1] | MasterType (debug) | reserved
350
+ CSW_INC = 0x00000010 # single address increment
351
+ CSW_SIZE = {1: 0, 2: 1, 4: 2}
352
+
353
+ class RawSWDProbe(Probe):
354
+ ORUNDETECT = 0 # overrun detection (set by probes that always do the data phase)
355
+
356
+ def __init__(self):
357
+ self._select = None
358
+ self._csw = None
359
+ self.ap = 0
360
+
361
+ # --- to be implemented by subclass ---
362
+ # _swj_sequence(nbits, data), _transfer(ops) -> list of read values
363
+ # ops: list of (apndp, rnw, addr, value)
364
+
365
+ def _write_block(self, ap, reg, words):
366
+ self._transfer([(ap, 0, reg, w) for w in words])
367
+
368
+ def _read_block(self, ap, reg, count):
369
+ return self._transfer([(ap, 1, reg, 0)] * count)
370
+
371
+ # --- SWD line handling ---
372
+ def _swd_switch(self):
373
+ self._swj_sequence(56, b'\xff' * 7) # line reset
374
+ self._swj_sequence(16, b'\x9e\xe7') # JTAG to SWD sequence
375
+ self._swj_sequence(56, b'\xff' * 7) # line reset
376
+ self._swj_sequence(16, b'\x00\x00') # idle cycles
377
+
378
+ def dp_connect(self):
379
+ self._select = None
380
+ self._csw = None
381
+ dpidr = None
382
+ for _ in range(3):
383
+ try:
384
+ self._swd_switch()
385
+ dpidr = self._transfer([(0, 1, DP_DPIDR, 0)])[0]
386
+ break
387
+ except (ProbeError, TransferFault):
388
+ time.sleep(0.01)
389
+ if dpidr is None or dpidr in (0, 0xFFFFFFFF):
390
+ raise ProbeError('No target found (check SWD connection and target power)')
391
+ self._transfer([(0, 0, DP_ABORT, ABORT_CLEAR)])
392
+ self._transfer([(0, 0, DP_CTRLSTAT, 0x50000000 | self.ORUNDETECT)])
393
+ for _ in range(100):
394
+ if (self._transfer([(0, 1, DP_CTRLSTAT, 0)])[0] & 0xA0000000) == 0xA0000000:
395
+ break
396
+ time.sleep(0.001)
397
+ else:
398
+ raise ProbeError('Target debug power-up failed')
399
+ return dpidr
400
+
401
+ def clear_errors(self):
402
+ try:
403
+ self._transfer([(0, 0, DP_ABORT, ABORT_CLEAR)])
404
+ except Exception:
405
+ pass
406
+ self._select = None
407
+ self._csw = None
408
+
409
+ # --- AP/DP access ---
410
+ def _sel_ops(self, reg):
411
+ sel = (self.ap << 24) | (reg & 0xF0)
412
+ if sel != self._select:
413
+ self._select = sel
414
+ return [(0, 0, DP_SELECT, sel)]
415
+ return []
416
+
417
+ def read_dp(self, reg):
418
+ return self._transfer([(0, 1, reg, 0)])[0]
419
+
420
+ def read_ap(self, ap, reg):
421
+ old = self.ap
422
+ self.ap = ap
423
+ try:
424
+ ops = self._sel_ops(reg) + [(1, 1, reg & 0x0C, 0)]
425
+ return self._transfer(ops)[-1]
426
+ finally:
427
+ self.ap = old
428
+
429
+ def _csw_ops(self, size, inc=True):
430
+ csw = CSW_DEFAULT | CSW_SIZE[size] | (CSW_INC if inc else 0)
431
+ ops = self._sel_ops(AP_CSW)
432
+ if csw != self._csw:
433
+ self._csw = csw
434
+ ops.append((1, 0, AP_CSW, csw))
435
+ return ops
436
+
437
+ def _safe(self, func, *args):
438
+ try:
439
+ return func(*args)
440
+ except TransferFault:
441
+ self.clear_errors()
442
+ raise
443
+
444
+ # --- memory access ---
445
+ def read_mem(self, addr, size):
446
+ return self._safe(self._read_mem, addr, size)
447
+
448
+ def _read_mem(self, addr, size):
449
+ result = bytearray()
450
+ for a, n in self._chunks(addr, size, 0x400):
451
+ self._transfer(self._csw_ops(4) + [(1, 0, AP_TAR, a)])
452
+ words = self._read_block(1, AP_DRW, n // 4)
453
+ result += struct.pack('<%dI' % len(words), *words)
454
+ return bytes(result)
455
+
456
+ def write_mem(self, addr, data):
457
+ return self._safe(self._write_mem, addr, data)
458
+
459
+ def _write_mem(self, addr, data):
460
+ if len(data) == 4: # single word: one transfer incl. error check
461
+ ops = self._csw_ops(4) + [(1, 0, AP_TAR, addr), (1, 0, AP_DRW, struct.unpack('<I', data)[0]),
462
+ (0, 1, DP_CTRLSTAT, 0)]
463
+ self._check_sticky(self._transfer(ops)[-1])
464
+ return
465
+ pos = 0
466
+ for a, n in self._chunks(addr, len(data), 0x400):
467
+ words = struct.unpack('<%dI' % (n // 4), data[pos:pos + n])
468
+ self._transfer(self._csw_ops(4) + [(1, 0, AP_TAR, a)])
469
+ self._write_block(1, AP_DRW, words)
470
+ pos += n
471
+ self._check_sticky(self.read_dp(DP_CTRLSTAT))
472
+
473
+ # Write errors may be reported delayed (STICKYERR in CTRL/STAT)
474
+ def _check_sticky(self, ctrlstat):
475
+ if ctrlstat & 0x22:
476
+ raise TransferFault('memory write fault')
477
+
478
+ def _read_sized(self, addr, size):
479
+ ops = self._csw_ops(size, False) + [(1, 0, AP_TAR, addr), (1, 1, AP_DRW, 0)]
480
+ value = self._transfer(ops)[-1]
481
+ return (value >> (8 * (addr & 3))) & ((1 << (8 * size)) - 1)
482
+
483
+ def _write_sized(self, addr, size, value):
484
+ ops = self._csw_ops(size, False) + [(1, 0, AP_TAR, addr), (1, 0, AP_DRW, value << (8 * (addr & 3))),
485
+ (0, 1, DP_CTRLSTAT, 0)]
486
+ self._check_sticky(self._transfer(ops)[-1])
487
+
488
+ def read16(self, addr):
489
+ return self._safe(self._read_sized, addr, 2)
490
+
491
+ def write16(self, addr, value):
492
+ self._safe(self._write_sized, addr, 2, value & 0xFFFF)
493
+
494
+ def read8(self, addr):
495
+ return self._safe(self._read_sized, addr, 1)
496
+
497
+ def write8(self, addr, value):
498
+ self._safe(self._write_sized, addr, 1, value & 0xFF)
499
+
500
+ def reconnect(self):
501
+ return self.dp_connect()
502
+
503
+
504
+ # ===================================================================================
505
+ # CMSIS-DAP Probe (v1 HID and v2 bulk)
506
+ # ===================================================================================
507
+
508
+ class CMSISDAP(RawSWDProbe):
509
+ def __init__(self, transport, name):
510
+ super().__init__()
511
+ self.t = transport
512
+ self.packet_size = 64
513
+ self.name = name
514
+ try:
515
+ self.packet_size = max(64, struct.unpack('<H', self._info(0xFF)[:2])[0])
516
+ except Exception:
517
+ self.packet_size = 64
518
+ self.t.packet_size = self.packet_size
519
+ try:
520
+ self.fw = self._info(0x04).decode('ascii', 'ignore').strip('\x00')
521
+ except Exception:
522
+ self.fw = ''
523
+ if self.fw:
524
+ self.name += ' (firmware %s)' % self.fw
525
+
526
+ @staticmethod
527
+ def from_usb(dev):
528
+ product = usb_string(dev, dev.iProduct) or ''
529
+ try:
530
+ cfg = dev.get_active_configuration()
531
+ except Exception:
532
+ try:
533
+ cfg = dev[0] # configuration descriptor only
534
+ except Exception:
535
+ return None
536
+ hid_intf = None
537
+ for intf in cfg:
538
+ istr = usb_string(dev, intf.iInterface) or ''
539
+ if 'CMSIS-DAP' not in istr.upper() and 'CMSIS-DAP' not in product.upper():
540
+ continue
541
+ if intf.bInterfaceClass == 0xFF:
542
+ eps = [ep for ep in intf if usb.util.endpoint_type(ep.bmAttributes) == usb.util.ENDPOINT_TYPE_BULK]
543
+ if len(eps) >= 2:
544
+ return CMSISDAP(_DAPBulk(dev, intf, eps), 'CMSIS-DAP v2 "%s"' % product)
545
+ elif intf.bInterfaceClass == 0x03 and hid_intf is None:
546
+ hid_intf = intf
547
+ if hid_intf is not None:
548
+ return CMSISDAP(_DAPHidUsb(dev, hid_intf), 'CMSIS-DAP v1 "%s"' % product)
549
+ return None
550
+
551
+ @staticmethod
552
+ def from_hidapi():
553
+ try:
554
+ import hid
555
+ except Exception:
556
+ return None
557
+ for d in hid.enumerate():
558
+ product = d.get('product_string') or ''
559
+ if 'CMSIS-DAP' in product.upper():
560
+ try:
561
+ return CMSISDAP(_DAPHidApi(hid, d['path']), 'CMSIS-DAP v1 "%s"' % product)
562
+ except Exception:
563
+ continue
564
+ return None
565
+
566
+ def _cmd(self, data):
567
+ resp = self.t.xfer(bytes(data))
568
+ if len(resp) < 1 or resp[0] != data[0]:
569
+ raise ProbeError('CMSIS-DAP: unexpected response to command 0x%02x' % data[0])
570
+ return resp
571
+
572
+ def _info(self, id):
573
+ resp = self._cmd([0x00, id])
574
+ return bytes(resp[2:2 + resp[1]])
575
+
576
+ def connect(self, speed_khz):
577
+ if self._cmd([0x02, 0x01])[1] != 0x01: # DAP_Connect (SWD)
578
+ raise ProbeError('CMSIS-DAP: SWD mode not supported')
579
+ self._cmd([0x11] + list(struct.pack('<I', speed_khz * 1000))) # DAP_SWJ_Clock
580
+ self._cmd([0x04, 0x02, 0x80, 0x00, 0x00, 0x00]) # DAP_TransferConfigure
581
+ self._cmd([0x13, 0x00]) # DAP_SWD_Configure
582
+ self._speed = speed_khz
583
+ return self.dp_connect()
584
+
585
+ def close(self):
586
+ try:
587
+ self._cmd([0x03]) # DAP_Disconnect
588
+ except Exception:
589
+ pass
590
+ self.t.close()
591
+
592
+ def set_reset(self, asserted):
593
+ self._cmd([0x10, 0x00 if asserted else 0x80, 0x80, 0, 0, 0, 0]) # DAP_SWJ_Pins (nRESET)
594
+
595
+ def _swj_sequence(self, nbits, data):
596
+ self._cmd([0x12, nbits & 0xFF] + list(data))
597
+
598
+ def _check_ack(self, ack):
599
+ if ack == 1:
600
+ return
601
+ if ack == 2:
602
+ raise TransferFault('SWD WAIT timeout')
603
+ if ack == 4:
604
+ raise TransferFault('SWD FAULT')
605
+ if ack & 0x08:
606
+ raise ProbeError('SWD parity error')
607
+ raise ProbeError('SWD no response from target')
608
+
609
+ def _transfer(self, ops):
610
+ result = list()
611
+ i = 0
612
+ while i < len(ops):
613
+ req = bytearray([0x05, 0x00, 0x00])
614
+ nreads = 0
615
+ n = 0
616
+ while i + n < len(ops):
617
+ ap, rnw, reg, val = ops[i + n]
618
+ need = 1 + (0 if rnw else 4)
619
+ if len(req) + need > self.packet_size or 3 + 4 * (nreads + rnw) > self.packet_size or n >= 255:
620
+ break
621
+ req.append(ap | (rnw << 1) | (reg & 0x0C))
622
+ if not rnw:
623
+ req += struct.pack('<I', val & 0xFFFFFFFF)
624
+ nreads += rnw
625
+ n += 1
626
+ req[2] = n
627
+ resp = self._cmd(req)
628
+ if resp[1] != n:
629
+ self._check_ack(resp[2])
630
+ raise ProbeError('CMSIS-DAP transfer incomplete')
631
+ self._check_ack(resp[2])
632
+ result += struct.unpack('<%dI' % nreads, bytes(resp[3:3 + 4 * nreads]))
633
+ i += n
634
+ return result
635
+
636
+ def _write_block(self, ap, reg, words):
637
+ maxw = (self.packet_size - 5) // 4
638
+ for i in range(0, len(words), maxw):
639
+ chunk = words[i:i + maxw]
640
+ req = bytearray([0x06, 0x00]) + struct.pack('<H', len(chunk)) + bytes([ap | (reg & 0x0C)])
641
+ req += struct.pack('<%dI' % len(chunk), *chunk)
642
+ resp = self._cmd(req)
643
+ if struct.unpack('<H', bytes(resp[1:3]))[0] != len(chunk):
644
+ self._check_ack(resp[3])
645
+ raise ProbeError('CMSIS-DAP block transfer incomplete')
646
+ self._check_ack(resp[3])
647
+
648
+ def _read_block(self, ap, reg, count):
649
+ maxw = (self.packet_size - 4) // 4
650
+ result = list()
651
+ while count:
652
+ n = min(count, maxw)
653
+ req = bytearray([0x06, 0x00]) + struct.pack('<H', n) + bytes([ap | 0x02 | (reg & 0x0C)])
654
+ resp = self._cmd(req)
655
+ if struct.unpack('<H', bytes(resp[1:3]))[0] != n:
656
+ self._check_ack(resp[3])
657
+ raise ProbeError('CMSIS-DAP block transfer incomplete')
658
+ self._check_ack(resp[3])
659
+ result += struct.unpack('<%dI' % n, bytes(resp[4:4 + 4 * n]))
660
+ count -= n
661
+ return result
662
+
663
+
664
+ # CMSIS-DAP v2 transport (bulk endpoints)
665
+ class _DAPBulk:
666
+ def __init__(self, dev, intf, eps):
667
+ self.dev = dev
668
+ self.intf = intf.bInterfaceNumber
669
+ self.ep_out = [ep for ep in eps if usb.util.endpoint_direction(ep.bEndpointAddress) == usb.util.ENDPOINT_OUT][0]
670
+ self.ep_in = [ep for ep in eps if usb.util.endpoint_direction(ep.bEndpointAddress) == usb.util.ENDPOINT_IN][0]
671
+ self.packet_size = 64
672
+ usb.util.claim_interface(dev, self.intf)
673
+ try: # flush stale responses
674
+ while True:
675
+ self.ep_in.read(1024, 10)
676
+ except Exception:
677
+ pass
678
+
679
+ def xfer(self, data):
680
+ self.ep_out.write(data, 1000)
681
+ return bytes(self.ep_in.read(max(self.packet_size, 64) * 2, 2000))
682
+
683
+ def close(self):
684
+ try:
685
+ usb.util.release_interface(self.dev, self.intf)
686
+ usb.util.dispose_resources(self.dev)
687
+ except Exception:
688
+ pass
689
+
690
+
691
+ # CMSIS-DAP v1 transport via libusb (HID interrupt endpoints)
692
+ class _DAPHidUsb:
693
+ def __init__(self, dev, intf):
694
+ self.dev = dev
695
+ self.intf = intf.bInterfaceNumber
696
+ self.packet_size = 64
697
+ try:
698
+ if dev.is_kernel_driver_active(self.intf):
699
+ dev.detach_kernel_driver(self.intf)
700
+ except Exception:
701
+ pass
702
+ usb.util.claim_interface(dev, self.intf)
703
+ self.ep_in = None
704
+ self.ep_out = None
705
+ for ep in intf:
706
+ if usb.util.endpoint_direction(ep.bEndpointAddress) == usb.util.ENDPOINT_IN:
707
+ self.ep_in = ep
708
+ else:
709
+ self.ep_out = ep
710
+ if self.ep_in is None:
711
+ raise ProbeError('CMSIS-DAP v1: no IN endpoint')
712
+
713
+ def xfer(self, data):
714
+ data = bytes(data) + b'\x00' * (self.packet_size - len(data))
715
+ if self.ep_out is not None:
716
+ self.ep_out.write(data, 1000)
717
+ else:
718
+ self.dev.ctrl_transfer(0x21, 0x09, 0x0200, self.intf, data, 1000)
719
+ return bytes(self.ep_in.read(self.packet_size, 2000))
720
+
721
+ def close(self):
722
+ try:
723
+ usb.util.release_interface(self.dev, self.intf)
724
+ usb.util.dispose_resources(self.dev)
725
+ except Exception:
726
+ pass
727
+
728
+
729
+ # CMSIS-DAP v1 transport via hidapi
730
+ class _DAPHidApi:
731
+ def __init__(self, hid, path):
732
+ self.h = hid.device()
733
+ self.h.open_path(path)
734
+ self.packet_size = 64
735
+
736
+ def xfer(self, data):
737
+ data = bytes(data) + b'\x00' * (self.packet_size - len(data))
738
+ self.h.write(b'\x00' + data)
739
+ resp = self.h.read(self.packet_size, 2000)
740
+ if not resp:
741
+ raise ProbeError('CMSIS-DAP v1: no response')
742
+ return bytes(resp)
743
+
744
+ def close(self):
745
+ try:
746
+ self.h.close()
747
+ except Exception:
748
+ pass
749
+
750
+
751
+ # ===================================================================================
752
+ # SEGGER J-Link Probe (SWD via native USB protocol)
753
+ # ===================================================================================
754
+
755
+ JLINK_VID = 0x1366
756
+
757
+ class JLink(RawSWDProbe):
758
+ ORUNDETECT = 1
759
+
760
+ def __init__(self, dev):
761
+ super().__init__()
762
+ self.dev = dev
763
+ try:
764
+ dev.set_configuration()
765
+ except Exception:
766
+ pass
767
+ cfg = dev.get_active_configuration()
768
+ self.intf = None
769
+ for intf in cfg:
770
+ eps = [ep for ep in intf if usb.util.endpoint_type(ep.bmAttributes) == usb.util.ENDPOINT_TYPE_BULK]
771
+ if intf.bInterfaceClass == 0xFF and len(eps) == 2:
772
+ self.intf = intf
773
+ break
774
+ if self.intf is None:
775
+ raise ProbeError('J-Link: no vendor interface found (driver installed?)')
776
+ for ep in self.intf:
777
+ if usb.util.endpoint_direction(ep.bEndpointAddress) == usb.util.ENDPOINT_IN:
778
+ self.ep_in = ep
779
+ else:
780
+ self.ep_out = ep
781
+ usb.util.claim_interface(dev, self.intf.bInterfaceNumber)
782
+ try: # flush stale responses
783
+ while True:
784
+ self.ep_in.read(4096, 10)
785
+ except Exception:
786
+ pass
787
+ self.caps = struct.unpack('<I', self._cmd([0xE8], 4))[0] # GET_CAPS
788
+ n = struct.unpack('<H', self._cmd([0x01], 2))[0] # GET_VERSION
789
+ self.fw = self._read(n).split(b'\x00')[0].decode('ascii', 'ignore')
790
+ self.name = 'J-Link (%s)' % self.fw if self.fw else 'J-Link'
791
+ try:
792
+ hw = self._cmd([0x07], 8) # GET_HW_STATUS
793
+ self.voltage = struct.unpack('<H', hw[:2])[0] / 1000.0
794
+ except Exception:
795
+ self.voltage = None
796
+
797
+ def _write(self, data):
798
+ self.ep_out.write(bytes(data), 1000)
799
+
800
+ def _read(self, size):
801
+ result = bytearray()
802
+ rsize = size + 1 if size % self.ep_in.wMaxPacketSize == 0 else size
803
+ while len(result) < size:
804
+ result += self.ep_in.read(max(rsize - len(result), 1), 2000)
805
+ return bytes(result[:size])
806
+
807
+ def _cmd(self, data, rsize=0):
808
+ self._write(data)
809
+ return self._read(rsize) if rsize else b''
810
+
811
+ def connect(self, speed_khz):
812
+ if self.caps & (1 << 17): # SELECT_IF
813
+ self._cmd([0xC7, 0x01], 4) # select SWD
814
+ self._cmd([0x05] + list(struct.pack('<H', min(speed_khz, 0xFFFE)))) # SET_SPEED
815
+ return self.dp_connect()
816
+
817
+ def close(self):
818
+ try:
819
+ usb.util.release_interface(self.dev, self.intf.bInterfaceNumber)
820
+ usb.util.dispose_resources(self.dev)
821
+ except Exception:
822
+ pass
823
+
824
+ def set_reset(self, asserted):
825
+ self._cmd([0xDC if asserted else 0xDD]) # HW_RESET0/1
826
+
827
+ # SWD I/O: list of (direction, value) bits -> list of input bits
828
+ def _swd_io(self, bits):
829
+ n = len(bits)
830
+ nbytes = (n + 7) // 8
831
+ dirb = bytearray(nbytes)
832
+ outb = bytearray(nbytes)
833
+ for i, (d, v) in enumerate(bits):
834
+ if d:
835
+ dirb[i >> 3] |= 1 << (i & 7)
836
+ if v:
837
+ outb[i >> 3] |= 1 << (i & 7)
838
+ resp = self._cmd(bytes([0xCF, 0x00]) + struct.pack('<H', n) + dirb + outb, nbytes + 1)
839
+ if resp[nbytes] != 0:
840
+ raise ProbeError('J-Link SWD I/O error 0x%02x' % resp[nbytes])
841
+ return [(resp[i >> 3] >> (i & 7)) & 1 for i in range(n)]
842
+
843
+ def _swj_sequence(self, nbits, data):
844
+ self._swd_io([(1, (data[i >> 3] >> (i & 7)) & 1) for i in range(nbits)])
845
+
846
+ @staticmethod
847
+ def _bits(value, n):
848
+ return [(value >> i) & 1 for i in range(n)]
849
+
850
+ # Expand ops into SWD transactions, handle posted AP reads via RDBUFF
851
+ def _transfer(self, ops):
852
+ results = [None] * sum(op[1] for op in ops)
853
+ trans = list() # (apndp, rnw, addr, value, deliver_index)
854
+ pending = None
855
+ ridx = 0
856
+ for ap, rnw, reg, val in ops:
857
+ if ap and rnw:
858
+ trans.append((1, 1, reg, 0, pending))
859
+ pending = ridx
860
+ ridx += 1
861
+ continue
862
+ if pending is not None:
863
+ trans.append((0, 1, DP_RDBUFF, 0, pending))
864
+ pending = None
865
+ if rnw:
866
+ trans.append((0, 1, reg, 0, ridx))
867
+ ridx += 1
868
+ else:
869
+ trans.append((ap, 0, reg, val, None))
870
+ if pending is not None:
871
+ trans.append((0, 1, DP_RDBUFF, 0, pending))
872
+ i = 0
873
+ retries = 0
874
+ while i < len(trans):
875
+ chunk = trans[i:i + 48]
876
+ bits = list()
877
+ for ap, rnw, reg, val, _ in chunk:
878
+ a2, a3 = (reg >> 2) & 1, (reg >> 3) & 1
879
+ par = (ap + rnw + a2 + a3) & 1
880
+ bits += [(1, 1), (1, ap), (1, rnw), (1, a2), (1, a3), (1, par), (1, 0), (1, 1), (0, 0)]
881
+ bits += [(0, 0)] * 3
882
+ if rnw:
883
+ bits += [(0, 0)] * 34
884
+ else:
885
+ bits += [(0, 0)] + [(1, b) for b in self._bits(val, 32)] + [(1, bin(val & 0xFFFFFFFF).count('1') & 1)]
886
+ bits += [(1, 0)] * 8
887
+ inp = self._swd_io(bits)
888
+ pos = 0
889
+ for k, (ap, rnw, reg, val, deliver) in enumerate(chunk):
890
+ ack = inp[pos + 9] | (inp[pos + 10] << 1) | (inp[pos + 11] << 2)
891
+ if ack != 1:
892
+ i += k
893
+ if ack == 2 and retries < 1000:
894
+ retries += 1
895
+ self._single(0, 0, DP_ABORT, 0x10)
896
+ if ap and rnw and deliver is not None:
897
+ results[deliver] = self._single(0, 1, DP_RDBUFF, 0)
898
+ trans[i] = (ap, rnw, reg, val, None)
899
+ break
900
+ if ack == 4:
901
+ self._single(0, 0, DP_ABORT, ABORT_CLEAR)
902
+ raise TransferFault('SWD FAULT')
903
+ if ack == 2:
904
+ raise TransferFault('SWD WAIT timeout')
905
+ raise ProbeError('SWD no response from target')
906
+ if rnw:
907
+ d = sum(inp[pos + 12 + b] << b for b in range(32))
908
+ if (bin(d).count('1') & 1) != inp[pos + 44]:
909
+ raise ProbeError('SWD parity error')
910
+ if deliver is not None:
911
+ results[deliver] = d
912
+ pos += 46
913
+ else:
914
+ pos += 46
915
+ else:
916
+ i += len(chunk)
917
+ return results
918
+
919
+ # Single transaction (used for error recovery)
920
+ def _single(self, ap, rnw, reg, val):
921
+ a2, a3 = (reg >> 2) & 1, (reg >> 3) & 1
922
+ par = (ap + rnw + a2 + a3) & 1
923
+ bits = [(1, 1), (1, ap), (1, rnw), (1, a2), (1, a3), (1, par), (1, 0), (1, 1), (0, 0)] + [(0, 0)] * 3
924
+ if rnw:
925
+ bits += [(0, 0)] * 34
926
+ else:
927
+ bits += [(0, 0)] + [(1, b) for b in self._bits(val, 32)] + [(1, bin(val).count('1') & 1)]
928
+ bits += [(1, 0)] * 8
929
+ inp = self._swd_io(bits)
930
+ if (inp[9] | (inp[10] << 1) | (inp[11] << 2)) != 1:
931
+ raise ProbeError('SWD error during recovery')
932
+ return sum(inp[12 + b] << b for b in range(32)) if rnw else None
933
+
934
+
935
+ # ===================================================================================
936
+ # ST-Link Probe (V2, V2-1, V3)
937
+ # ===================================================================================
938
+
939
+ STLINK_VID = 0x0483
940
+ STLINK_PIDS = { # PID: (name, OUT endpoint, IN endpoint)
941
+ 0x3748: ('ST-Link V2', 0x02, 0x81),
942
+ 0x374a: ('ST-Link V2-1', 0x01, 0x81),
943
+ 0x374b: ('ST-Link V2-1', 0x01, 0x81),
944
+ 0x3752: ('ST-Link V2-1', 0x01, 0x81),
945
+ 0x374e: ('ST-Link V3', 0x01, 0x81),
946
+ 0x374f: ('ST-Link V3', 0x01, 0x81),
947
+ 0x3753: ('ST-Link V3', 0x01, 0x81),
948
+ 0x3754: ('ST-Link V3', 0x01, 0x81),
949
+ 0x3755: ('ST-Link V3', 0x01, 0x81),
950
+ 0x3757: ('ST-Link V3', 0x01, 0x81),
951
+ }
952
+ STLINK_OK = 0x80
953
+ STLINK_FAULTS = (0x01, 0x10, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19, 0x1a)
954
+ STLINK_V2_FREQ = ((4600, 0), (1800, 1), (1200, 2), (950, 3), (650, 5), (480, 7),
955
+ (400, 9), (360, 10), (240, 15), (150, 25), (125, 31), (100, 40))
956
+
957
+ class STLink(Probe):
958
+ def __init__(self, dev):
959
+ self.dev = dev
960
+ self.pname, out_ep, in_ep = STLINK_PIDS[dev.idProduct]
961
+ try:
962
+ dev.set_configuration()
963
+ except Exception:
964
+ pass
965
+ usb.util.claim_interface(dev, 0)
966
+ self.ep_out, self.ep_in = out_ep, in_ep
967
+ try: # flush stale responses
968
+ while True:
969
+ dev.read(self.ep_in, 64, 10)
970
+ except Exception:
971
+ pass
972
+ self._idle()
973
+ ver = struct.unpack('>H', self._cmd([0xF1], 6)[:2])[0] # GET_VERSION
974
+ self.hw = (ver >> 12) & 0xF
975
+ self.jtag = (ver >> 6) & 0x3F
976
+ if self.hw >= 3:
977
+ r = self._cmd([0xFB], 12) # GET_VERSION_EXT
978
+ self.jtag = r[2]
979
+ self.version = 'V%dJ%dM%d' % (self.hw, self.jtag, r[3])
980
+ else:
981
+ self.version = 'V%dJ%dM%d' % (self.hw, self.jtag, ver & 0x3F)
982
+ if self.hw < 3 and self.jtag < 24:
983
+ raise ProbeError('ST-Link firmware %s is too old, please update' % self.version)
984
+ self.name = '%s (firmware %s)' % (self.pname, self.version)
985
+ try:
986
+ a0, a1 = struct.unpack('<II', self._cmd([0xF7], 8)) # GET_TARGET_VOLTAGE
987
+ self.voltage = 2 * a1 * 1.2 / a0 if a0 else None
988
+ except Exception:
989
+ self.voltage = None
990
+ self.maxrw = 1024
991
+
992
+ def _version_ok(self, v2, v3):
993
+ return self.jtag >= (v3 if self.hw >= 3 else v2)
994
+
995
+ def _cmd(self, cmd, rsize=0, data=None):
996
+ cmd = bytes(cmd) + b'\x00' * (16 - len(cmd))
997
+ self.dev.write(self.ep_out, cmd, 1000)
998
+ if data:
999
+ self.dev.write(self.ep_out, bytes(data), 1000)
1000
+ if rsize:
1001
+ return bytes(self.dev.read(self.ep_in, rsize, 2000))
1002
+ return b''
1003
+
1004
+ def _status(self, resp):
1005
+ if resp[0] != STLINK_OK:
1006
+ if resp[0] in STLINK_FAULTS:
1007
+ raise TransferFault('ST-Link transfer error 0x%02x' % resp[0])
1008
+ raise ProbeError('ST-Link error 0x%02x' % resp[0])
1009
+
1010
+ def _idle(self):
1011
+ mode = self._cmd([0xF5], 2)[0] # GET_CURRENT_MODE
1012
+ if mode == 0x00:
1013
+ self._cmd([0xF3, 0x07]) # DFU exit
1014
+ elif mode == 0x02:
1015
+ self._cmd([0xF2, 0x21]) # debug exit
1016
+ elif mode == 0x03:
1017
+ self._cmd([0xF4, 0x01]) # SWIM exit
1018
+
1019
+ def connect(self, speed_khz):
1020
+ self._idle()
1021
+ if self.hw >= 3:
1022
+ self._status(self._cmd([0xF2, 0x61, 0x00, 0x00] + list(struct.pack('<I', speed_khz)), 8))
1023
+ else:
1024
+ div = STLINK_V2_FREQ[-1][1]
1025
+ for f, d in STLINK_V2_FREQ:
1026
+ if speed_khz >= f:
1027
+ div = d
1028
+ break
1029
+ self._status(self._cmd([0xF2, 0x43, div], 2))
1030
+ return self.reconnect()
1031
+
1032
+ def reconnect(self):
1033
+ self._idle()
1034
+ self._status(self._cmd([0xF2, 0x30, 0xA3, 0x00], 2)) # enter SWD
1035
+ if self._version_ok(28, 2):
1036
+ self._status(self._cmd([0xF2, 0x4B, 0x00, 0x00], 2)) # init AP 0
1037
+ return self.read_dp(DP_DPIDR)
1038
+
1039
+ def close(self):
1040
+ try:
1041
+ self._idle()
1042
+ except Exception:
1043
+ pass
1044
+ try:
1045
+ usb.util.release_interface(self.dev, 0)
1046
+ usb.util.dispose_resources(self.dev)
1047
+ except Exception:
1048
+ pass
1049
+
1050
+ def set_reset(self, asserted):
1051
+ self._status(self._cmd([0xF2, 0x3C, 0x00 if asserted else 0x01], 2))
1052
+
1053
+ def read_dp(self, reg):
1054
+ r = self._cmd([0xF2, 0x45] + list(struct.pack('<HH', 0xFFFF, reg)), 8)
1055
+ self._status(r)
1056
+ return struct.unpack('<I', r[4:8])[0]
1057
+
1058
+ def read_ap(self, ap, reg):
1059
+ r = self._cmd([0xF2, 0x45] + list(struct.pack('<HH', ap, reg)), 8)
1060
+ self._status(r)
1061
+ return struct.unpack('<I', r[4:8])[0]
1062
+
1063
+ def _rwstatus(self):
1064
+ self._status(self._cmd([0xF2, 0x3E], 12)) # GETLASTRWSTATUS2
1065
+
1066
+ def read_mem(self, addr, size):
1067
+ result = bytearray()
1068
+ for a, n in self._chunks(addr, size, self.maxrw):
1069
+ result += self._cmd([0xF2, 0x07] + list(struct.pack('<IH', a, n)), n)
1070
+ self._rwstatus()
1071
+ return bytes(result)
1072
+
1073
+ def write_mem(self, addr, data):
1074
+ pos = 0
1075
+ for a, n in self._chunks(addr, len(data), self.maxrw):
1076
+ self._cmd([0xF2, 0x08] + list(struct.pack('<IH', a, n)), data=data[pos:pos + n])
1077
+ self._rwstatus()
1078
+ pos += n
1079
+
1080
+ def read16(self, addr):
1081
+ if self._version_ok(26, 1):
1082
+ r = self._cmd([0xF2, 0x47] + list(struct.pack('<IH', addr, 2)), 2)
1083
+ self._rwstatus()
1084
+ return struct.unpack('<H', r)[0]
1085
+ return self.read8(addr) | (self.read8(addr + 1) << 8)
1086
+
1087
+ def write16(self, addr, value):
1088
+ if self._version_ok(26, 1):
1089
+ self._cmd([0xF2, 0x48] + list(struct.pack('<IH', addr, 2)), data=struct.pack('<H', value & 0xFFFF))
1090
+ self._rwstatus()
1091
+ else:
1092
+ self.write8(addr, value & 0xFF)
1093
+ self.write8(addr + 1, (value >> 8) & 0xFF)
1094
+
1095
+ def read8(self, addr):
1096
+ r = self._cmd([0xF2, 0x0C] + list(struct.pack('<IH', addr, 1)), 2)
1097
+ self._rwstatus()
1098
+ return r[0]
1099
+
1100
+ def write8(self, addr, value):
1101
+ self._cmd([0xF2, 0x0D] + list(struct.pack('<IH', addr, 1)), data=bytes([value & 0xFF]))
1102
+ self._rwstatus()
1103
+
1104
+
1105
+ # ===================================================================================
1106
+ # Target Class (Cortex-M core control, chip detection, flash programming)
1107
+ # ===================================================================================
1108
+
1109
+ DHCSR = 0xE000EDF0
1110
+ DCRSR = 0xE000EDF4
1111
+ DCRDR = 0xE000EDF8
1112
+ DEMCR = 0xE000EDFC
1113
+ AIRCR = 0xE000ED0C
1114
+ CPUID = 0xE000ED00
1115
+ DBGKEY = 0xA05F0000
1116
+ C_DEBUGEN = 1 << 0
1117
+ C_HALT = 1 << 1
1118
+ C_MASKINTS = 1 << 3
1119
+ S_REGRDY = 1 << 16
1120
+ S_HALT = 1 << 17
1121
+
1122
+ CPU_NAMES = {0xC20: 'Cortex-M0', 0xC60: 'Cortex-M0+', 0xC23: 'Cortex-M3', 0xC24: 'Cortex-M4',
1123
+ 0xC27: 'Cortex-M7', 0xD21: 'Cortex-M33', 0xD20: 'Cortex-M23'}
1124
+
1125
+ class Chip:
1126
+ def __init__(self, name, driver, flash_base, flash_size, ram_base=0x20000000, ram_size=0x1000,
1127
+ regions=None, bootpin=None, nrst=False, lock=True):
1128
+ self.name = name
1129
+ self.driver = driver
1130
+ self.flash_base = flash_base
1131
+ self.flash_size = flash_size
1132
+ self.ram_base = ram_base
1133
+ self.ram_size = ram_size
1134
+ self.regions = regions or [(flash_base, flash_size, 0)]
1135
+ self.has_bootpin = bootpin # None: not available, 'fixed': always usable, 'option'
1136
+ self.has_nrst = nrst
1137
+ self.has_lock = lock
1138
+
1139
+
1140
+ class Target:
1141
+ def __init__(self, probe, speed=1000):
1142
+ self.probe = probe
1143
+ self.speed = speed
1144
+ self.chip = None
1145
+ self.locked = False
1146
+ self.halted = False
1147
+
1148
+ # ------------------------------------------------------------------------------
1149
+ # Connection and Core Control
1150
+ # ------------------------------------------------------------------------------
1151
+
1152
+ def connect(self):
1153
+ try:
1154
+ self.probe.connect(self.speed)
1155
+ self.halt()
1156
+ except Exception:
1157
+ self._connect_under_reset() # e.g. SWD pins disabled or low-power mode
1158
+ self.detect()
1159
+ if self.chip.driver is not None and not self.locked:
1160
+ self.reset_halt()
1161
+
1162
+ def _connect_under_reset(self):
1163
+ self.probe.set_reset(True)
1164
+ time.sleep(0.05)
1165
+ try:
1166
+ self.probe.reconnect()
1167
+ try:
1168
+ self.write32(DEMCR, 1)
1169
+ self.write32(DHCSR, DBGKEY | C_DEBUGEN | C_HALT)
1170
+ except TransferFault:
1171
+ pass
1172
+ finally:
1173
+ self.probe.set_reset(False)
1174
+ time.sleep(0.05)
1175
+ try:
1176
+ self.halt()
1177
+ except Exception:
1178
+ self.halted = False # possibly locked (e.g. SAMD security bit)
1179
+
1180
+ def read32(self, addr):
1181
+ return self.probe.read32(addr)
1182
+
1183
+ def write32(self, addr, value):
1184
+ self.probe.write32(addr, value)
1185
+
1186
+ def read_mem(self, addr, size):
1187
+ return self.probe.read_mem(addr, size)
1188
+
1189
+ def write_mem(self, addr, data):
1190
+ self.probe.write_mem(addr, data)
1191
+
1192
+ def try_read32(self, addr):
1193
+ try:
1194
+ return self.read32(addr)
1195
+ except (TransferFault, ProbeError):
1196
+ if hasattr(self.probe, 'clear_errors'):
1197
+ self.probe.clear_errors()
1198
+ return None
1199
+
1200
+ def wait(self, addr, mask, value=0, timeout=10.0, kick=None):
1201
+ start = time.time()
1202
+ while True:
1203
+ v = self.read32(addr)
1204
+ if (v & mask) == value:
1205
+ return v
1206
+ if kick:
1207
+ kick()
1208
+ if time.time() - start > timeout:
1209
+ raise Exception('Timeout waiting for target (0x%08x = 0x%08x)' % (addr, v))
1210
+
1211
+ def halt(self):
1212
+ self.write32(DHCSR, DBGKEY | C_DEBUGEN | C_HALT)
1213
+ self.wait(DHCSR, S_HALT, S_HALT, 0.5)
1214
+ self.halted = True
1215
+
1216
+ def reset_halt(self):
1217
+ self.write32(DEMCR, self.read32(DEMCR) | 1) # VC_CORERESET
1218
+ try:
1219
+ self.write32(AIRCR, 0x05FA0004) # SYSRESETREQ
1220
+ except (ProbeError, TransferFault):
1221
+ pass
1222
+ time.sleep(0.05)
1223
+ for _ in range(20):
1224
+ try:
1225
+ if self.read32(DHCSR) & S_HALT:
1226
+ break
1227
+ except (ProbeError, TransferFault):
1228
+ self.reconnect()
1229
+ time.sleep(0.01)
1230
+ else:
1231
+ raise Exception('Target did not halt after reset')
1232
+ self.write32(DEMCR, self.read32(DEMCR) & ~1)
1233
+ self.halted = True
1234
+ self.freeze_watchdog()
1235
+
1236
+ def reconnect(self):
1237
+ last = None
1238
+ for _ in range(20):
1239
+ try:
1240
+ self.probe.reconnect()
1241
+ self.halt()
1242
+ return
1243
+ except (ProbeError, TransferFault) as ex:
1244
+ last = ex
1245
+ time.sleep(0.1)
1246
+ raise Exception('Lost connection to target (%s)' % last)
1247
+
1248
+ # Reconnect after the chip was reset by an option byte reload
1249
+ def reconnect_after_reset(self):
1250
+ time.sleep(0.2)
1251
+ try:
1252
+ self.reconnect()
1253
+ except Exception:
1254
+ self._connect_under_reset()
1255
+ self.locked = self.chip.driver.is_locked()
1256
+ if not self.locked:
1257
+ self.reset_halt()
1258
+
1259
+ def write_reg(self, reg, value):
1260
+ self.write32(DCRDR, value)
1261
+ self.write32(DCRSR, reg | (1 << 16))
1262
+ self.wait(DHCSR, S_REGRDY, S_REGRDY, 0.5)
1263
+
1264
+ def read_reg(self, reg):
1265
+ self.write32(DCRSR, reg)
1266
+ self.wait(DHCSR, S_REGRDY, S_REGRDY, 0.5)
1267
+ return self.read32(DCRDR)
1268
+
1269
+ def freeze_watchdog(self):
1270
+ fz = getattr(self.chip.driver, 'iwdg_freeze', None) if self.chip and self.chip.driver else None
1271
+ if fz:
1272
+ v = self.try_read32(fz[0])
1273
+ if v is not None:
1274
+ try:
1275
+ self.write32(fz[0], v | fz[1])
1276
+ except (ProbeError, TransferFault):
1277
+ pass
1278
+
1279
+ def exit(self, run=True):
1280
+ try:
1281
+ if run and self.halted:
1282
+ self.write32(DEMCR, 0)
1283
+ self.write32(DHCSR, DBGKEY | C_DEBUGEN)
1284
+ try:
1285
+ self.write32(AIRCR, 0x05FA0004)
1286
+ except (ProbeError, TransferFault):
1287
+ pass
1288
+ time.sleep(0.01)
1289
+ try:
1290
+ self.write32(DHCSR, DBGKEY)
1291
+ except (ProbeError, TransferFault):
1292
+ pass
1293
+ finally:
1294
+ try:
1295
+ self.probe.set_reset(False)
1296
+ except Exception:
1297
+ pass
1298
+ self.probe.close()
1299
+
1300
+ # ------------------------------------------------------------------------------
1301
+ # Chip Detection
1302
+ # ------------------------------------------------------------------------------
1303
+
1304
+ def detect(self):
1305
+ if not self.halted:
1306
+ # Core not accessible: check for a locked SAMD (DSU external access)
1307
+ did = self.try_read32(SAMD_DSU + 0x100 + 0x18)
1308
+ chip = samd_chip(self, did) if did else None
1309
+ if chip is None:
1310
+ raise Exception('Cannot access target core')
1311
+ self.chip = chip
1312
+ self.locked = True
1313
+ return
1314
+ cpuid = self.read32(CPUID)
1315
+ part = (cpuid >> 4) & 0xFFF
1316
+ self.cpu = CPU_NAMES.get(part, 'CPUID 0x%08x' % cpuid)
1317
+ idcodes = list()
1318
+ for addr in STM32_IDCODE_ADDR.get(part, []):
1319
+ idcode = self.try_read32(addr)
1320
+ if idcode is None:
1321
+ continue
1322
+ idcodes.append(idcode)
1323
+ dev = STM32_DEVICES.get(idcode & 0xFFF)
1324
+ if dev is not None and dev[0] == addr:
1325
+ self.chip = stm32_chip(self, part, idcode & 0xFFF, dev)
1326
+ break
1327
+ else:
1328
+ chip = None
1329
+ if part in (0xC20, 0xC60):
1330
+ chip = py32_chip(self, self.try_read32(0x40015800))
1331
+ if chip is None and part in (0xC60, 0xC24):
1332
+ chip = samd_chip(self, self.try_read32(SAMD_DSU + 0x18))
1333
+ if chip is None:
1334
+ raise Exception('Unsupported chip (%s, IDCODE %s)' % (self.cpu,
1335
+ ', '.join('0x%08x' % i for i in idcodes) or 'n/a'))
1336
+ self.chip = chip
1337
+ self.locked = self.chip.driver.is_locked()
1338
+
1339
+ # ------------------------------------------------------------------------------
1340
+ # Flash Loader
1341
+ # ------------------------------------------------------------------------------
1342
+
1343
+ def _layout(self):
1344
+ base = self.chip.ram_base
1345
+ bufsize = min(self.chip.ram_size - 0x100, 0x4000)
1346
+ return base, base + 0xA0, base + 0x100, bufsize
1347
+
1348
+ def load_loader(self):
1349
+ code = struct.pack('<%dH' % len(LOADER_CODE), *LOADER_CODE)
1350
+ self.write_mem(self.chip.ram_base, code + b'\x00' * ((-len(code)) % 4))
1351
+
1352
+ # Run loader for count units of data (already in RAM buffer at src)
1353
+ def run_loader(self, p, src, dst, count, timeout=None):
1354
+ code, params, _, _ = self._layout()
1355
+ unit = p['unit']
1356
+ block = struct.pack('<17I', src, dst, count, unit, p.get('stride', unit),
1357
+ p['sr'], p['busy'], p.get('xor', 0), p.get('err', 0),
1358
+ p.get('cr', 0), p.get('trig', 0), p.get('pos', 0xFFFFFFFF),
1359
+ p.get('flags', 0), p.get('set', 0), p.get('kick', 0),
1360
+ p.get('kickval', 0), 0)
1361
+ self.write_mem(params, block)
1362
+ self.write_reg(0, params)
1363
+ self.write_reg(13, params)
1364
+ self.write_reg(14, code | 1)
1365
+ self.write_reg(15, code)
1366
+ self.write_reg(16, 0x01000000)
1367
+ self.write32(DHCSR, DBGKEY | C_DEBUGEN | C_HALT | C_MASKINTS)
1368
+ self.write32(DHCSR, DBGKEY | C_DEBUGEN | C_MASKINTS)
1369
+ if timeout is None:
1370
+ timeout = 2.0 + 0.05 * count
1371
+ start = time.time()
1372
+ while not (self.read32(DHCSR) & S_HALT):
1373
+ if time.time() - start > timeout:
1374
+ self.halt()
1375
+ raise Exception('Flash loader timeout at 0x%08x' % self.read_reg(2))
1376
+ result = self.read_reg(0)
1377
+ if result:
1378
+ fail = self.read32(params + 0x40)
1379
+ raise Exception('Flash operation failed at 0x%08x (status 0x%08x)' % (fail, result))
1380
+
1381
+ # Write data to target using the loader (data length must be a multiple of unit)
1382
+ def loader_write(self, p, dst, data, stride=None):
1383
+ self.load_loader()
1384
+ _, _, buf, bufsize = self._layout()
1385
+ unit = p['unit']
1386
+ step = stride or unit
1387
+ chunk = max(unit, bufsize // unit * unit)
1388
+ pos = 0
1389
+ while pos < len(data):
1390
+ block = data[pos:pos + chunk]
1391
+ self.write_mem(buf, block + b'\xff' * ((-len(block)) % 4))
1392
+ count = len(block) // unit
1393
+ self.run_loader(p, buf, dst, count)
1394
+ dst += count * step
1395
+ pos += len(block)
1396
+
1397
+ # ------------------------------------------------------------------------------
1398
+ # High-Level Functions
1399
+ # ------------------------------------------------------------------------------
1400
+
1401
+ def check_image(self, data):
1402
+ end = self.chip.regions[-1][0] + self.chip.regions[-1][1]
1403
+ if len(data) > end - self.chip.flash_base:
1404
+ raise Exception('Not enough memory (file: %d bytes, flash: %d bytes)' % (len(data), self.chip.flash_size))
1405
+ pos = 0
1406
+ for addr, size, _ in self.chip.regions:
1407
+ off = addr - self.chip.flash_base
1408
+ if off > pos and data[pos:off].strip(b'\xff'):
1409
+ raise Exception('BIN file contains data outside of flash (offset 0x%x)' % pos)
1410
+ pos = off + size
1411
+ self.chip.driver.check_image(data)
1412
+
1413
+ def unlock(self):
1414
+ if not self.locked:
1415
+ print('Chip is not locked, nothing to do.')
1416
+ return False
1417
+ self.chip.driver.unlock()
1418
+ self.reconnect_after_reset()
1419
+ if self.locked:
1420
+ raise Exception('Unlocking failed')
1421
+ self.detect() # update chip information
1422
+ self.reset_halt()
1423
+ return True
1424
+
1425
+ def lock(self):
1426
+ self.chip.driver.lock()
1427
+ self.reconnect_after_reset()
1428
+
1429
+ def erase(self):
1430
+ self.chip.driver.erase()
1431
+
1432
+ def flash_data(self, data):
1433
+ drv = self.chip.driver
1434
+ unit = drv.unit
1435
+ data = bytes(data) + b'\xff' * ((-len(data)) % unit)
1436
+ for addr, size, bank in self.chip.regions:
1437
+ off = addr - self.chip.flash_base
1438
+ seg = data[off:off + size]
1439
+ if not seg:
1440
+ continue
1441
+ drv.prepare(bank)
1442
+ try:
1443
+ self.loader_write(drv.loader_params(bank), addr, seg)
1444
+ finally:
1445
+ drv.finish(bank)
1446
+
1447
+ def verify_data(self, data):
1448
+ for addr, size, bank in self.chip.regions:
1449
+ off = addr - self.chip.flash_base
1450
+ seg = data[off:off + size]
1451
+ if not seg:
1452
+ continue
1453
+ readback = self.read_mem(addr, len(seg) + ((-len(seg)) % 4))[:len(seg)]
1454
+ if readback != seg:
1455
+ for i in range(len(seg)):
1456
+ if readback[i] != seg[i]:
1457
+ raise Exception('Verification failed at 0x%08x' % (addr + i))
1458
+
1459
+ def set_bootpin(self):
1460
+ if self.chip.has_bootpin == 'fixed':
1461
+ print('BOOT0 pin is always usable on this chip, nothing to do.')
1462
+ return
1463
+ self.chip.driver.bootpin()
1464
+ self.reconnect_after_reset()
1465
+
1466
+ def set_nrst(self, gpio):
1467
+ self.chip.driver.nrst(gpio)
1468
+ self.reconnect_after_reset()
1469
+
1470
+
1471
+ # ===================================================================================
1472
+ # Flash Driver Base Class
1473
+ # ===================================================================================
1474
+
1475
+ class FlashDriver:
1476
+ unit = 4
1477
+ iwdg_freeze = None
1478
+
1479
+ def __init__(self, target):
1480
+ self.t = target
1481
+
1482
+ def is_locked(self):
1483
+ return False
1484
+
1485
+ def check_image(self, data):
1486
+ pass
1487
+
1488
+ def prepare(self, bank):
1489
+ pass
1490
+
1491
+ def finish(self, bank):
1492
+ pass
1493
+
1494
+ def unlock(self):
1495
+ raise Exception('Unlocking is not supported on this chip')
1496
+
1497
+ def lock(self):
1498
+ raise Exception('Locking is not supported on this chip')
1499
+
1500
+ def bootpin(self):
1501
+ raise Exception('BOOT0 pin configuration is not supported on this chip')
1502
+
1503
+ def nrst(self, gpio):
1504
+ raise Exception('NRST pin configuration is not supported on this chip')
1505
+
1506
+ def r(self, reg):
1507
+ return self.t.read32(self.base + reg)
1508
+
1509
+ def w(self, reg, value):
1510
+ self.t.write32(self.base + reg, value)
1511
+
1512
+ def _unlock_keys(self, cr, lockbit, keyr, keys=(0x45670123, 0xCDEF89AB)):
1513
+ if self.r(cr) & lockbit:
1514
+ self.w(keyr, keys[0])
1515
+ self.w(keyr, keys[1])
1516
+ if self.r(cr) & lockbit:
1517
+ raise Exception('Unlocking flash controller failed')
1518
+
1519
+
1520
+ # ===================================================================================
1521
+ # STM32F0/F1/F3 Flash Driver
1522
+ # ===================================================================================
1523
+
1524
+ class STM32F1Flash(FlashDriver):
1525
+ unit = 2
1526
+
1527
+ def __init__(self, target, part, devid, size):
1528
+ super().__init__(target)
1529
+ self.base = 0x40022000
1530
+ self.f1 = (part == 0xC23) # F1: Cortex-M3
1531
+ self.default_rdp = 0xA5 if self.f1 else 0xAA
1532
+ self.bootsel = devid in (0x445, 0x442) # F04x, F09x
1533
+ self.iwdg_freeze = (0xE0042004, 1 << 8) if self.f1 else \
1534
+ ((0x40015808, 1 << 12) if part == 0xC20 else (0xE0042008, 1 << 12))
1535
+
1536
+ def _bank(self, bank):
1537
+ return self.base + 0x40 * bank
1538
+
1539
+ def _wait(self, bank, timeout=10.0):
1540
+ sr = self.t.wait(self._bank(bank) + 0x0C, 0x01, 0, timeout)
1541
+ if sr & 0x14:
1542
+ self.t.write32(self._bank(bank) + 0x0C, 0x34)
1543
+ raise Exception('Flash error (SR 0x%08x)' % sr)
1544
+
1545
+ def _unlock(self, bank):
1546
+ b = self._bank(bank)
1547
+ if self.t.read32(b + 0x10) & 0x80:
1548
+ self.t.write32(b + 0x04, 0x45670123)
1549
+ self.t.write32(b + 0x04, 0xCDEF89AB)
1550
+
1551
+ def banks(self):
1552
+ return sorted(set(r[2] for r in self.t.chip.regions))
1553
+
1554
+ def is_locked(self):
1555
+ obr = self.r(0x1C)
1556
+ return bool(obr & 0x02) if self.f1 else bool((obr >> 1) & 0x03)
1557
+
1558
+ def erase(self):
1559
+ for bank in self.banks():
1560
+ b = self._bank(bank)
1561
+ self._unlock(bank)
1562
+ self._wait(bank)
1563
+ self.t.write32(b + 0x0C, 0x34)
1564
+ self.t.write32(b + 0x10, 0x04) # MER
1565
+ self.t.write32(b + 0x10, 0x44) # MER | STRT
1566
+ self._wait(bank, 60.0)
1567
+ self.t.write32(b + 0x10, 0x80) # LOCK
1568
+
1569
+ def prepare(self, bank):
1570
+ b = self._bank(bank)
1571
+ self._unlock(bank)
1572
+ self._wait(bank)
1573
+ self.t.write32(b + 0x0C, 0x34)
1574
+ self.t.write32(b + 0x10, 0x01) # PG
1575
+
1576
+ def loader_params(self, bank):
1577
+ return {'unit': 2, 'flags': 1, 'sr': self._bank(bank) + 0x0C, 'busy': 0x01, 'err': 0x14}
1578
+
1579
+ def finish(self, bank):
1580
+ self.t.write32(self._bank(bank) + 0x10, 0x80)
1581
+
1582
+ def _options(self, rdp=None, user_set=0):
1583
+ obr = self.r(0x1C)
1584
+ wrp = self.r(0x20)
1585
+ if self.f1:
1586
+ user, data = (obr >> 2) & 0xFF, (obr >> 10) & 0xFFFF
1587
+ else:
1588
+ user, data = (obr >> 8) & 0xFF, (obr >> 16) & 0xFFFF
1589
+ if rdp is None:
1590
+ rdp = 0x00 if self.is_locked() else self.default_rdp
1591
+ user |= user_set
1592
+ ob = [rdp, user, data & 0xFF, data >> 8, wrp & 0xFF, (wrp >> 8) & 0xFF, (wrp >> 16) & 0xFF, wrp >> 24]
1593
+ self._unlock(0)
1594
+ self.w(0x08, 0x45670123) # OPTKEYR
1595
+ self.w(0x08, 0xCDEF89AB)
1596
+ self.w(0x10, 0x220) # OPTER | OPTWRE
1597
+ self.w(0x10, 0x260) # OPTER | OPTWRE | STRT
1598
+ self._wait(0, 60.0)
1599
+ self.w(0x10, 0x210) # OPTPG | OPTWRE
1600
+ self.t.loader_write(self.loader_params(0), 0x1FFFF800, struct.pack('<8H', *ob))
1601
+ if self.f1:
1602
+ self.w(0x10, 0x80)
1603
+ try:
1604
+ self.t.write32(AIRCR, 0x05FA0004) # reload option bytes by reset
1605
+ except (ProbeError, TransferFault):
1606
+ pass
1607
+ else:
1608
+ try:
1609
+ self.w(0x10, 0x2200) # OBL_LAUNCH | OPTWRE
1610
+ except (ProbeError, TransferFault):
1611
+ pass
1612
+
1613
+ def unlock(self):
1614
+ self._options(rdp=self.default_rdp)
1615
+
1616
+ def lock(self):
1617
+ self._options(rdp=0x00)
1618
+
1619
+ def bootpin(self):
1620
+ if self.f1:
1621
+ raise Exception('BOOT0 pin configuration is not supported on this chip')
1622
+ self._options(user_set=0x90 if self.bootsel else 0x10) # (BOOT_SEL) | nBOOT1
1623
+
1624
+
1625
+ # ===================================================================================
1626
+ # STM32F2/F4/F7 Flash Driver
1627
+ # ===================================================================================
1628
+
1629
+ class STM32F4Flash(FlashDriver):
1630
+ unit = 2
1631
+
1632
+ def __init__(self, target, part, devid, size):
1633
+ super().__init__(target)
1634
+ self.base = 0x40023C00
1635
+ self.iwdg_freeze = (0xE0042008, 1 << 12)
1636
+ optcr = target.read32(self.base + 0x14)
1637
+ if devid in (0x419, 0x434):
1638
+ self.dual = size >= 2048 * 1024 or (size == 1024 * 1024 and bool(optcr & (1 << 30)))
1639
+ elif devid == 0x451:
1640
+ self.dual = not (optcr & (1 << 29))
1641
+ else:
1642
+ self.dual = False
1643
+
1644
+ def _wait(self, timeout=10.0):
1645
+ sr = self.t.wait(self.base + 0x0C, 1 << 16, 0, timeout)
1646
+ if sr & 0xF2:
1647
+ self.w(0x0C, 0xF3)
1648
+ raise Exception('Flash error (SR 0x%08x)' % sr)
1649
+
1650
+ def _unlock(self):
1651
+ self._unlock_keys(0x10, 1 << 31, 0x04)
1652
+
1653
+ def is_locked(self):
1654
+ return ((self.r(0x14) >> 8) & 0xFF) != 0xAA
1655
+
1656
+ def erase(self):
1657
+ self._unlock()
1658
+ self._wait()
1659
+ self.w(0x0C, 0xF3)
1660
+ cr = 0x04 | (1 << 15 if self.dual else 0) | (1 << 8) # MER (| MER1) | PSIZE x16
1661
+ self.w(0x10, cr)
1662
+ self.w(0x10, cr | (1 << 16)) # STRT
1663
+ self._wait(120.0)
1664
+ self.w(0x10, 1 << 31)
1665
+
1666
+ def prepare(self, bank):
1667
+ self._unlock()
1668
+ self._wait()
1669
+ self.w(0x0C, 0xF3)
1670
+ self.w(0x10, 0x101) # PG | PSIZE x16
1671
+
1672
+ def loader_params(self, bank):
1673
+ return {'unit': 2, 'flags': 1, 'sr': self.base + 0x0C, 'busy': 1 << 16, 'err': 0xF2}
1674
+
1675
+ def finish(self, bank):
1676
+ self.w(0x10, 1 << 31)
1677
+
1678
+ def _set_rdp(self, rdp):
1679
+ self._unlock()
1680
+ if self.r(0x14) & 1:
1681
+ self.w(0x08, 0x08192A3B)
1682
+ self.w(0x08, 0x4C5D6E7F)
1683
+ self._wait()
1684
+ optcr = (self.r(0x14) & ~0xFF03) | (rdp << 8)
1685
+ self.w(0x14, optcr)
1686
+ self.w(0x14, optcr | 0x02) # OPTSTRT
1687
+ self._wait(300.0)
1688
+ self.w(0x14, optcr | 0x01) # OPTLOCK
1689
+ try:
1690
+ self.t.write32(AIRCR, 0x05FA0004)
1691
+ except (ProbeError, TransferFault):
1692
+ pass
1693
+
1694
+ def unlock(self):
1695
+ self._set_rdp(0xAA)
1696
+
1697
+ def lock(self):
1698
+ self._set_rdp(0xBB)
1699
+
1700
+
1701
+ # ===================================================================================
1702
+ # STM32L0/L1 Flash Driver
1703
+ # ===================================================================================
1704
+
1705
+ class STM32L0Flash(FlashDriver):
1706
+ def __init__(self, target, part, devid, size):
1707
+ super().__init__(target)
1708
+ self.l1 = (part == 0xC23)
1709
+ self.base = 0x40023C00 if self.l1 else 0x40022000
1710
+ self.page = 256 if self.l1 else 128
1711
+ self.unit = self.page // 2 # half page
1712
+ self.size = size
1713
+ self.iwdg_freeze = (0xE0042008 if self.l1 else 0x40015808, 1 << 12)
1714
+
1715
+ def _unlock(self):
1716
+ pecr = self.r(0x04)
1717
+ if pecr & 0x01:
1718
+ self.w(0x0C, 0x89ABCDEF)
1719
+ self.w(0x0C, 0x02030405)
1720
+ if self.r(0x04) & 0x02:
1721
+ self.w(0x10, 0x8C9DAEBF)
1722
+ self.w(0x10, 0x13141516)
1723
+ if self.r(0x04) & 0x03:
1724
+ raise Exception('Unlocking flash controller failed')
1725
+
1726
+ def _lock(self):
1727
+ self.w(0x04, (self.r(0x04) & ~0x608) | 0x01) # clear ERASE/FPRG/PROG, set PELOCK
1728
+
1729
+ def _wait(self, timeout=10.0):
1730
+ sr = self.t.wait(self.base + 0x18, 0x01, 0, timeout)
1731
+ if sr & 0x32700:
1732
+ self.w(0x18, 0x32F00)
1733
+ raise Exception('Flash error (SR 0x%08x)' % sr)
1734
+
1735
+ def is_locked(self):
1736
+ return (self.r(0x1C) & 0xFF) != 0xAA
1737
+
1738
+ def erase(self):
1739
+ self._unlock()
1740
+ self._wait()
1741
+ self.w(0x18, 0x32F00)
1742
+ self.w(0x04, (self.r(0x04) & ~0x400) | 0x208) # ERASE | PROG
1743
+ p = {'unit': 4, 'stride': self.page, 'sr': self.base + 0x18, 'busy': 0x01, 'err': 0x32700}
1744
+ try:
1745
+ self.t.loader_write(p, 0x08000000, bytes(4 * (self.size // self.page)), stride=self.page)
1746
+ finally:
1747
+ self._lock()
1748
+
1749
+ def prepare(self, bank):
1750
+ self._unlock()
1751
+ self._wait()
1752
+ self.w(0x18, 0x32F00)
1753
+ self.w(0x04, (self.r(0x04) & ~0x200) | 0x408) # FPRG | PROG
1754
+
1755
+ def loader_params(self, bank):
1756
+ return {'unit': self.unit, 'sr': self.base + 0x18, 'busy': 0x01, 'err': 0x32700}
1757
+
1758
+ def finish(self, bank):
1759
+ self._lock()
1760
+
1761
+ def _set_rdp(self, rdp):
1762
+ self._unlock()
1763
+ self.w(0x14, 0xFBEAD9C8) # OPTKEYR
1764
+ self.w(0x14, 0x24252627)
1765
+ self._wait()
1766
+ lo = (self.r(0x1C) & 0x100) | rdp
1767
+ self.t.write32(0x1FF80000, lo | ((~lo & 0xFFFF) << 16))
1768
+ self._wait(120.0)
1769
+ try:
1770
+ self.w(0x04, self.r(0x04) | (1 << 18)) # OBL_LAUNCH
1771
+ except (ProbeError, TransferFault):
1772
+ pass
1773
+
1774
+ def unlock(self):
1775
+ self._set_rdp(0xAA)
1776
+
1777
+ def lock(self):
1778
+ self._set_rdp(0xBB)
1779
+
1780
+
1781
+ # ===================================================================================
1782
+ # STM32 C0/G0/G4/L4/L4+/L5/U0/U3/U5/WB/WBA/WL Flash Driver
1783
+ # ===================================================================================
1784
+
1785
+ class STM32L4Flash(FlashDriver):
1786
+ def __init__(self, target, part, devid, size, opts):
1787
+ super().__init__(target)
1788
+ self.base = opts.get('base', 0x40022000)
1789
+ self.l5 = opts.get('l5', False)
1790
+ self.dual = opts.get('dual', False)
1791
+ self.unit = 16 if opts.get('quad') else 8
1792
+ self.boot = opts.get('boot')
1793
+ self.nrststyle = opts.get('nrst')
1794
+ self.busy = (1 << 16) | (1 << 18) | ((1 << 17) if devid == 0x467 else 0)
1795
+ self.KEYR, self.OPTKEYR, self.SR, self.CR, self.OPTR = \
1796
+ (0x08, 0x10, 0x20, 0x28, 0x40) if self.l5 else (0x08, 0x0C, 0x10, 0x14, 0x20)
1797
+ if opts.get('tz') and (self.r(self.OPTR) & (1 << 31)):
1798
+ raise Exception('TrustZone is enabled on this chip, not supported')
1799
+ fz = opts.get('fz')
1800
+ self.iwdg_freeze = (fz, 1 << 12) if fz else None
1801
+
1802
+ def _wait(self, timeout=10.0):
1803
+ sr = self.t.wait(self.base + self.SR, self.busy, 0, timeout)
1804
+ if sr & 0x3FA:
1805
+ self.w(self.SR, 0x3FB)
1806
+ raise Exception('Flash error (SR 0x%08x)' % sr)
1807
+
1808
+ def _unlock(self):
1809
+ self._unlock_keys(self.CR, 1 << 31, self.KEYR)
1810
+
1811
+ def is_locked(self):
1812
+ return (self.r(self.OPTR) & 0xFF) != 0xAA
1813
+
1814
+ def erase(self):
1815
+ self._unlock()
1816
+ self._wait()
1817
+ self.w(self.SR, 0x3FB)
1818
+ cr = 0x04 | ((1 << 15) if self.dual else 0) # MER1 (| MER2)
1819
+ self.w(self.CR, cr)
1820
+ self.w(self.CR, cr | (1 << 16)) # STRT
1821
+ self._wait(120.0)
1822
+ self.w(self.CR, 1 << 31)
1823
+
1824
+ def prepare(self, bank):
1825
+ self._unlock()
1826
+ self._wait()
1827
+ self.w(self.SR, 0x3FB)
1828
+ self.w(self.CR, 0x01) # PG
1829
+
1830
+ def loader_params(self, bank):
1831
+ return {'unit': self.unit, 'sr': self.base + self.SR, 'busy': self.busy, 'err': 0x3FA}
1832
+
1833
+ def finish(self, bank):
1834
+ self.w(self.CR, 1 << 31)
1835
+
1836
+ def _set_optr(self, func):
1837
+ self._unlock()
1838
+ if self.r(self.CR) & (1 << 30):
1839
+ self.w(self.OPTKEYR, 0x08192A3B)
1840
+ self.w(self.OPTKEYR, 0x4C5D6E7F)
1841
+ if self.r(self.CR) & (1 << 30):
1842
+ raise Exception('Unlocking option bytes failed')
1843
+ self._wait()
1844
+ self.w(self.SR, 0x3FB | (1 << 15))
1845
+ self.w(self.OPTR, func(self.r(self.OPTR)))
1846
+ self.w(self.CR, self.r(self.CR) | (1 << 17)) # OPTSTRT
1847
+ self._wait(300.0)
1848
+ try:
1849
+ self.w(self.CR, self.r(self.CR) | (1 << 27)) # OBL_LAUNCH
1850
+ except (ProbeError, TransferFault):
1851
+ pass
1852
+
1853
+ def unlock(self):
1854
+ self._set_optr(lambda v: (v & ~0xFF) | 0xAA)
1855
+
1856
+ def lock(self):
1857
+ self._set_optr(lambda v: (v & ~0xFF) | 0xBB)
1858
+
1859
+ def bootpin(self):
1860
+ if self.boot == 'sel': # C0/G0/U0: nBOOT_SEL=0, nBOOT1=1
1861
+ self._set_optr(lambda v: (v & ~(1 << 24)) | (1 << 25))
1862
+ elif self.boot == 'sw': # G4/L4/WB/WL: nSWBOOT0=1, nBOOT1=1
1863
+ self._set_optr(lambda v: v | (1 << 26) | (1 << 23))
1864
+ elif self.boot == 'sw5': # L5/U5: nSWBOOT0=1
1865
+ self._set_optr(lambda v: v | (1 << 26))
1866
+ else:
1867
+ super().bootpin()
1868
+
1869
+ def nrst(self, gpio):
1870
+ mode = 0b10 if gpio else 0b11
1871
+ if self.nrststyle == 'g0':
1872
+ self._set_optr(lambda v: (v & ~(3 << 27)) | (mode << 27))
1873
+ elif self.nrststyle == 'g4':
1874
+ self._set_optr(lambda v: (v & ~(3 << 28)) | (mode << 28))
1875
+ else:
1876
+ super().nrst(gpio)
1877
+
1878
+
1879
+ # ===================================================================================
1880
+ # STM32H7 Flash Driver
1881
+ # ===================================================================================
1882
+
1883
+ class STM32H7Flash(FlashDriver):
1884
+ def __init__(self, target, part, devid, size):
1885
+ super().__init__(target)
1886
+ self.base = 0x52002000
1887
+ self.h7a = (devid == 0x480)
1888
+ self.unit = 16 if self.h7a else 32
1889
+ self.iwdg_freeze = (0x5C001054, 1 << 18)
1890
+
1891
+ def _b(self, bank):
1892
+ return self.base + 0x100 * bank
1893
+
1894
+ def _wait(self, bank, timeout=10.0):
1895
+ sr = self.t.wait(self._b(bank) + 0x10, 0x05, 0, timeout)
1896
+ if sr & 0x07EE0000:
1897
+ self.t.write32(self._b(bank) + 0x14, 0x07EE0000)
1898
+ raise Exception('Flash error (SR 0x%08x)' % sr)
1899
+
1900
+ def _unlock(self, bank):
1901
+ b = self._b(bank)
1902
+ if self.t.read32(b + 0x0C) & 1:
1903
+ self.t.write32(b + 0x04, 0x45670123)
1904
+ self.t.write32(b + 0x04, 0xCDEF89AB)
1905
+ if self.t.read32(b + 0x0C) & 1:
1906
+ raise Exception('Unlocking flash controller failed')
1907
+
1908
+ def is_locked(self):
1909
+ return ((self.r(0x1C) >> 8) & 0xFF) != 0xAA
1910
+
1911
+ def erase(self):
1912
+ for bank in sorted(set(r[2] for r in self.t.chip.regions)):
1913
+ b = self._b(bank)
1914
+ self._unlock(bank)
1915
+ self._wait(bank)
1916
+ self.t.write32(b + 0x14, 0x07EF0000)
1917
+ if self.h7a:
1918
+ cr, start = 0x08, 1 << 5 # BER, START
1919
+ else:
1920
+ cr, start = 0x08 | (3 << 4), 1 << 7 # BER | PSIZE x64, START
1921
+ self.t.write32(b + 0x0C, cr)
1922
+ self.t.write32(b + 0x0C, cr | start)
1923
+ self._wait(bank, 120.0)
1924
+ self.t.write32(b + 0x0C, 0x01)
1925
+
1926
+ def prepare(self, bank):
1927
+ b = self._b(bank)
1928
+ self._unlock(bank)
1929
+ self._wait(bank)
1930
+ self.t.write32(b + 0x14, 0x07EF0000)
1931
+ self.t.write32(b + 0x0C, 0x02) # PG
1932
+
1933
+ def loader_params(self, bank):
1934
+ return {'unit': self.unit, 'sr': self._b(bank) + 0x10, 'busy': 0x05, 'err': 0x07EE0000}
1935
+
1936
+ def finish(self, bank):
1937
+ self.t.write32(self._b(bank) + 0x0C, 0x01)
1938
+
1939
+ def _set_rdp(self, rdp):
1940
+ if self.r(0x18) & 1:
1941
+ self.w(0x08, 0x08192A3B)
1942
+ self.w(0x08, 0x4C5D6E7F)
1943
+ if self.r(0x18) & 1:
1944
+ raise Exception('Unlocking option bytes failed')
1945
+ self.w(0x24, 1 << 30) # clear OPTCHANGEERR
1946
+ self.w(0x20, (self.r(0x20) & ~0xFF00) | (rdp << 8))
1947
+ self.w(0x18, self.r(0x18) | 0x02) # OPTSTART
1948
+ cur = self.t.wait(self.base + 0x1C, 0x01, 0, 300.0)
1949
+ self.w(0x18, self.r(0x18) | 0x01) # OPTLOCK
1950
+ if cur & (1 << 30):
1951
+ raise Exception('Option byte change error')
1952
+ try:
1953
+ self.t.write32(AIRCR, 0x05FA0004)
1954
+ except (ProbeError, TransferFault):
1955
+ pass
1956
+
1957
+ def unlock(self):
1958
+ self._set_rdp(0xAA)
1959
+
1960
+ def lock(self):
1961
+ self._set_rdp(0xBB)
1962
+
1963
+
1964
+ # ===================================================================================
1965
+ # STM32H5 Flash Driver
1966
+ # ===================================================================================
1967
+
1968
+ class STM32H5Flash(FlashDriver):
1969
+ unit = 16
1970
+ iwdg_freeze = (0x44024008, 1 << 12)
1971
+
1972
+ def __init__(self, target, part, devid, size):
1973
+ super().__init__(target)
1974
+ self.base = 0x40022000
1975
+
1976
+ def _wait(self, timeout=10.0):
1977
+ sr = self.t.wait(self.base + 0x20, 0x0B, 0, timeout)
1978
+ if sr & 0x00FC0000:
1979
+ self.w(0x30, 0x00FF0000)
1980
+ raise Exception('Flash error (SR 0x%08x)' % sr)
1981
+
1982
+ def _unlock(self):
1983
+ self._unlock_keys(0x28, 0x01, 0x04)
1984
+
1985
+ def erase(self):
1986
+ self._unlock()
1987
+ self._wait()
1988
+ self.w(0x30, 0x00FF0000)
1989
+ self.w(0x28, 1 << 15) # MER
1990
+ self.w(0x28, (1 << 15) | (1 << 5)) # MER | START
1991
+ self._wait(120.0)
1992
+ self.w(0x28, 0x01)
1993
+
1994
+ def prepare(self, bank):
1995
+ self._unlock()
1996
+ self._wait()
1997
+ self.w(0x30, 0x00FF0000)
1998
+ self.w(0x28, 0x02) # PG
1999
+
2000
+ def loader_params(self, bank):
2001
+ return {'unit': 16, 'sr': self.base + 0x20, 'busy': 0x0B, 'err': 0x00FC0000}
2002
+
2003
+ def finish(self, bank):
2004
+ self.w(0x28, 0x01)
2005
+
2006
+
2007
+ # ===================================================================================
2008
+ # PUYA PY32 Flash Driver
2009
+ # ===================================================================================
2010
+
2011
+ PY32_VARIANTS = {
2012
+ # timing params, stride, word step, IWDG_SW, NRST_MODE, nBOOT1, RDP
2013
+ 'f0xx': (0x1FFF0F1C, 0x14, 4, 12, 14, 15, True),
2014
+ 'f07x': (0x1FFF3238, 0x28, 8, 11, 13, 14, True),
2015
+ 'f002b': (0x1FFF011C, 0x00, 4, 12, 14, None, False),
2016
+ }
2017
+
2018
+ class PY32Flash(FlashDriver):
2019
+ unit = 128
2020
+
2021
+ def __init__(self, target, variant):
2022
+ super().__init__(target)
2023
+ self.base = 0x40022000
2024
+ self.variant = variant
2025
+ self.tbase, self.tstride, self.tstep, self.iwdg_bit, self.nrst_bit, self.nboot1_bit, self.rdp = \
2026
+ PY32_VARIANTS[variant]
2027
+ self.iwdg_freeze = (0x40015808, 1 << 12)
2028
+
2029
+ def _kick(self):
2030
+ self.t.write32(0x40003000, 0xAAAA) # reload IWDG
2031
+
2032
+ def _wait(self, timeout=10.0):
2033
+ sr = self.t.wait(self.base + 0x10, 1 << 16, 0, timeout, kick=self._kick)
2034
+ if sr & 0x10:
2035
+ self.w(0x10, 0x11)
2036
+ raise Exception('Flash write protection error')
2037
+
2038
+ def _setup(self):
2039
+ self._unlock_keys(0x14, 1 << 31, 0x08)
2040
+ # Flash timing according to current HSI frequency (from factory configuration)
2041
+ hsi = (self.t.read32(0x40021004) >> 13) & 0x07
2042
+ if self.variant == 'f002b':
2043
+ addr = 0x1FFF0130 if hsi == 5 else 0x1FFF011C
2044
+ e = [self.t.read32(addr + 4 * i) for i in range(5)]
2045
+ ts = ((0x100, e[0] & 0x1FF), (0x104, (e[0] >> 18) & 0x3FF), (0x110, (e[0] >> 9) & 0x1FF),
2046
+ (0x108, e[1] & 0x1FF), (0x10C, (e[1] >> 16) & 0xFFF), (0x114, e[2] & 0x3FFFF),
2047
+ (0x118, e[3] & 0x3FFFF), (0x11C, e[4] & 0xFFFF), (0x120, (e[4] >> 16) & 0x3FFF))
2048
+ else:
2049
+ if hsi > 4:
2050
+ hsi = 0
2051
+ addr = self.tbase + hsi * self.tstride
2052
+ e = [self.t.read32(addr + self.tstep * i) for i in range(5)]
2053
+ ts = ((0x100, e[0] & 0xFF), (0x104, (e[0] >> 16) & 0x1FF), (0x110, (e[0] >> 8) & 0xFF),
2054
+ (0x108, e[1] & 0xFF), (0x10C, (e[1] >> 16) & 0x7FF), (0x114, e[2] & 0x1FFFF),
2055
+ (0x118, e[3] & 0x1FFFF), (0x11C, e[4] & 0xFFFF), (0x120, (e[4] >> 16) & 0x3FFF))
2056
+ for reg, value in ts:
2057
+ self.w(reg, value)
2058
+ # Configure hardware watchdog for long timeout
2059
+ if not (self.r(0x20) >> self.iwdg_bit) & 1:
2060
+ self.t.write32(0x40003000, 0x5555)
2061
+ self.t.write32(0x40003004, 0x06)
2062
+ self.t.write32(0x40003008, 0xFFF)
2063
+ self._kick()
2064
+ self._wait()
2065
+ self.w(0x10, 0x11) # clear EOP, WRPERR
2066
+
2067
+ def is_locked(self):
2068
+ return self.rdp and (self.r(0x20) & 0xFF) != 0xAA
2069
+
2070
+ def erase(self):
2071
+ self._setup()
2072
+ self.w(0x14, (1 << 2) | (1 << 24)) # MER | EOPIE
2073
+ self.t.write32(0x08000000, 0xFFFFFFFF)
2074
+ self._wait(60.0)
2075
+ self.w(0x14, 1 << 31)
2076
+
2077
+ def prepare(self, bank):
2078
+ self._setup()
2079
+ self.w(0x14, 0)
2080
+
2081
+ def loader_params(self, bank):
2082
+ return {'unit': 128, 'sr': self.base + 0x10, 'busy': 1 << 16, 'err': 0x10,
2083
+ 'cr': self.base + 0x14, 'trig': 1 << 19, 'pos': 124, 'flags': 2,
2084
+ 'set': (1 << 0) | (1 << 24), 'kick': 0x40003000, 'kickval': 0xAAAA}
2085
+
2086
+ def finish(self, bank):
2087
+ self.w(0x14, 1 << 31)
2088
+
2089
+ def _set_optr(self, func):
2090
+ self._setup()
2091
+ if self.r(0x14) & (1 << 30):
2092
+ self.w(0x0C, 0x08192A3B)
2093
+ self.w(0x0C, 0x4C5D6E7F)
2094
+ optr, sdkr, wrpr = self.r(0x20), self.r(0x24), self.r(0x2C)
2095
+ btcr = self.r(0x28) if self.variant == 'f002b' else None
2096
+ self.w(0x20, func(optr) & 0xFFFF)
2097
+ self.w(0x24, sdkr & 0xFFFF)
2098
+ self.w(0x2C, wrpr & 0xFFFF)
2099
+ if btcr is not None:
2100
+ self.w(0x28, btcr & 0xFFFF)
2101
+ self.w(0x14, self.r(0x14) | (1 << 17) | (1 << 24)) # OPTSTRT | EOPIE
2102
+ self.t.write32(0x40022080, 0xFF)
2103
+ self._wait(120.0)
2104
+ self.w(0x14, self.r(0x14) & ~((1 << 17) | (1 << 24)))
2105
+ try:
2106
+ self.w(0x14, self.r(0x14) | (1 << 27)) # OBL_LAUNCH
2107
+ except (ProbeError, TransferFault):
2108
+ pass
2109
+
2110
+ def unlock(self):
2111
+ self._set_optr(lambda v: (v & ~0xFF) | 0xAA)
2112
+
2113
+ def lock(self):
2114
+ if not self.rdp:
2115
+ super().lock()
2116
+ self._set_optr(lambda v: (v & ~0xFF) | 0x55)
2117
+
2118
+ def bootpin(self):
2119
+ if self.nboot1_bit is None:
2120
+ super().bootpin()
2121
+ self._set_optr(lambda v: v | (1 << self.nboot1_bit))
2122
+
2123
+ def nrst(self, gpio):
2124
+ self._set_optr(lambda v: (v & ~(1 << self.nrst_bit)) | ((1 if gpio else 0) << self.nrst_bit))
2125
+
2126
+
2127
+ # ===================================================================================
2128
+ # Microchip/ATMEL SAMD Flash Drivers
2129
+ # ===================================================================================
2130
+
2131
+ SAMD_DSU = 0x41002000
2132
+ SAMD_NVMCTRL = 0x41004000
2133
+
2134
+ class SAMDFlash(FlashDriver):
2135
+ def __init__(self, target, d5x):
2136
+ super().__init__(target)
2137
+ self.base = SAMD_NVMCTRL
2138
+ self.d5x = d5x
2139
+ param = self.r(0x08)
2140
+ self.unit = 8 << ((param >> 16) & 0x07) # page size
2141
+ self.size = (param & 0xFFFF) * self.unit
2142
+
2143
+ def is_locked(self):
2144
+ status = self.t.try_read32(SAMD_DSU + 0x100)
2145
+ if status is None:
2146
+ return True
2147
+ return bool((status >> 16) & 0x01) # STATUSB.PROT
2148
+
2149
+ def _cmd(self, cmd):
2150
+ if self.d5x:
2151
+ self.t.probe.write16(self.base + 0x10, 0xFFFF)
2152
+ self.w(0x04, 0xA500 | cmd)
2153
+ self.t.wait(self.base + 0x10, 1 << 16, 1 << 16, 2.0) # STATUS.READY
2154
+ flags = self.t.probe.read16(self.base + 0x10)
2155
+ if flags & 0x4E:
2156
+ raise Exception('NVM controller error (INTFLAG 0x%04x)' % flags)
2157
+ else:
2158
+ self.w(0x18, 0x1C)
2159
+ self.w(0x14, 0x02)
2160
+ self.w(0x00, 0xA500 | cmd)
2161
+ flags = self.t.wait(self.base + 0x14, 0x01, 0x01, 2.0) # INTFLAG.READY
2162
+ if flags & 0x02:
2163
+ raise Exception('NVM controller error (STATUS 0x%04x)' % (self.r(0x18) & 0xFFFF))
2164
+
2165
+ def _bootprot(self):
2166
+ user = self.t.read32(0x00804000)
2167
+ if self.d5x:
2168
+ return (15 - ((user >> 26) & 0x0F)) * 8192
2169
+ v = user & 0x07
2170
+ return 0 if v == 7 else 256 << (7 - v)
2171
+
2172
+ def check_image(self, data):
2173
+ if self.t.locked:
2174
+ return
2175
+ bp = self._bootprot()
2176
+ if bp and data[:bp].strip(b'\xff'):
2177
+ raise Exception('First %d bytes of flash are protected by BOOTPROT fuse (bootloader)' % bp)
2178
+
2179
+ def erase(self):
2180
+ try: # disable DSU write protection
2181
+ if self.d5x:
2182
+ self.t.write32(0x40000000, (1 << 16) | (1 << 5) | (1 << 1))
2183
+ else:
2184
+ self.t.write32(0x41000000, 1 << 1)
2185
+ except (ProbeError, TransferFault):
2186
+ if hasattr(self.t.probe, 'clear_errors'):
2187
+ self.t.probe.clear_errors()
2188
+ self.t.probe.write8(SAMD_DSU + 0x101, 0x1F) # clear STATUSA
2189
+ self.t.probe.write8(SAMD_DSU + 0x100, 0x10) # CTRL.CE (chip erase)
2190
+ start = time.time()
2191
+ while not (self.t.probe.read8(SAMD_DSU + 0x101) & 0x01): # STATUSA.DONE
2192
+ if time.time() - start > 5.0:
2193
+ raise Exception('Chip erase timeout')
2194
+ time.sleep(0.01)
2195
+ if self.t.probe.read8(SAMD_DSU + 0x101) & 0x08:
2196
+ raise Exception('Chip erase failed')
2197
+ self.t.reconnect_after_reset()
2198
+
2199
+ def prepare(self, bank):
2200
+ if self.d5x:
2201
+ self.t.probe.write16(self.base, self.t.probe.read16(self.base) & ~0x30) # WMODE = MAN
2202
+ regions, lock = 32, self.r(0x18)
2203
+ for i in range(regions):
2204
+ if not (lock >> i) & 1:
2205
+ self.w(0x14, i * (self.size // regions))
2206
+ self._cmd(0x12) # UR
2207
+ self._cmd(0x15) # PBC
2208
+ else:
2209
+ self.w(0x04, self.r(0x04) | 0x80) # CTRLB.MANW
2210
+ regions, lock = 16, self.r(0x20) & 0xFFFF
2211
+ for i in range(regions):
2212
+ if not (lock >> i) & 1:
2213
+ self.w(0x1C, (i * (self.size // regions)) >> 1)
2214
+ self._cmd(0x41) # UR
2215
+ self._cmd(0x44) # PBC
2216
+
2217
+ def loader_params(self, bank):
2218
+ if self.d5x:
2219
+ return {'unit': self.unit, 'sr': self.base + 0x10, 'busy': 1 << 16, 'xor': 1 << 16,
2220
+ 'err': 0x4E, 'cr': self.base + 0x04, 'trig': 0xA503, 'pos': self.unit}
2221
+ return {'unit': self.unit, 'sr': self.base + 0x14, 'busy': 0x01, 'xor': 0x01,
2222
+ 'err': 0x02, 'cr': self.base, 'trig': 0xA504, 'pos': self.unit}
2223
+
2224
+ def unlock(self):
2225
+ self.erase()
2226
+
2227
+ def lock(self):
2228
+ self._cmd(0x16 if self.d5x else 0x45) # SSB
2229
+ try:
2230
+ self.t.write32(AIRCR, 0x05FA0004)
2231
+ except (ProbeError, TransferFault):
2232
+ pass
2233
+
2234
+
2235
+ # ===================================================================================
2236
+ # Chip Definitions and Detection
2237
+ # ===================================================================================
2238
+
2239
+ # DBGMCU_IDCODE addresses depending on CPU core
2240
+ STM32_IDCODE_ADDR = {
2241
+ 0xC20: [0x40015800], # Cortex-M0
2242
+ 0xC60: [0x40015800], # Cortex-M0+
2243
+ 0xC23: [0xE0042000], # Cortex-M3
2244
+ 0xC24: [0xE0042000], # Cortex-M4
2245
+ 0xC27: [0x5C001000, 0xE0042000], # Cortex-M7 (H7, F7)
2246
+ 0xD21: [0xE0044000, 0x44024000], # Cortex-M33 (L5, U3, U5, WBA, H5)
2247
+ }
2248
+
2249
+ _M0 = 0x40015800
2250
+ _M4 = 0xE0042000
2251
+ _M33 = 0xE0044000
2252
+ _L4 = {'boot': 'sw', 'fz': 0xE0042008}
2253
+ _G0 = {'boot': 'sel', 'nrst': 'g0', 'fz': 0x40015808}
2254
+ _L5 = {'l5': True, 'tz': True, 'dual': True, 'fz': 0xE0044008}
2255
+
2256
+ # DEV_ID: (IDCODE address, name, driver, flash size register, max flash KB, min RAM KB, options)
2257
+ STM32_DEVICES = {
2258
+ # STM32F0/F1/F3
2259
+ 0x444: (_M0, 'STM32F03x', 'f1', 0x1FFFF7CC, 32, 4, {}),
2260
+ 0x445: (_M0, 'STM32F04x/F070x6', 'f1', 0x1FFFF7CC, 32, 6, {}),
2261
+ 0x440: (_M0, 'STM32F05x/F030x8', 'f1', 0x1FFFF7CC, 64, 8, {}),
2262
+ 0x448: (_M0, 'STM32F07x/F070xB', 'f1', 0x1FFFF7CC, 128, 16, {}),
2263
+ 0x442: (_M0, 'STM32F09x/F030xC', 'f1', 0x1FFFF7CC, 256, 32, {}),
2264
+ 0x412: (_M4, 'STM32F10x (low density)', 'f1', 0x1FFFF7E0, 32, 4, {}),
2265
+ 0x410: (_M4, 'STM32F10x (medium density)', 'f1', 0x1FFFF7E0, 128, 10, {}),
2266
+ 0x414: (_M4, 'STM32F10x (high density)', 'f1', 0x1FFFF7E0, 512, 32, {}),
2267
+ 0x430: (_M4, 'STM32F10x (XL density)', 'f1', 0x1FFFF7E0, 1024, 64, {}),
2268
+ 0x418: (_M4, 'STM32F105/F107', 'f1', 0x1FFFF7E0, 256, 64, {}),
2269
+ 0x420: (_M4, 'STM32F100 (low/medium density)', 'f1', 0x1FFFF7E0, 128, 4, {}),
2270
+ 0x428: (_M4, 'STM32F100 (high density)', 'f1', 0x1FFFF7E0, 512, 24, {}),
2271
+ 0x422: (_M4, 'STM32F302xB/C/F303xB/C/F358', 'f1', 0x1FFFF7CC, 256, 32, {}),
2272
+ 0x446: (_M4, 'STM32F302xD/E/F303xD/E/F398', 'f1', 0x1FFFF7CC, 512, 64, {}),
2273
+ 0x432: (_M4, 'STM32F37x', 'f1', 0x1FFFF7CC, 256, 16, {}),
2274
+ 0x438: (_M4, 'STM32F303x6/8/F334/F328', 'f1', 0x1FFFF7CC, 64, 12, {}),
2275
+ 0x439: (_M4, 'STM32F301/F302x6/8/F318', 'f1', 0x1FFFF7CC, 64, 16, {}),
2276
+ # STM32F2/F4/F7
2277
+ 0x411: (_M4, 'STM32F2xx', 'f4', 0x1FFF7A22, 1024, 64, {}),
2278
+ 0x413: (_M4, 'STM32F40x/F41x', 'f4', 0x1FFF7A22, 1024, 128, {}),
2279
+ 0x419: (_M4, 'STM32F42x/F43x', 'f4', 0x1FFF7A22, 2048, 192, {}),
2280
+ 0x434: (_M4, 'STM32F469/F479', 'f4', 0x1FFF7A22, 2048, 320, {}),
2281
+ 0x423: (_M4, 'STM32F401xB/C', 'f4', 0x1FFF7A22, 256, 64, {}),
2282
+ 0x433: (_M4, 'STM32F401xD/E', 'f4', 0x1FFF7A22, 512, 96, {}),
2283
+ 0x431: (_M4, 'STM32F411', 'f4', 0x1FFF7A22, 512, 128, {}),
2284
+ 0x441: (_M4, 'STM32F412', 'f4', 0x1FFF7A22, 1024, 256, {}),
2285
+ 0x458: (_M4, 'STM32F410', 'f4', 0x1FFF7A22, 128, 32, {}),
2286
+ 0x421: (_M4, 'STM32F446', 'f4', 0x1FFF7A22, 512, 128, {}),
2287
+ 0x463: (_M4, 'STM32F413/F423', 'f4', 0x1FFF7A22, 1536, 320, {}),
2288
+ 0x449: (_M4, 'STM32F74x/F75x', 'f4', 0x1FF0F442, 1024, 320, {}),
2289
+ 0x451: (_M4, 'STM32F76x/F77x', 'f4', 0x1FF0F442, 2048, 512, {}),
2290
+ 0x452: (_M4, 'STM32F72x/F73x', 'f4', 0x1FF07A22, 512, 256, {}),
2291
+ # STM32L0/L1
2292
+ 0x457: (_M0, 'STM32L01x/L02x', 'l0', 0x1FF8007C, 16, 2, {}),
2293
+ 0x425: (_M0, 'STM32L031/L041', 'l0', 0x1FF8007C, 32, 8, {}),
2294
+ 0x417: (_M0, 'STM32L05x/L06x', 'l0', 0x1FF8007C, 64, 8, {}),
2295
+ 0x447: (_M0, 'STM32L07x/L08x', 'l0', 0x1FF8007C, 192, 20, {}),
2296
+ 0x416: (_M4, 'STM32L1xx (Cat.1)', 'l0', 0x1FF8004C, 128, 10, {}),
2297
+ 0x429: (_M4, 'STM32L1xx (Cat.2)', 'l0', 0x1FF8004C, 128, 16, {}),
2298
+ 0x427: (_M4, 'STM32L1xx (Cat.3)', 'l0', 0x1FF800CC, 256, 32, {}),
2299
+ 0x436: (_M4, 'STM32L1xx (Cat.4)', 'l0', 0x1FF800CC, 384, 48, {}),
2300
+ 0x437: (_M4, 'STM32L1xx (Cat.5/6)', 'l0', 0x1FF800CC, 512, 80, {}),
2301
+ # STM32C0/G0/U0
2302
+ 0x443: (_M0, 'STM32C01x', 'l4', 0x1FFF75A0, 32, 6, _G0),
2303
+ 0x453: (_M0, 'STM32C03x', 'l4', 0x1FFF75A0, 32, 12, _G0),
2304
+ 0x44C: (_M0, 'STM32C05x', 'l4', 0x1FFF75A0, 64, 12, _G0),
2305
+ 0x493: (_M0, 'STM32C071', 'l4', 0x1FFF75A0, 128, 24, _G0),
2306
+ 0x44D: (_M0, 'STM32C09x', 'l4', 0x1FFF75A0, 256, 36, _G0),
2307
+ 0x466: (_M0, 'STM32G03x/G04x', 'l4', 0x1FFF75E0, 64, 8, _G0),
2308
+ 0x456: (_M0, 'STM32G05x/G06x', 'l4', 0x1FFF75E0, 64, 18, _G0),
2309
+ 0x460: (_M0, 'STM32G07x/G08x', 'l4', 0x1FFF75E0, 128, 36, _G0),
2310
+ 0x467: (_M0, 'STM32G0Bx/G0Cx', 'l4', 0x1FFF75E0, 512, 144, dict(_G0, dual=True)),
2311
+ 0x459: (_M0, 'STM32U031', 'l4', 0x1FFF3EA0, 64, 12, _G0),
2312
+ 0x489: (_M0, 'STM32U073/U083', 'l4', 0x1FFF6EA0, 256, 40, _G0),
2313
+ # STM32G4/L4/L4+/WB/WL
2314
+ 0x468: (_M4, 'STM32G43x/G44x', 'l4', 0x1FFF75E0, 128, 32, {'boot': 'sw', 'nrst': 'g4', 'fz': 0xE0042008}),
2315
+ 0x469: (_M4, 'STM32G47x/G48x', 'l4', 0x1FFF75E0, 512, 128, {'boot': 'sw', 'nrst': 'g4', 'fz': 0xE0042008, 'dual': True}),
2316
+ 0x479: (_M4, 'STM32G49x/G4Ax', 'l4', 0x1FFF75E0, 512, 112, {'boot': 'sw', 'nrst': 'g4', 'fz': 0xE0042008}),
2317
+ 0x415: (_M4, 'STM32L47x/L48x', 'l4', 0x1FFF75E0, 1024, 96, dict(_L4, dual=True)),
2318
+ 0x435: (_M4, 'STM32L43x/L44x', 'l4', 0x1FFF75E0, 256, 48, _L4),
2319
+ 0x461: (_M4, 'STM32L49x/L4Ax', 'l4', 0x1FFF75E0, 1024, 256, dict(_L4, dual=True)),
2320
+ 0x462: (_M4, 'STM32L45x/L46x', 'l4', 0x1FFF75E0, 512, 128, _L4),
2321
+ 0x464: (_M4, 'STM32L41x/L42x', 'l4', 0x1FFF75E0, 128, 40, _L4),
2322
+ 0x470: (_M4, 'STM32L4Rx/L4Sx', 'l4', 0x1FFF75E0, 2048, 192, dict(_L4, dual=True)),
2323
+ 0x471: (_M4, 'STM32L4Px/L4Qx', 'l4', 0x1FFF75E0, 1024, 192, dict(_L4, dual=True)),
2324
+ 0x494: (_M4, 'STM32WB1x', 'l4', 0x1FFF75E0, 320, 12, {'base': 0x58004000, 'boot': 'sw'}),
2325
+ 0x495: (_M4, 'STM32WB5x', 'l4', 0x1FFF75E0, 1024, 192, {'base': 0x58004000, 'boot': 'sw'}),
2326
+ 0x496: (_M4, 'STM32WB3x', 'l4', 0x1FFF75E0, 512, 96, {'base': 0x58004000, 'boot': 'sw'}),
2327
+ 0x497: (_M4, 'STM32WLE/WL5x', 'l4', 0x1FFF75E0, 256, 32, {'base': 0x58004000, 'boot': 'sw'}),
2328
+ # STM32L5/U3/U5/WBA
2329
+ 0x472: (_M33, 'STM32L55x/L56x', 'l4', 0x0BFA05E0, 512, 192, dict(_L5, boot='sw5')),
2330
+ 0x455: (_M33, 'STM32U535/U545', 'l4', 0x0BFA07A0, 512, 192, dict(_L5, quad=True, boot='sw5')),
2331
+ 0x482: (_M33, 'STM32U57x/U58x', 'l4', 0x0BFA07A0, 2048, 192, dict(_L5, quad=True, boot='sw5')),
2332
+ 0x481: (_M33, 'STM32U59x/U5Ax', 'l4', 0x0BFA07A0, 4096, 192, dict(_L5, quad=True, boot='sw5')),
2333
+ 0x476: (_M33, 'STM32U5Fx/U5Gx', 'l4', 0x0BFA07A0, 4096, 192, dict(_L5, quad=True, boot='sw5')),
2334
+ 0x454: (_M33, 'STM32U37x/U38x', 'l4', 0x0BFA07A0, 1024, 64, _L5),
2335
+ 0x42A: (_M33, 'STM32U3Bx/U3Cx', 'l4', 0x0BFA07A0, 2048, 64, _L5),
2336
+ 0x492: (_M33, 'STM32WBA5x', 'l4', 0x0BF907A0, 1024, 64, dict(_L5, quad=True, dual=False)),
2337
+ 0x4B0: (_M33, 'STM32WBA6x', 'l4', 0x0BFA07A0, 2048, 64, dict(_L5, quad=True)),
2338
+ 0x4B2: (_M33, 'STM32WBA2x', 'l4', 0x0BF8D7A0, 512, 32, dict(_L5, dual=False)),
2339
+ # STM32H5/H7
2340
+ 0x474: (0x44024000, 'STM32H503', 'h5', 0x08FFF80C, 128, 32, {}),
2341
+ 0x478: (0x44024000, 'STM32H52x/H53x', 'h5', 0x08FFF80C, 512, 128, {}),
2342
+ 0x484: (0x44024000, 'STM32H56x/H57x', 'h5', 0x08FFF80C, 2048, 256, {}),
2343
+ 0x450: (0x5C001000, 'STM32H74x/H75x', 'h7', 0x1FF1E880, 2048, 128, {}),
2344
+ 0x480: (0x5C001000, 'STM32H7Ax/H7Bx', 'h7', 0x08FFF80C, 2048, 128, {}),
2345
+ 0x483: (0x5C001000, 'STM32H72x/H73x', 'h7', 0x1FF1E880, 1024, 128, {}),
2346
+ }
2347
+
2348
+ def stm32_chip(target, part, devid, dev):
2349
+ addr, name, kind, fsize_addr, maxkb, ramkb, opts = dev
2350
+ v = target.try_read32(fsize_addr & ~3)
2351
+ kb = 0 if v is None else (v >> (8 * (fsize_addr & 2))) & 0xFFFF
2352
+ if devid == 0x436: # L1 Cat.4: 0 = 384 KB, 1 = 256 KB
2353
+ kb = 256 if kb == 1 else 384
2354
+ if kb == 0 or kb == 0xFFFF or kb > maxkb:
2355
+ kb = maxkb
2356
+ size = kb * 1024
2357
+ base = 0x08000000
2358
+ regions = None
2359
+ bootpin, nrst, lock = 'fixed', False, True
2360
+ if kind == 'f1':
2361
+ drv = STM32F1Flash(target, part, devid, size)
2362
+ if devid == 0x430 and size > 0x80000:
2363
+ regions = [(base, 0x80000, 0), (base + 0x80000, size - 0x80000, 1)]
2364
+ if part != 0xC23:
2365
+ bootpin = 'option'
2366
+ elif kind == 'f4':
2367
+ drv = STM32F4Flash(target, part, devid, size)
2368
+ elif kind == 'l0':
2369
+ drv = STM32L0Flash(target, part, devid, size)
2370
+ elif kind == 'l4':
2371
+ drv = STM32L4Flash(target, part, devid, size, opts)
2372
+ bootpin = 'option' if opts.get('boot') else None
2373
+ nrst = opts.get('nrst') is not None
2374
+ elif kind == 'h7':
2375
+ drv = STM32H7Flash(target, part, devid, size)
2376
+ if devid in (0x450, 0x480) and size >= 0x100000:
2377
+ half = size // 2
2378
+ regions = [(base, half, 0), (base + 0x100000, half, 1)]
2379
+ else:
2380
+ drv = STM32H5Flash(target, part, devid, size)
2381
+ lock = False
2382
+ return Chip(name, drv, base, size, 0x20000000, ramkb * 1024, regions, bootpin, nrst, lock)
2383
+
2384
+ def py32_chip(target, idcode):
2385
+ if idcode is None:
2386
+ return None
2387
+ if idcode == 0x06188061:
2388
+ v = target.try_read32(0x1FFF31FC)
2389
+ if v is None:
2390
+ return None
2391
+ flash = ((v & 7) + 1) << 14 # 16 KB units (max. 128 KB)
2392
+ return Chip('PY32F07x', PY32Flash(target, 'f07x'), 0x08000000, flash, 0x20000000, 0x2000,
2393
+ bootpin='option', nrst=True)
2394
+ if (idcode & 0xFFF) != 0 or idcode in (0, 0xFFFFFFFF):
2395
+ return None
2396
+ v = target.try_read32(0x1FFF0FFC)
2397
+ t = target.try_read32(0x1FFF0F1C)
2398
+ if v not in (None, 0, 0xFFFFFFFF) and t not in (None, 0, 0xFFFFFFFF):
2399
+ flash, ram = ((v & 7) + 1) << 13, (((v >> 4) & 3) + 1) << 11
2400
+ return Chip('PY32F002A/F003/F030', PY32Flash(target, 'f0xx'), 0x08000000, flash, 0x20000000, ram,
2401
+ bootpin='option', nrst=True)
2402
+ t = target.try_read32(0x1FFF011C)
2403
+ if t not in (None, 0, 0xFFFFFFFF):
2404
+ return Chip('PY32F002B', PY32Flash(target, 'f002b'), 0x08000000, 0x6000, 0x20000000, 0xC00,
2405
+ bootpin=None, nrst=True, lock=False)
2406
+ return None
2407
+
2408
+ SAMD_FAMILIES = { # (processor, family, series): name
2409
+ (1, 0, 0): 'ATSAMD20', (1, 0, 1): 'ATSAMD21', (1, 0, 2): 'ATSAMD10', (1, 0, 3): 'ATSAMD11',
2410
+ (1, 0, 4): 'ATSAMD09', (1, 1, 1): 'ATSAML21', (1, 1, 2): 'ATSAML22', (1, 2, 0): 'ATSAMC20',
2411
+ (1, 2, 1): 'ATSAMC21', (6, 0, 6): 'ATSAMD51', (6, 3, 1): 'ATSAME51', (6, 3, 3): 'ATSAME53',
2412
+ (6, 3, 4): 'ATSAME54',
2413
+ }
2414
+
2415
+ def samd_chip(target, did):
2416
+ if did is None:
2417
+ return None
2418
+ key = (did >> 28, (did >> 23) & 0x1F, (did >> 16) & 0x3F)
2419
+ name = SAMD_FAMILIES.get(key)
2420
+ if name is None:
2421
+ return None
2422
+ d5x = key[0] == 6
2423
+ drv = SAMDFlash(target, d5x) if target.halted else SAMDLockedFlash(target, d5x)
2424
+ return Chip(name, drv, 0x00000000, drv.size, 0x20000000, 0x10000 if d5x else 0x1000, bootpin=None)
2425
+
2426
+ class SAMDLockedFlash(SAMDFlash):
2427
+ def __init__(self, target, d5x):
2428
+ FlashDriver.__init__(self, target)
2429
+ self.base = SAMD_NVMCTRL
2430
+ self.d5x = d5x
2431
+ self.unit = 64
2432
+ self.size = 0
2433
+
2434
+ def print_supported_chips():
2435
+ print('Supported chips:')
2436
+ names = sorted(set(d[1] for d in STM32_DEVICES.values()))
2437
+ line = ''
2438
+ for n in names:
2439
+ if len(line) + len(n) > 76:
2440
+ print(' ' + line.rstrip(', '))
2441
+ line = ''
2442
+ line += n + ', '
2443
+ print(' ' + line.rstrip(', '))
2444
+ print(' PY32F002A, PY32F003, PY32F030, PY32F002B, PY32F071, PY32F072')
2445
+ print(' ' + ', '.join(sorted(set(SAMD_FAMILIES.values()))))
2446
+
2447
+
2448
+ # ===================================================================================
2449
+ # Generic Flash Loader (Thumb code, Cortex-M0 compatible, see loader.S)
2450
+ # ===================================================================================
2451
+
2452
+ LOADER_CODE = (
2453
+ 0x6801, 0x6842, 0x6883, 0x2B00, 0xD03A, 0x6B45, 0x2D00, 0xD003, 0x6A46, 0x6837,
2454
+ 0x432F, 0x6037, 0x2400, 0x6AC5, 0x42AC, 0xD101, 0xF000, 0xF833, 0x68C5, 0x42AC,
2455
+ 0xD20A, 0x6B05, 0x086D, 0xD203, 0x590D, 0x5115, 0x3404, 0xE7F0, 0x5B0D, 0x5315,
2456
+ 0x3402, 0xE7EC, 0xF3BF, 0x8F4F, 0x6B86, 0x2E00, 0xD001, 0x6BC7, 0x6037, 0x6946,
2457
+ 0x6836, 0x69C7, 0x407E, 0x6987, 0x423E, 0xD1F3, 0x6946, 0x6836, 0x6A07, 0x423E,
2458
+ 0xD10E, 0x6B45, 0x2D00, 0xD003, 0x6A46, 0x6837, 0x43AF, 0x6037, 0x68C5, 0x1949,
2459
+ 0x6905, 0x1952, 0x3B01, 0xE7C2, 0x2000, 0xBE00, 0x6402, 0x0030, 0xBE00, 0x6A46,
2460
+ 0x6A87, 0x6B05, 0x08AD, 0xD301, 0x6835, 0x432F, 0x6037, 0x4770,
2461
+ )
2462
+
2463
+
2464
+ # ===================================================================================
2465
+
2466
+ if __name__ == "__main__":
2467
+ _main()