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()
|