tinyupdi 1.5.0__tar.gz

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.
tinyupdi-1.5.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Stefan Wagner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,97 @@
1
+ Metadata-Version: 2.1
2
+ Name: tinyupdi
3
+ Version: 1.5.0
4
+ Summary: Minimal UPDI Programming Tool for tinyAVR
5
+ Home-page: https://github.com/wagiminator/MCU-Flash-Tools
6
+ Author: Stefan Wagner
7
+ Author-email: Stefan Wagner <wagiminator@web.de>
8
+ Maintainer-email: Stefan Wagner <wagiminator@web.de>
9
+ License: MIT License
10
+
11
+ Copyright (c) 2022 Stefan Wagner
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ Project-URL: Homepage, https://wagiminator.github.io/
31
+ Project-URL: Repository, https://github.com/wagiminator/MCU-Flash-Tools
32
+ Project-URL: Issues, https://github.com/wagiminator/MCU-Flash-Tools/issues
33
+ Keywords: AVR,Microchip,ATtiny,tinyAVR,UPDI,microcontroller
34
+ Classifier: Development Status :: 5 - Production/Stable
35
+ Classifier: Programming Language :: Python :: 3
36
+ Classifier: License :: OSI Approved :: MIT License
37
+ Classifier: Operating System :: OS Independent
38
+ Classifier: Intended Audience :: Developers
39
+ Classifier: Topic :: Software Development :: Embedded Systems
40
+ Requires-Python: >=3.8
41
+ Description-Content-Type: text/markdown
42
+ License-File: LICENSE
43
+ Requires-Dist: pyserial
44
+
45
+ # tinyUPDI - Minimal UPDI Programming Tool for tinyAVR
46
+ ## Description
47
+ This tool allows tinyAVR series 0, 1, and 2 microcontrollers to be programmed using a USB-to-serial converter connected in a special way to the UPDI pin (also called SerialUPDI). More information can be found [here](https://github.com/SpenceKonde/AVR-Guidance/blob/master/UPDI/jtag2updi.md).
48
+
49
+ ## Preparations
50
+ If necessary, a driver for the USB-to-serial converter used must be installed.
51
+
52
+ ## Installation
53
+ - Install [Python3](https://www.pythontutorial.net/getting-started/install-python/).
54
+ - Install [pip](https://pip.pypa.io/en/stable/installation/).
55
+ - Install rvprog:
56
+
57
+ ```
58
+ pip install tinyupdi
59
+ ```
60
+
61
+ ## Usage
62
+ Connect the USB-to-serial converter via USB to the PC and via the circuit described below to the UPDI pin of the microcontroller.
63
+
64
+ ```
65
+ USB2SERIAL tinyAVR
66
+ +--------+ +-----------+
67
+ | RXD| <------------+---> |UPDI (PA0) |
68
+ | | | | |
69
+ | TXD| ---|1kOhm|---+ | |
70
+ | | | |
71
+ | VDD| -----------------> |VDD |
72
+ | GND| -----------------> |GND |
73
+ +--------+ +-----------+
74
+ ```
75
+
76
+ Alternatively, a pre-assembled [SerialUPDI programmer](https://github.com/wagiminator/AVR-Programmer) can be used.
77
+
78
+ ```
79
+ Usage: tinyupdi [-h] [-d DEVICE] [-e] [-f FLASH] [-fs [FUSES [FUSES ...]]] [-t TRIM]
80
+
81
+ Optional arguments:
82
+ -h, --help show help message and exit
83
+ -d, --device set target device (if not set, it will be auto-detected)
84
+ -e, --erase perform a chip erase (implied with --flash)
85
+ -f FLASH, --flash FLASH BIN file to flash
86
+ -fs [FUSES [FUSES ...]], --fuses [FUSES [FUSES ...]]
87
+ fuses to set (syntax: fuse_nr:0xvalue)
88
+ -t TRIM, --trim TRIM configure oscillator for given frequency (set fuse 2)
89
+ Example:
90
+ tinyupdi -f firmware.bin -fs 6:0x04 7:0x00 8:0x00 -t 8000000
91
+ ```
92
+
93
+ ## Links
94
+ 1. [MCU Templates](https://github.com/wagiminator/MCU-Templates)
95
+ 2. [MCU Development Boards](https://github.com/wagiminator/Development-Boards)
96
+ 3. [AVR Development Boards](https://github.com/wagiminator/AVR-Development-Boards)
97
+ 4. [SerialUPDI Programmers](https://github.com/wagiminator/AVR-Programmer)
@@ -0,0 +1,53 @@
1
+ # tinyUPDI - Minimal UPDI Programming Tool for tinyAVR
2
+ ## Description
3
+ This tool allows tinyAVR series 0, 1, and 2 microcontrollers to be programmed using a USB-to-serial converter connected in a special way to the UPDI pin (also called SerialUPDI). More information can be found [here](https://github.com/SpenceKonde/AVR-Guidance/blob/master/UPDI/jtag2updi.md).
4
+
5
+ ## Preparations
6
+ If necessary, a driver for the USB-to-serial converter used must be installed.
7
+
8
+ ## Installation
9
+ - Install [Python3](https://www.pythontutorial.net/getting-started/install-python/).
10
+ - Install [pip](https://pip.pypa.io/en/stable/installation/).
11
+ - Install rvprog:
12
+
13
+ ```
14
+ pip install tinyupdi
15
+ ```
16
+
17
+ ## Usage
18
+ Connect the USB-to-serial converter via USB to the PC and via the circuit described below to the UPDI pin of the microcontroller.
19
+
20
+ ```
21
+ USB2SERIAL tinyAVR
22
+ +--------+ +-----------+
23
+ | RXD| <------------+---> |UPDI (PA0) |
24
+ | | | | |
25
+ | TXD| ---|1kOhm|---+ | |
26
+ | | | |
27
+ | VDD| -----------------> |VDD |
28
+ | GND| -----------------> |GND |
29
+ +--------+ +-----------+
30
+ ```
31
+
32
+ Alternatively, a pre-assembled [SerialUPDI programmer](https://github.com/wagiminator/AVR-Programmer) can be used.
33
+
34
+ ```
35
+ Usage: tinyupdi [-h] [-d DEVICE] [-e] [-f FLASH] [-fs [FUSES [FUSES ...]]] [-t TRIM]
36
+
37
+ Optional arguments:
38
+ -h, --help show help message and exit
39
+ -d, --device set target device (if not set, it will be auto-detected)
40
+ -e, --erase perform a chip erase (implied with --flash)
41
+ -f FLASH, --flash FLASH BIN file to flash
42
+ -fs [FUSES [FUSES ...]], --fuses [FUSES [FUSES ...]]
43
+ fuses to set (syntax: fuse_nr:0xvalue)
44
+ -t TRIM, --trim TRIM configure oscillator for given frequency (set fuse 2)
45
+ Example:
46
+ tinyupdi -f firmware.bin -fs 6:0x04 7:0x00 8:0x00 -t 8000000
47
+ ```
48
+
49
+ ## Links
50
+ 1. [MCU Templates](https://github.com/wagiminator/MCU-Templates)
51
+ 2. [MCU Development Boards](https://github.com/wagiminator/Development-Boards)
52
+ 3. [AVR Development Boards](https://github.com/wagiminator/AVR-Development-Boards)
53
+ 4. [SerialUPDI Programmers](https://github.com/wagiminator/AVR-Programmer)
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tinyupdi"
7
+ version = "1.5.0"
8
+ authors = [
9
+ { name="Stefan Wagner", email="wagiminator@web.de" },
10
+ ]
11
+ maintainers = [
12
+ { name="Stefan Wagner", email="wagiminator@web.de" },
13
+ ]
14
+ description = "Minimal UPDI Programming Tool for tinyAVR"
15
+ readme = "README.md"
16
+ license = {file = "LICENSE"}
17
+ requires-python = ">=3.8"
18
+ dependencies = [
19
+ "pyserial",
20
+ ]
21
+ keywords = ["AVR", "Microchip", "ATtiny", "tinyAVR", "UPDI", "microcontroller"]
22
+ classifiers = [
23
+ "Development Status :: 5 - Production/Stable",
24
+ "Programming Language :: Python :: 3",
25
+ "License :: OSI Approved :: MIT License",
26
+ "Operating System :: OS Independent",
27
+ "Intended Audience :: Developers",
28
+ "Topic :: Software Development :: Embedded Systems",
29
+ ]
30
+
31
+ [project.scripts]
32
+ tinyupdi = "tinyupdi:_main"
33
+
34
+ [project.urls]
35
+ Homepage = "https://wagiminator.github.io/"
36
+ Repository = "https://github.com/wagiminator/MCU-Flash-Tools"
37
+ Issues = "https://github.com/wagiminator/MCU-Flash-Tools/issues"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,15 @@
1
+ from setuptools import setup
2
+
3
+ setup(
4
+ name='tinyupdi',
5
+ version='1.5.0',
6
+ description='Minimal UPDI Programming Tool for tinyAVR',
7
+ url='https://github.com/wagiminator/MCU-Flash-Tools',
8
+ author='Stefan Wagner',
9
+ author_email='wagiminator@web.de',
10
+ license='MIT License',
11
+ packages=['tinyupdi'],
12
+ install_requires=[
13
+ 'pyserial',
14
+ ],
15
+ )
@@ -0,0 +1 @@
1
+ from .tinyupdi import _main
@@ -0,0 +1,769 @@
1
+ #!/usr/bin/env python3
2
+ # ===================================================================================
3
+ # Project: TinyUPDI - Minimal application-specific UPDI programmer based on pyupdi
4
+ # Version: v1.5
5
+ # Year: 2022
6
+ # Author: Stefan Wagner
7
+ # Github: https://github.com/wagiminator
8
+ # License: MIT License
9
+ # ===================================================================================
10
+ #
11
+ # Description:
12
+ # ------------
13
+ # TinyUPDI is a serial UPDI programmer software that is adapted to a specific
14
+ # application and reduced to the functions required for this. It is normally used
15
+ # for projects with a tinyAVR and a built-in USB to serial adapter to simplify
16
+ # firmware uploading.
17
+ #
18
+ # References:
19
+ # -----------
20
+ # TinyUPDI is essentially based on pyupdi and pymcuprog:
21
+ # - https://github.com/mraardvark/pyupdi
22
+ # - https://github.com/microchip-pic-avr-tools/pymcuprog
23
+ #
24
+ # Dependencies:
25
+ # -------------
26
+ # - pyserial
27
+ #
28
+ # Restrictions:
29
+ # -------------
30
+ # - Target MCU must be a 0, 1, or 2-series tinyAVR.
31
+ # - Only flash and fuses can be programmed.
32
+ # - Firmware file must be a binary (not ELF or HEX!).
33
+ #
34
+ # Operating Instructions:
35
+ # -----------------------
36
+ # - python3 tinyupdi.py [-h] [-d DEVICE] [-e] [-f FLASH] [-fs [FUSES [FUSES ...]]]
37
+ # -h, --help show help message and exit
38
+ # -d, --device set target device (if not set, it will be auto-detected)
39
+ # -e, --erase perform a chip erase (implied with --flash)
40
+ # -f FLASH, --flash FLASH BIN file to flash
41
+ # -fs [FUSES [FUSES ...]], --fuses [FUSES [FUSES ...]]
42
+ # fuses to set (syntax: fuse_nr:0xvalue)
43
+ # -t TRIM, --trim TRIM configure oscillator for given frequency (fuse 2)
44
+ # - Example:
45
+ # python3 tinyupdi.py -d attiny202 -f firmware.bin -fs 6:0x04 7:0x00 8:0x00 -t 8000000
46
+
47
+
48
+ import re
49
+ import sys
50
+ import time
51
+ import argparse
52
+ import serial
53
+ from serial import Serial
54
+ from serial.tools.list_ports import comports
55
+
56
+ # If the PID/VID of the USB-to-Serial converter is known, it can be defined here,
57
+ # which can make the auto-detection a lot faster.
58
+ #VID = '1A86'
59
+ #PID = '7523'
60
+
61
+
62
+ # ===================================================================================
63
+ # Main Function
64
+ # ===================================================================================
65
+
66
+ def _main():
67
+ # Parse command line arguments
68
+ parser = argparse.ArgumentParser(description='Minimal command line'
69
+ ' interface for UPDI programming')
70
+ parser.add_argument('-d', '--device', choices=get_supported_devices(),
71
+ help='target device (if not set, it will be auto-detected)')
72
+ parser.add_argument("-e", "--erase", action="store_true",
73
+ help="perform a chip erase (implied with --flash)")
74
+ parser.add_argument('-f', '--flash', help='BIN file to flash')
75
+ parser.add_argument('-fs', '--fuses', action='append', nargs='*',
76
+ help='fuses to set (syntax: fuse_nr:0xvalue)')
77
+ parser.add_argument('-t', '--trim', help='configure oscillator for given frequency (fuse 2)')
78
+ args = parser.parse_args(sys.argv[1:])
79
+
80
+ # Check arguments
81
+ if not any( (args.fuses, args.flash, args.erase) ):
82
+ print('No arguments - no action!')
83
+ sys.exit(0)
84
+
85
+ if args.fuses is not None:
86
+ fuselist = list()
87
+ for fslist in args.fuses:
88
+ for fsarg in fslist:
89
+ if not re.match('^[0-9]+:0x[0-9a-fA-F]+$', fsarg):
90
+ sys.stderr.write('ERROR: Bad fuses format {}. Expected fuse_nr:0xvalue\n'.format(fsarg))
91
+ sys.exit(1)
92
+ lst = fsarg.split(':0x')
93
+ fuselist.append( (int(lst[0]), int(lst[1], 16)) )
94
+
95
+ # Establish connection via serial to UPDI device
96
+ try:
97
+ print('Connecting to device ...')
98
+ tinyupdi = Programmer()
99
+ except Exception as ex:
100
+ sys.stderr.write('ERROR: ' + str(ex) + '!\n')
101
+ sys.exit(1)
102
+ print('SUCCESS: Found programmer on port %s.' % tinyupdi.port)
103
+
104
+ # Programming the device
105
+ try:
106
+ # Detect target device
107
+ print('Detecting target MCU ...')
108
+ device = tinyupdi.detect()
109
+ print('SUCCESS: Found %s with %d bytes of flash.' % (device, tinyupdi.flash_size))
110
+ if (args.device is not None) and (not args.device == device):
111
+ raise Exception('Found device is not ' + args.device)
112
+
113
+ # Perform chip erase
114
+ if (args.erase) or (args.flash is not None):
115
+ print('Performing chip erase ...')
116
+ tinyupdi.erase()
117
+
118
+ # Flash binary file
119
+ if args.flash is not None:
120
+ print('Flashing %s to %s ...' % (args.flash, device))
121
+ with open(args.flash, 'rb') as f: data = f.read()
122
+ tinyupdi.flash_data(data)
123
+ print('Verifying ...')
124
+ tinyupdi.verify_data(data)
125
+ print('SUCCESS: %d bytes written and verified.' % len(data))
126
+
127
+ # Write fuses
128
+ if args.fuses is not None:
129
+ print('Writing fuses ...')
130
+ for fuse in fuselist:
131
+ tinyupdi.set_fuse(fuse[0], fuse[1])
132
+ print('Verifying fuses ...')
133
+ for fuse in fuselist:
134
+ tinyupdi.verify_fuse(fuse[0], fuse[1])
135
+ print('SUCCESS: Fuses written and verified.')
136
+
137
+ # Write fuse to set oscillator according to frequency
138
+ if args.trim is not None:
139
+ print('Setting oscillator for %s Hz ...' % args.trim)
140
+ osc = tinyupdi.trim(int(args.trim))
141
+ print('SUCCESS: Oscillator runs at %d MHz with corresponding factory calibration.' % osc)
142
+
143
+ except Exception as ex:
144
+ sys.stderr.write('ERROR: ' + str(ex) + '!\n')
145
+ tinyupdi.exit()
146
+ sys.exit(1)
147
+
148
+ # Finish all up
149
+ tinyupdi.exit()
150
+ print('DONE.')
151
+ sys.exit(0)
152
+
153
+
154
+ # ===================================================================================
155
+ # Programmer Class
156
+ # ===================================================================================
157
+
158
+ class Programmer(Serial):
159
+ def __init__(self):
160
+ super().__init__(baudrate = 230400, timeout = 1, parity=serial.PARITY_EVEN, stopbits=serial.STOPBITS_TWO)
161
+ self.syscfg_address = 0x0F00
162
+ self.nvmctrl_address = 0x1000
163
+ self.sigrow_address = 0x1100
164
+ self.fuses_address = 0x1280
165
+ self.userrow_address = 0x1300
166
+ self.flash_pagesize = 64
167
+ self.flash_start = 0x8000
168
+ self.flash_size = 0x0800
169
+ self.identify()
170
+
171
+ # Identify port of programmer and enter programming mode
172
+ def identify(self):
173
+ for p in comports():
174
+ if (('VID' not in globals()) or (VID in p.hwid)) and (('PID' not in globals()) or (PID in p.hwid)):
175
+ self.port = p.device
176
+ try:
177
+ self.open()
178
+ except:
179
+ continue
180
+ self.__send_double_break()
181
+ self.__init()
182
+ if not self.__check():
183
+ self.close()
184
+ continue
185
+ try:
186
+ self.__enter_progmode()
187
+ except:
188
+ self.__unlock()
189
+ return
190
+ raise Exception('Device not found')
191
+
192
+
193
+ # ------------------------------------------------------------------------------
194
+ # High-Level Functions
195
+ # ------------------------------------------------------------------------------
196
+
197
+ # Detect device
198
+ def detect(self):
199
+ deviceid = self.__get_device_id()
200
+ for dev in UPDI_DEVICES:
201
+ if dev['device_id'] == deviceid:
202
+ self.flash_size = dev['flash_size']
203
+ self.flash_pagesize = dev['flash_pagesize']
204
+ return dev['name']
205
+ raise Exception('Unknown or unsupported device')
206
+
207
+ # Disable UPDI and serial connection
208
+ def exit(self):
209
+ self.__reset(apply_reset=True)
210
+ self.__reset(apply_reset=False)
211
+ self.__stcs(UPDI_CS_CTRLB, (1 << UPDI_CTRLB_UPDIDIS_BIT) | (1 << UPDI_CTRLB_CCDETDIS_BIT))
212
+ self.close()
213
+
214
+ # Does a chip erase using the NVM controller (v0)
215
+ def erase(self):
216
+ if not self.__wait_nvm_ready():
217
+ raise Exception('Timeout waiting for flash ready before erase')
218
+ self.__execute_nvm_command(UPDI_V0_NVMCTRL_CTRLA_CHIP_ERASE)
219
+ if not self.__wait_nvm_ready():
220
+ raise Exception('Timeout waiting for flash ready after erase')
221
+
222
+ # Flash bin file
223
+ def flash_bin(self, filename):
224
+ with open(filename, 'rb') as f: data = f.read()
225
+ self.flash_data(data)
226
+ return len(data)
227
+
228
+ # Verify bin file
229
+ def verify_bin(self, filename):
230
+ with open(filename, 'rb') as f: data = f.read()
231
+ self.verify_data(data)
232
+ return len(data)
233
+
234
+ # Flash data
235
+ def flash_data(self, data):
236
+ if len(data) > self.flash_size:
237
+ raise Exception('Not enough memory')
238
+ self.__write_flash(self.flash_start, data)
239
+
240
+ # Verify data
241
+ def verify_data(self, data):
242
+ if len(data) > self.flash_size:
243
+ raise Exception('Not enough memory')
244
+ readback = self.__read_flash(self.flash_start, len(data))
245
+ if data != readback:
246
+ raise Exception('Flash verification failure')
247
+
248
+ # Set single fuse
249
+ def set_fuse(self, fusenum, value):
250
+ self.__write_fuse(fusenum, value)
251
+
252
+ # Verify single fuse
253
+ def verify_fuse(self, fusenum, value):
254
+ actual_val = self.__read_fuse(fusenum)
255
+ if actual_val != value:
256
+ raise Exception('Fuse ' + str(fusenum) + ' verification failure')
257
+
258
+ # Configure oscillator for given system freqency
259
+ def trim(self, freq):
260
+ for x in [1, 2, 4, 8, 16, 32, 64, 6, 10, 12, 24, 48]:
261
+ if round(20000000 / x) == freq:
262
+ self.set_fuse(0x02, 0x02)
263
+ self.verify_fuse(0x02, 0x02)
264
+ return 20
265
+ if round(16000000 / x) == freq:
266
+ self.set_fuse(0x02, 0x01)
267
+ self.verify_fuse(0x02, 0x01)
268
+ return 16
269
+ raise Exception('Unsupported frequency')
270
+
271
+
272
+ # ------------------------------------------------------------------------------
273
+ # Physical Layer for UPDI Stack
274
+ # ------------------------------------------------------------------------------
275
+
276
+ # Send a double break to reset the UPDI port
277
+ def __send_double_break(self):
278
+ self.close()
279
+ temporary_serial = Serial(self.port, 300, stopbits=serial.STOPBITS_ONE, timeout=1)
280
+ temporary_serial.write([UPDI_BREAK, UPDI_BREAK])
281
+ temporary_serial.read(2)
282
+ temporary_serial.close()
283
+ self.open()
284
+
285
+ # Send a char array to UPDI with NO inter-byte delay
286
+ def __send(self, command):
287
+ self.write(command)
288
+ self.read(len(command))
289
+
290
+ # Receive a frame of a known number of chars from UPDI
291
+ def __receive(self, size):
292
+ return self.read(size)
293
+
294
+
295
+ # ------------------------------------------------------------------------------
296
+ # Datalink Layer for UPDI Stack
297
+ # ------------------------------------------------------------------------------
298
+
299
+ # Set the inter-byte delay bit and disable collision detection
300
+ def __init(self):
301
+ self.__stcs(UPDI_CS_CTRLB, 1 << UPDI_CTRLB_CCDETDIS_BIT)
302
+ self.__stcs(UPDI_CS_CTRLA, 1 << UPDI_CTRLA_IBDLY_BIT)
303
+
304
+ # Check UPDI by loading CS STATUSA
305
+ def __check(self):
306
+ if self.__ldcs(UPDI_CS_STATUSA) != 0:
307
+ return True
308
+ return False
309
+
310
+ # Load data from Control/Status space
311
+ def __ldcs(self, address):
312
+ self.__send([UPDI_PHY_SYNC, UPDI_LDCS | (address & 0x0F)])
313
+ response = self.__receive(1)
314
+ if len(response) != 1:
315
+ return 0x00
316
+ return response[0]
317
+
318
+ # Store a value to Control/Status space
319
+ def __stcs(self, address, value):
320
+ self.__send([UPDI_PHY_SYNC, UPDI_STCS | (address & 0x0F), value])
321
+
322
+ # Load a single byte direct from a 16-bit address
323
+ def __ld(self, address):
324
+ self.__send([UPDI_PHY_SYNC, UPDI_LDS | UPDI_ADDRESS_16 | UPDI_DATA_8,
325
+ address & 0xFF, (address >> 8) & 0xFF])
326
+ return self.__receive(1)[0]
327
+
328
+ # Load a 16-bit word directly from a 16-bit address
329
+ def __ld16(self, address):
330
+ self.__send([UPDI_PHY_SYNC, UPDI_LDS | UPDI_ADDRESS_16 | UPDI_DATA_16,
331
+ address & 0xFF, (address >> 8) & 0xFF])
332
+ return self.__receive(2)
333
+
334
+ # Store a single byte value directly to a 16-bit address
335
+ def __st(self, address, value):
336
+ self.__send([UPDI_PHY_SYNC, UPDI_STS | UPDI_ADDRESS_16 | UPDI_DATA_8,
337
+ address & 0xFF, (address >> 8) & 0xFF])
338
+ response = self.__receive(1)
339
+ if len(response) != 1 or response[0] != UPDI_PHY_ACK:
340
+ raise Exception('Failed to communicate with device (st)')
341
+ self.__send([value & 0xFF])
342
+ response = self.__receive(1)
343
+ if len(response) != 1 or response[0] != UPDI_PHY_ACK:
344
+ raise Exception('Failed to communicate with device (st)')
345
+
346
+ # Store a 16-bit word value directly to a 16-bit address
347
+ def __st16(self, address, value):
348
+ self.__send([UPDI_PHY_SYNC, UPDI_STS | UPDI_ADDRESS_16 | UPDI_DATA_16,
349
+ address & 0xFF, (address >> 8) & 0xFF])
350
+ response = self.__receive(1)
351
+ if len(response) != 1 or response[0] != UPDI_PHY_ACK:
352
+ raise Exception('Failed to communicate with device (st)')
353
+ self.__send([value & 0xFF, (value >> 8) & 0xFF])
354
+ response = self.__receive(1)
355
+ if len(response) != 1 or response[0] != UPDI_PHY_ACK:
356
+ raise Exception('Failed to communicate with device (st)')
357
+
358
+ # Load a number of bytes from the pointer location with pointer post-increment
359
+ def __ld_ptr_inc(self, size):
360
+ self.__send([UPDI_PHY_SYNC, UPDI_LD | UPDI_PTR_INC | UPDI_DATA_8])
361
+ return self.__receive(size)
362
+
363
+ # Load a 16-bit word value from the pointer location with pointer post-increment
364
+ def __ld_ptr_inc16(self, words):
365
+ self.__send([UPDI_PHY_SYNC, UPDI_LD | UPDI_PTR_INC | UPDI_DATA_16])
366
+ return self.__receive(words << 1)
367
+
368
+ # Set the pointer location
369
+ def __st_ptr(self, address):
370
+ self.__send([UPDI_PHY_SYNC, UPDI_ST | UPDI_PTR_ADDRESS | UPDI_DATA_16,
371
+ address & 0xFF, (address >> 8) & 0xFF])
372
+ response = self.__receive(1)
373
+ if len(response) != 1 or response[0] != UPDI_PHY_ACK:
374
+ raise Exception('Failed to communicate with device (st_ptr)')
375
+
376
+ # Store data to the pointer location with pointer post-increment
377
+ def __st_ptr_inc(self, data):
378
+ self.__send([UPDI_PHY_SYNC, UPDI_ST | UPDI_PTR_INC | UPDI_DATA_8, data[0]])
379
+ response = self.__receive(1)
380
+ if len(response) != 1 or response[0] != UPDI_PHY_ACK:
381
+ raise Exception('Failed to communicate with device (ACK st_ptr_inc)')
382
+ n = 1
383
+ while n < len(data):
384
+ self.__send([data[n]])
385
+ response = self.__receive(1)
386
+ if len(response) != 1 or response[0] != UPDI_PHY_ACK:
387
+ raise Exception('Failed to communicate with device (st_ptr_inc)')
388
+ n += 1
389
+
390
+ # Store a 16-bit word value to the pointer location with pointer post-increment
391
+ def __st_ptr_inc16(self, data):
392
+ ctrla_ackon = 1 << UPDI_CTRLA_IBDLY_BIT
393
+ ctrla_ackoff = ctrla_ackon | (1 << UPDI_CTRLA_RSD_BIT)
394
+ self.__stcs(UPDI_CS_CTRLA, ctrla_ackoff)
395
+ self.__send([UPDI_PHY_SYNC, UPDI_ST | UPDI_PTR_INC | UPDI_DATA_16] )
396
+ self.__send(data)
397
+ self.__stcs(UPDI_CS_CTRLA, ctrla_ackon)
398
+
399
+ # Store a value to the repeat counter
400
+ def __repeat(self, repeats):
401
+ if (repeats - 1) > UPDI_MAX_REPEAT_SIZE:
402
+ raise Exception('Invalid repeat count')
403
+ repeats -= 1
404
+ self.__send([UPDI_PHY_SYNC, UPDI_REPEAT | UPDI_REPEAT_BYTE, repeats & 0xFF])
405
+
406
+ # Write a key
407
+ def __key(self, size, key):
408
+ if len(key) != 8 << size:
409
+ raise Exception('Invalid Key length')
410
+ self.__send([UPDI_PHY_SYNC, UPDI_KEY | UPDI_KEY_KEY | size])
411
+ self.__send(list(reversed(list(key))))
412
+
413
+
414
+ # ------------------------------------------------------------------------------
415
+ # Application Layer for UPDI Stack
416
+ # ------------------------------------------------------------------------------
417
+
418
+ # Enter NVM programming mode
419
+ def __enter_progmode(self):
420
+ if self.__in_prog_mode():
421
+ return
422
+ self.__key(UPDI_KEY_64, UPDI_KEY_NVM)
423
+ key_status = self.__ldcs(UPDI_ASI_KEY_STATUS)
424
+ if not key_status & (1 << UPDI_ASI_KEY_STATUS_NVMPROG):
425
+ raise Exception('Key not accepted')
426
+ self.__reset(apply_reset=True)
427
+ self.__reset(apply_reset=False)
428
+ if not self.__wait_unlocked(100):
429
+ raise Exception('Failed to enter NVM programming mode: device is locked')
430
+ if not self.__in_prog_mode():
431
+ raise Exception('Failed to enter NVM programming mode')
432
+
433
+ # Get device ID
434
+ def __get_device_id(self):
435
+ return int.from_bytes(self.__read_data(self.sigrow_address, 3), byteorder='big')
436
+
437
+ # Check whether the NVM PROG flag is up
438
+ def __in_prog_mode(self):
439
+ if self.__ldcs(UPDI_ASI_SYS_STATUS) & (1 << UPDI_ASI_SYS_STATUS_NVMPROG):
440
+ return True
441
+ return False
442
+
443
+ # Wait for the device to be unlocked
444
+ def __wait_unlocked(self, timeout_ms):
445
+ timeout = Timeout(timeout_ms)
446
+ while not timeout.expired():
447
+ if not self.__ldcs(UPDI_ASI_SYS_STATUS) & (1 << UPDI_ASI_SYS_STATUS_LOCKSTATUS):
448
+ return True
449
+ return False
450
+
451
+ # Unlock and erase
452
+ def __unlock(self):
453
+ self.__key(UPDI_KEY_64, UPDI_KEY_CHIPERASE)
454
+ key_status = self.__ldcs(UPDI_ASI_KEY_STATUS)
455
+ if not key_status & (1 << UPDI_ASI_KEY_STATUS_CHIPERASE):
456
+ raise Exception('Key not accepted')
457
+ self.__reset(apply_reset=True)
458
+ self.__reset(apply_reset=False)
459
+ if not self.__wait_unlocked(500):
460
+ raise Exception('Failed to chip erase using key')
461
+
462
+ # Apply or release UPDI reset condition
463
+ def __reset(self, apply_reset):
464
+ if apply_reset:
465
+ self.__stcs(UPDI_ASI_RESET_REQ, UPDI_RESET_REQ_VALUE)
466
+ else:
467
+ self.__stcs(UPDI_ASI_RESET_REQ, 0x00)
468
+
469
+ # Wait for the NVM controller to be ready
470
+ def __wait_nvm_ready(self):
471
+ timeout = Timeout(10000)
472
+ while not timeout.expired():
473
+ status = self.__ld(self.nvmctrl_address + UPDI_NVMCTRL_STATUS)
474
+ if status & (1 << UPDI_NVM_STATUS_WRITE_ERROR):
475
+ return False
476
+ if not status & ((1 << UPDI_NVM_STATUS_EEPROM_BUSY) | (1 << UPDI_NVM_STATUS_FLASH_BUSY)):
477
+ return True
478
+ return False
479
+
480
+ # Execute an NVM COMMAND on the NVM CTRL
481
+ def __execute_nvm_command(self, command):
482
+ return self.__st(self.nvmctrl_address + UPDI_NVMCTRL_CTRLA, command)
483
+
484
+ # Write a number of words to memory
485
+ def __write_data_words(self, address, data):
486
+ if len(data) == 2:
487
+ value = data[0] + (data[1] << 8)
488
+ return self.__st16(address, value)
489
+ if len(data) > UPDI_MAX_REPEAT_SIZE << 1:
490
+ raise Exception('Invalid data length')
491
+ self.__st_ptr(address)
492
+ self.__repeat(len(data) >> 1)
493
+ return self.__st_ptr_inc16(data)
494
+
495
+ # Writes a number of bytes to memory
496
+ def __write_data(self, address, data):
497
+ if len(data) == 1:
498
+ return self.__st(address, data[0])
499
+ elif len(data) == 2:
500
+ self.__st(address, data[0])
501
+ return self.__st(address + 1, data[1])
502
+ if len(data) > UPDI_MAX_REPEAT_SIZE:
503
+ raise Exception('Invalid data length')
504
+ self.__st_ptr(address)
505
+ self.__repeat(len(data))
506
+ return self.__st_ptr_inc(data)
507
+
508
+ # Write a page of data to NVM (v0)
509
+ def __write_nvm(self, address, data, use_word_access=True):
510
+ if not self.__wait_nvm_ready():
511
+ raise Exception('Timeout waiting for flash ready before page buffer clear')
512
+ self.__execute_nvm_command(UPDI_V0_NVMCTRL_CTRLA_PAGE_BUFFER_CLR)
513
+ if not self.__wait_nvm_ready():
514
+ raise Exception('Timeout waiting for flash ready after page buffer clear')
515
+ if use_word_access:
516
+ self.__write_data_words(address, data)
517
+ else:
518
+ self.__write_data(address, data)
519
+ self.__execute_nvm_command(UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE)
520
+ if not self.__wait_nvm_ready():
521
+ raise Exception('Timeout waiting for flash ready after page write')
522
+
523
+ # Write one fuse value (v0)
524
+ def __write_fuse(self, fusenum, value):
525
+ if not self.__wait_nvm_ready():
526
+ raise Exception('Timeout waiting for NVM controller to be ready before fuse write')
527
+ address = self.fuses_address + fusenum
528
+ self.__st(self.nvmctrl_address + UPDI_NVMCTRL_ADDRL, address & 0xFF)
529
+ self.__st(self.nvmctrl_address + UPDI_NVMCTRL_ADDRH, (address >> 8) & 0xFF)
530
+ self.__st(self.nvmctrl_address + UPDI_NVMCTRL_DATAL, value & 0xFF)
531
+ self.__execute_nvm_command(UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE)
532
+ if not self.__wait_nvm_ready():
533
+ raise Exception('Timeout waiting for NVM controller to be ready after fuse write')
534
+
535
+ # Read a number of bytes of data from UPDI
536
+ def __read_data(self, address, size):
537
+ if size > UPDI_MAX_REPEAT_SIZE:
538
+ raise Exception('Can\'t read that many bytes in one go')
539
+ self.__st_ptr(address)
540
+ if size > 1:
541
+ self.__repeat(size)
542
+ return self.__ld_ptr_inc(size)
543
+
544
+ # Read a number of words of data from UPDI
545
+ def __read_data_words(self, address, words):
546
+ if words > UPDI_MAX_REPEAT_SIZE:
547
+ raise Exception('Can\'t read that many words in one go')
548
+ self.__st_ptr(address)
549
+ if words > 1:
550
+ self.__repeat(words)
551
+ return self.__ld_ptr_inc16(words)
552
+
553
+
554
+ # ------------------------------------------------------------------------------
555
+ # NVM programming algorithm
556
+ # ------------------------------------------------------------------------------
557
+
558
+ # Read from flash
559
+ def __read_flash(self, address, size):
560
+ pages = size // self.flash_pagesize
561
+ if size % self.flash_pagesize:
562
+ pages += 1
563
+ data = bytes()
564
+ for _ in range(pages):
565
+ data += (self.__read_data_words(address, self.flash_pagesize >> 1))
566
+ address += self.flash_pagesize
567
+ return data[:size]
568
+
569
+ # Write to flash
570
+ def __write_flash(self, address, data):
571
+ data = self.__pad_data(data, self.flash_pagesize)
572
+ pages = self.__page_data(data, self.flash_pagesize)
573
+ for page in pages:
574
+ self.__write_nvm(address, page, use_word_access=True)
575
+ address += len(page)
576
+
577
+ # Read one fuse value
578
+ def __read_fuse(self, fusenum):
579
+ address = self.fuses_address + fusenum
580
+ data = self.__ld(address)
581
+ return data
582
+
583
+ # Pad data so that there are full pages
584
+ def __pad_data(self, data, blocksize):
585
+ return data + b'\xFF' * (len(data) % blocksize)
586
+
587
+ # Divide data into pages
588
+ def __page_data(self, data, size):
589
+ total_length = len(data)
590
+ result = list()
591
+ while len(result) < total_length / size:
592
+ result.append(data[:size])
593
+ data = data[size:]
594
+ return result
595
+
596
+
597
+ # ===================================================================================
598
+ # Timeout Class
599
+ # ===================================================================================
600
+
601
+ # Simple timeout helper in milliseconds.
602
+ class Timeout:
603
+ def __init__(self, timeout_ms):
604
+ self.timeout_ms = timeout_ms
605
+ self.start_time = time.time()
606
+
607
+ def expired(self):
608
+ return time.time() - self.start_time > self.timeout_ms / 1000.0
609
+
610
+
611
+ # ===================================================================================
612
+ # Error Class - Raise an Error
613
+ # ===================================================================================
614
+
615
+ class PrgError(Exception):
616
+ def __init__(self, msg='Something went wrong'):
617
+ super(PrgError, self).__init__(msg)
618
+ sys.stderr.write('ERROR: ' + msg + '\n')
619
+ sys.exit(1)
620
+
621
+
622
+ # ===================================================================================
623
+ # Device Definitions and Functions
624
+ # ===================================================================================
625
+
626
+ # Get list of supported device names
627
+ def get_supported_devices():
628
+ result = list()
629
+ for d in UPDI_DEVICES:
630
+ result.append(d['name'])
631
+ return result
632
+
633
+ # Device definitions
634
+ UPDI_DEVICES = [
635
+ {'name': 'attiny202', 'device_id': 0x1E9123, 'flash_size': 0x0800, 'flash_pagesize': 0x40},
636
+ {'name': 'attiny402', 'device_id': 0x1E9227, 'flash_size': 0x1000, 'flash_pagesize': 0x40},
637
+ {'name': 'attiny212', 'device_id': 0x1E9121, 'flash_size': 0x0800, 'flash_pagesize': 0x40},
638
+ {'name': 'attiny412', 'device_id': 0x1E9223, 'flash_size': 0x1000, 'flash_pagesize': 0x40},
639
+ {'name': 'attiny204', 'device_id': 0x1E9122, 'flash_size': 0x0800, 'flash_pagesize': 0x40},
640
+ {'name': 'attiny404', 'device_id': 0x1E9226, 'flash_size': 0x1000, 'flash_pagesize': 0x40},
641
+ {'name': 'attiny804', 'device_id': 0x1E9325, 'flash_size': 0x2000, 'flash_pagesize': 0x40},
642
+ {'name': 'attiny1604', 'device_id': 0x1E9425, 'flash_size': 0x4000, 'flash_pagesize': 0x40},
643
+ {'name': 'attiny214', 'device_id': 0x1E9120, 'flash_size': 0x0800, 'flash_pagesize': 0x40},
644
+ {'name': 'attiny414', 'device_id': 0x1E9222, 'flash_size': 0x1000, 'flash_pagesize': 0x40},
645
+ {'name': 'attiny814', 'device_id': 0x1E9322, 'flash_size': 0x2000, 'flash_pagesize': 0x40},
646
+ {'name': 'attiny1614', 'device_id': 0x1E9422, 'flash_size': 0x4000, 'flash_pagesize': 0x40},
647
+ {'name': 'attiny424', 'device_id': 0x1E922C, 'flash_size': 0x1000, 'flash_pagesize': 0x40},
648
+ {'name': 'attiny824', 'device_id': 0x1E9329, 'flash_size': 0x2000, 'flash_pagesize': 0x40},
649
+ {'name': 'attiny1624', 'device_id': 0x1E942A, 'flash_size': 0x4000, 'flash_pagesize': 0x40},
650
+ {'name': 'attiny3224', 'device_id': 0x1E9528, 'flash_size': 0x8000, 'flash_pagesize': 0x80},
651
+ {'name': 'attiny406', 'device_id': 0x1E9225, 'flash_size': 0x1000, 'flash_pagesize': 0x40},
652
+ {'name': 'attiny806', 'device_id': 0x1E9324, 'flash_size': 0x2000, 'flash_pagesize': 0x40},
653
+ {'name': 'attiny1606', 'device_id': 0x1E9424, 'flash_size': 0x4000, 'flash_pagesize': 0x40},
654
+ {'name': 'attiny416', 'device_id': 0x1E9221, 'flash_size': 0x1000, 'flash_pagesize': 0x40},
655
+ {'name': 'attiny816', 'device_id': 0x1E9321, 'flash_size': 0x2000, 'flash_pagesize': 0x40},
656
+ {'name': 'attiny1616', 'device_id': 0x1E9421, 'flash_size': 0x4000, 'flash_pagesize': 0x40},
657
+ {'name': 'attiny3216', 'device_id': 0x1E9521, 'flash_size': 0x8000, 'flash_pagesize': 0x80},
658
+ {'name': 'attiny426', 'device_id': 0x1E922B, 'flash_size': 0x1000, 'flash_pagesize': 0x40},
659
+ {'name': 'attiny826', 'device_id': 0x1E9328, 'flash_size': 0x2000, 'flash_pagesize': 0x40},
660
+ {'name': 'attiny1626', 'device_id': 0x1E9429, 'flash_size': 0x4000, 'flash_pagesize': 0x40},
661
+ {'name': 'attiny3226', 'device_id': 0x1E9527, 'flash_size': 0x8000, 'flash_pagesize': 0x80},
662
+ {'name': 'attiny417', 'device_id': 0x1E9220, 'flash_size': 0x1000, 'flash_pagesize': 0x40},
663
+ {'name': 'attiny427', 'device_id': 0x1E922A, 'flash_size': 0x1000, 'flash_pagesize': 0x40},
664
+ {'name': 'attiny807', 'device_id': 0x1E9323, 'flash_size': 0x2000, 'flash_pagesize': 0x40},
665
+ {'name': 'attiny817', 'device_id': 0x1E9320, 'flash_size': 0x2000, 'flash_pagesize': 0x40},
666
+ {'name': 'attiny827', 'device_id': 0x1E9327, 'flash_size': 0x2000, 'flash_pagesize': 0x40},
667
+ {'name': 'attiny1607', 'device_id': 0x1E9423, 'flash_size': 0x4000, 'flash_pagesize': 0x40},
668
+ {'name': 'attiny1617', 'device_id': 0x1E9420, 'flash_size': 0x4000, 'flash_pagesize': 0x40},
669
+ {'name': 'attiny1627', 'device_id': 0x1E9428, 'flash_size': 0x4000, 'flash_pagesize': 0x40},
670
+ {'name': 'attiny3217', 'device_id': 0x1E9522, 'flash_size': 0x8000, 'flash_pagesize': 0x80},
671
+ {'name': 'attiny3227', 'device_id': 0x1E9526, 'flash_size': 0x8000, 'flash_pagesize': 0x80}
672
+ ]
673
+
674
+
675
+ # ===================================================================================
676
+ # UPDI Protocol Constants
677
+ # ===================================================================================
678
+
679
+ UPDI_BREAK = 0x00
680
+ UPDI_LDS = 0x00
681
+ UPDI_STS = 0x40
682
+ UPDI_LD = 0x20
683
+ UPDI_ST = 0x60
684
+ UPDI_LDCS = 0x80
685
+ UPDI_STCS = 0xC0
686
+ UPDI_REPEAT = 0xA0
687
+ UPDI_KEY = 0xE0
688
+ UPDI_PTR = 0x00
689
+ UPDI_PTR_INC = 0x04
690
+ UPDI_PTR_ADDRESS = 0x08
691
+ UPDI_ADDRESS_8 = 0x00
692
+ UPDI_ADDRESS_16 = 0x04
693
+ UPDI_ADDRESS_24 = 0x08
694
+ UPDI_DATA_8 = 0x00
695
+ UPDI_DATA_16 = 0x01
696
+ UPDI_DATA_24 = 0x02
697
+
698
+ UPDI_PHY_SYNC = 0x55
699
+ UPDI_PHY_ACK = 0x40
700
+ UPDI_REPEAT_BYTE = 0x00
701
+ UPDI_REPEAT_WORD = 0x01
702
+ UPDI_CS_STATUSA = 0x00
703
+ UPDI_CS_STATUSB = 0x01
704
+ UPDI_CS_CTRLA = 0x02
705
+ UPDI_CS_CTRLB = 0x03
706
+ UPDI_CTRLA_RSD_BIT = 3
707
+ UPDI_CTRLA_IBDLY_BIT = 7
708
+ UPDI_CTRLB_CCDETDIS_BIT = 3
709
+ UPDI_CTRLB_UPDIDIS_BIT = 2
710
+ UPDI_ASI_KEY_STATUS = 0x07
711
+ UPDI_ASI_RESET_REQ = 0x08
712
+ UPDI_ASI_CTRLA = 0x09
713
+ UPDI_ASI_SYS_CTRLA = 0x0A
714
+ UPDI_ASI_SYS_STATUS = 0x0B
715
+ UPDI_ASI_CRC_STATUS = 0x0C
716
+ UPDI_ASI_STATUSA_REVID = 4
717
+ UPDI_ASI_STATUSB_PESIG = 0
718
+ UPDI_ASI_KEY_STATUS_CHIPERASE = 3
719
+ UPDI_ASI_KEY_STATUS_NVMPROG = 4
720
+ UPDI_ASI_KEY_STATUS_UROWWRITE = 5
721
+ UPDI_ASI_SYS_STATUS_RSTSYS = 5
722
+ UPDI_ASI_SYS_STATUS_INSLEEP = 4
723
+ UPDI_ASI_SYS_STATUS_NVMPROG = 3
724
+ UPDI_ASI_SYS_STATUS_UROWPROG = 2
725
+ UPDI_ASI_SYS_STATUS_LOCKSTATUS = 0
726
+ UPDI_ASI_SYS_CTRLA_UROW_FINAL = 1
727
+ UPDI_RESET_REQ_VALUE = 0x59
728
+ UPDI_MAX_REPEAT_SIZE = (0xFF+1)
729
+
730
+ UPDI_KEY_SIB = 0x04
731
+ UPDI_KEY_KEY = 0x00
732
+ UPDI_KEY_64 = 0x00
733
+ UPDI_KEY_128 = 0x01
734
+ UPDI_KEY_256 = 0x02
735
+ UPDI_KEY_NVM = b'NVMProg '
736
+ UPDI_KEY_CHIPERASE = b'NVMErase'
737
+ UPDI_KEY_UROW = b'NVMUs&te'
738
+ UPDI_SIB_8BYTES = UPDI_KEY_64
739
+ UPDI_SIB_16BYTES = UPDI_KEY_128
740
+ UPDI_SIB_32BYTES = UPDI_KEY_256
741
+
742
+ UPDI_NVMCTRL_CTRLA = 0x00
743
+ UPDI_NVMCTRL_CTRLB = 0x01
744
+ UPDI_NVMCTRL_STATUS = 0x02
745
+ UPDI_NVMCTRL_INTCTRL = 0x03
746
+ UPDI_NVMCTRL_INTFLAGS = 0x04
747
+ UPDI_NVMCTRL_DATAL = 0x06
748
+ UPDI_NVMCTRL_DATAH = 0x07
749
+ UPDI_NVMCTRL_ADDRL = 0x08
750
+ UPDI_NVMCTRL_ADDRH = 0x09
751
+
752
+ UPDI_V0_NVMCTRL_CTRLA_NOP = 0x00
753
+ UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE = 0x01
754
+ UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE = 0x02
755
+ UPDI_V0_NVMCTRL_CTRLA_ERASE_WRITE_PAGE = 0x03
756
+ UPDI_V0_NVMCTRL_CTRLA_PAGE_BUFFER_CLR = 0x04
757
+ UPDI_V0_NVMCTRL_CTRLA_CHIP_ERASE = 0x05
758
+ UPDI_V0_NVMCTRL_CTRLA_ERASE_EEPROM = 0x06
759
+ UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE = 0x07
760
+
761
+ UPDI_NVM_STATUS_WRITE_ERROR = 2
762
+ UPDI_NVM_STATUS_EEPROM_BUSY = 1
763
+ UPDI_NVM_STATUS_FLASH_BUSY = 0
764
+
765
+
766
+ # ===================================================================================
767
+
768
+ if __name__ == "__main__":
769
+ _main()
@@ -0,0 +1,97 @@
1
+ Metadata-Version: 2.1
2
+ Name: tinyupdi
3
+ Version: 1.5.0
4
+ Summary: Minimal UPDI Programming Tool for tinyAVR
5
+ Home-page: https://github.com/wagiminator/MCU-Flash-Tools
6
+ Author: Stefan Wagner
7
+ Author-email: Stefan Wagner <wagiminator@web.de>
8
+ Maintainer-email: Stefan Wagner <wagiminator@web.de>
9
+ License: MIT License
10
+
11
+ Copyright (c) 2022 Stefan Wagner
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ Project-URL: Homepage, https://wagiminator.github.io/
31
+ Project-URL: Repository, https://github.com/wagiminator/MCU-Flash-Tools
32
+ Project-URL: Issues, https://github.com/wagiminator/MCU-Flash-Tools/issues
33
+ Keywords: AVR,Microchip,ATtiny,tinyAVR,UPDI,microcontroller
34
+ Classifier: Development Status :: 5 - Production/Stable
35
+ Classifier: Programming Language :: Python :: 3
36
+ Classifier: License :: OSI Approved :: MIT License
37
+ Classifier: Operating System :: OS Independent
38
+ Classifier: Intended Audience :: Developers
39
+ Classifier: Topic :: Software Development :: Embedded Systems
40
+ Requires-Python: >=3.8
41
+ Description-Content-Type: text/markdown
42
+ License-File: LICENSE
43
+ Requires-Dist: pyserial
44
+
45
+ # tinyUPDI - Minimal UPDI Programming Tool for tinyAVR
46
+ ## Description
47
+ This tool allows tinyAVR series 0, 1, and 2 microcontrollers to be programmed using a USB-to-serial converter connected in a special way to the UPDI pin (also called SerialUPDI). More information can be found [here](https://github.com/SpenceKonde/AVR-Guidance/blob/master/UPDI/jtag2updi.md).
48
+
49
+ ## Preparations
50
+ If necessary, a driver for the USB-to-serial converter used must be installed.
51
+
52
+ ## Installation
53
+ - Install [Python3](https://www.pythontutorial.net/getting-started/install-python/).
54
+ - Install [pip](https://pip.pypa.io/en/stable/installation/).
55
+ - Install rvprog:
56
+
57
+ ```
58
+ pip install tinyupdi
59
+ ```
60
+
61
+ ## Usage
62
+ Connect the USB-to-serial converter via USB to the PC and via the circuit described below to the UPDI pin of the microcontroller.
63
+
64
+ ```
65
+ USB2SERIAL tinyAVR
66
+ +--------+ +-----------+
67
+ | RXD| <------------+---> |UPDI (PA0) |
68
+ | | | | |
69
+ | TXD| ---|1kOhm|---+ | |
70
+ | | | |
71
+ | VDD| -----------------> |VDD |
72
+ | GND| -----------------> |GND |
73
+ +--------+ +-----------+
74
+ ```
75
+
76
+ Alternatively, a pre-assembled [SerialUPDI programmer](https://github.com/wagiminator/AVR-Programmer) can be used.
77
+
78
+ ```
79
+ Usage: tinyupdi [-h] [-d DEVICE] [-e] [-f FLASH] [-fs [FUSES [FUSES ...]]] [-t TRIM]
80
+
81
+ Optional arguments:
82
+ -h, --help show help message and exit
83
+ -d, --device set target device (if not set, it will be auto-detected)
84
+ -e, --erase perform a chip erase (implied with --flash)
85
+ -f FLASH, --flash FLASH BIN file to flash
86
+ -fs [FUSES [FUSES ...]], --fuses [FUSES [FUSES ...]]
87
+ fuses to set (syntax: fuse_nr:0xvalue)
88
+ -t TRIM, --trim TRIM configure oscillator for given frequency (set fuse 2)
89
+ Example:
90
+ tinyupdi -f firmware.bin -fs 6:0x04 7:0x00 8:0x00 -t 8000000
91
+ ```
92
+
93
+ ## Links
94
+ 1. [MCU Templates](https://github.com/wagiminator/MCU-Templates)
95
+ 2. [MCU Development Boards](https://github.com/wagiminator/Development-Boards)
96
+ 3. [AVR Development Boards](https://github.com/wagiminator/AVR-Development-Boards)
97
+ 4. [SerialUPDI Programmers](https://github.com/wagiminator/AVR-Programmer)
@@ -0,0 +1,12 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ setup.py
5
+ tinyupdi/__init__.py
6
+ tinyupdi/tinyupdi.py
7
+ tinyupdi.egg-info/PKG-INFO
8
+ tinyupdi.egg-info/SOURCES.txt
9
+ tinyupdi.egg-info/dependency_links.txt
10
+ tinyupdi.egg-info/entry_points.txt
11
+ tinyupdi.egg-info/requires.txt
12
+ tinyupdi.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ tinyupdi = tinyupdi:_main
@@ -0,0 +1 @@
1
+ pyserial
@@ -0,0 +1 @@
1
+ tinyupdi