summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBorja Martinez2016-09-25 12:01:00 -0500
committerBorja Martinez2016-09-25 12:01:00 -0500
commit60278208598eea8ff075260eae80884cdbc6d9fe (patch)
tree7e3f354e63a9d7d97d1fecd61e91ebd8237db563
parentcf8496c5c2396b06d8784a498da3321e55a64829 (diff)
downloadi3-mote-60278208598eea8ff075260eae80884cdbc6d9fe.tar.gz
i3-mote-60278208598eea8ff075260eae80884cdbc6d9fe.tar.xz
i3-mote-60278208598eea8ff075260eae80884cdbc6d9fe.zip
Python Script for CC2650 BSL
-rw-r--r--Basic-Test-Package/BSL/CC2650/cc2650-bsl.py1167
1 files changed, 1167 insertions, 0 deletions
diff --git a/Basic-Test-Package/BSL/CC2650/cc2650-bsl.py b/Basic-Test-Package/BSL/CC2650/cc2650-bsl.py
new file mode 100644
index 0000000..9142d6f
--- /dev/null
+++ b/Basic-Test-Package/BSL/CC2650/cc2650-bsl.py
@@ -0,0 +1,1167 @@
1#!/usr/bin/env python
2
3# Copyright (c) 2014, Jelmer Tiete <jelmer@tiete.be>.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12# notice, this list of conditions and the following disclaimer in the
13# documentation and/or other materials provided with the distribution.
14# 3. The name of the author may not be used to endorse or promote
15# products derived from this software without specific prior
16# written permission.
17
18# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
24# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30# Implementation based on stm32loader by Ivan A-R <ivan@tuxotronic.org>
31
32# Serial boot loader over UART for CC13xx / CC2538 / CC26xx
33# Based on the info found in TI's swru333a.pdf (spma029.pdf)
34#
35# Bootloader only starts if no valid image is found or if boot loader
36# backdoor is enabled.
37# Make sure you don't lock yourself out!! (enable backdoor in your firmware)
38# More info at https://github.com/JelmerT/cc2538-bsl
39
40from __future__ import print_function
41from subprocess import Popen, PIPE
42
43import sys, getopt
44import glob
45import time
46import tempfile
47import os
48import subprocess
49import struct
50import binascii
51import traceback
52
53try:
54 import magic
55 have_magic = True
56except ImportError:
57 have_magic = False
58
59try:
60 from intelhex import IntelHex
61 have_hex_support = True
62except ImportError:
63 have_hex_support = False
64
65#version
66VERSION_STRING = "2.1"
67
68# Verbose level
69QUIET = 5
70
71# Check which version of Python is running
72PY3 = sys.version_info >= (3,0)
73
74try:
75 import serial
76except ImportError:
77 print('{} requires the Python serial library'.format(sys.argv[0]))
78 print('Please install it with one of the following:')
79 print('')
80 if PY3:
81 print(' Ubuntu: sudo apt-get install python3-serial')
82 print(' Mac: sudo port install py34-serial')
83 else:
84 print(' Ubuntu: sudo apt-get install python-serial')
85 print(' Mac: sudo port install py-serial')
86 sys.exit(1)
87
88
89def mdebug(level, message, attr='\n'):
90 if QUIET >= level:
91 print(message, end=attr, file=sys.stderr)
92
93# Takes chip IDs (obtained via Get ID command) to human-readable names
94CHIP_ID_STRS = {0xb964: 'CC2538'}
95
96RETURN_CMD_STRS = {0x40: 'Success',
97 0x41: 'Unknown command',
98 0x42: 'Invalid command',
99 0x43: 'Invalid address',
100 0x44: 'Flash fail'
101 }
102
103COMMAND_RET_SUCCESS = 0x40
104COMMAND_RET_UNKNOWN_CMD = 0x41
105COMMAND_RET_INVALID_CMD = 0x42
106COMMAND_RET_INVALID_ADR = 0x43
107COMMAND_RET_FLASH_FAIL = 0x44
108
109class CmdException(Exception):
110 pass
111
112class FirmwareFile(object):
113 HEX_FILE_EXTENSIONS = ('hex', 'ihx', 'ihex')
114
115 def __init__(self, path):
116 """
117 Read a firmware file and store its data ready for device programming.
118
119 This class will try to guess the file type if python-magic is available.
120
121 If python-magic indicates a plain text file, and if IntelHex is
122 available, then the file will be treated as one of Intel HEX format.
123
124 In all other cases, the file will be treated as a raw binary file.
125
126 In both cases, the file's contents are stored in bytes for subsequent
127 usage to program a device or to perform a crc check.
128
129 Parameters:
130 path -- A str with the path to the firmware file.
131
132 Attributes:
133 bytes: A bytearray with firmware contents ready to send to the device
134 """
135 self._crc32 = None
136 firmware_is_hex = False
137
138 if have_magic:
139 file_type = bytearray(magic.from_file(path, True))
140
141 #from_file() returns bytes with PY3, str with PY2. This comparison
142 #will be True in both cases"""
143 if file_type == b'text/plain':
144 firmware_is_hex = True
145 mdebug(5, "Firmware file: Intel Hex")
146 elif file_type == b'application/octet-stream':
147 mdebug(5, "Firmware file: Raw Binary")
148 else:
149 error_str = "Could not determine firmware type. Magic " \
150 "indicates '%s'" % (file_type)
151 raise CmdException(error_str)
152 else:
153 if os.path.splitext(path)[1][1:] in self.HEX_FILE_EXTENSIONS:
154 firmware_is_hex = True
155 mdebug(5, "Your firmware looks like an Intel Hex file")
156 else:
157 mdebug(5, "Cannot auto-detect firmware filetype: Assuming .bin")
158
159 mdebug(10, "For more solid firmware type auto-detection, install "
160 "python-magic.")
161 mdebug(10, "Please see the readme for more details.")
162
163 if firmware_is_hex:
164 if have_hex_support:
165 self.bytes = bytearray(IntelHex(path).tobinarray())
166 return
167 else:
168 error_str = "Firmware is Intel Hex, but the IntelHex library " \
169 "could not be imported.\n" \
170 "Install IntelHex in site-packages or program " \
171 "your device with a raw binary (.bin) file.\n" \
172 "Please see the readme for more details."
173 raise CmdException(error_str)
174
175 with open(path, 'rb') as f:
176 self.bytes = bytearray(f.read())
177
178 def crc32(self):
179 """
180 Return the crc32 checksum of the firmware image
181
182 Return:
183 The firmware's CRC32, ready for comparison with the CRC
184 returned by the ROM bootloader's COMMAND_CRC32
185 """
186 if self._crc32 is None:
187 self._crc32 = binascii.crc32(bytearray(self.bytes)) & 0xffffffff
188
189 return self._crc32
190
191class CommandInterface(object):
192 ACK_BYTE = 0xCC
193 NACK_BYTE = 0x33
194 def open(self, aport='/dev/tty.usbserial-000013FAB', abaudrate=500000):
195 self.sp = serial.Serial(
196 port=aport,
197 baudrate=abaudrate, # baudrate
198 bytesize=8, # number of databits
199 parity=serial.PARITY_NONE,
200 stopbits=1,
201 xonxoff=0, # enable software flow control
202 rtscts=0, # disable RTS/CTS flow control
203 timeout=3 # set a timeout value, None for waiting forever
204 )
205
206
207 def invoke_bootloader(self, dtr_active_high=False, inverted=False):
208 # Use the DTR and RTS lines to control bootloader and the !RESET pin.
209 # This can automatically invoke the bootloader without the user
210 # having to toggle any pins.
211 #
212 # If inverted is False (default):
213 # DTR: connected to the bootloader pin
214 # RTS: connected to !RESET
215 # If inverted is True, pin connections are the other way round
216 if inverted:
217 set_bootloader_pin = self.sp.setRTS
218 set_reset_pin = self.sp.setDTR
219 else:
220 set_bootloader_pin = self.sp.setDTR
221 set_reset_pin = self.sp.setRTS
222
223 # BMH: Forze DTR
224 mdebug(0, "Forze DTR Inactive (Active = %d) RTS-DTR Inverted: %d" % (dtr_active_high,inverted))
225 set_bootloader_pin(1 if dtr_active_high else 0)
226 time.sleep(0.2)
227
228 set_bootloader_pin(0 if dtr_active_high else 1)
229 set_reset_pin(0)
230 set_reset_pin(1)
231 set_reset_pin(0)
232 time.sleep(0.002) # Make sure the pin is still asserted when the chip
233 # comes out of reset. This fixes an issue where there
234 # wasn't enough delay here on Mac.
235 set_bootloader_pin(0 if not dtr_active_high else 1)
236
237 #set_reset_pin(0)
238 #set_bootloader_pin(0)
239 #set_reset_pin(1)
240 #set_bootloader_pin(1)
241
242 #set_reset_pin(0)
243 #time.sleep(0.002) # Make sure the pin is still asserted when the chip
244 # comes out of reset. This fixes an issue where there
245 # wasn't enough delay here on Mac.
246 #set_bootloader_pin(0 if not dtr_active_high else 1)
247
248
249
250
251 # Some boards have a co-processor that detects this sequence here and
252 # then drives the main chip's BSL enable and !RESET pins. Depending on
253 # board design and co-processor behaviour, the !RESET pin may get
254 # asserted after we have finished the sequence here. In this case, we
255 # need a small delay so as to avoid trying to talk to main chip before
256 # it has actually entered its bootloader mode.
257 #
258 # See contiki-os/contiki#1533
259 time.sleep(0.1)
260
261 def close(self):
262 self.sp.close()
263
264
265 def _wait_for_ack(self, info = "", timeout = 1):
266 stop = time.time() + timeout
267 got = bytearray(2)
268 while got[-2] != 00 or got[-1] not in (CommandInterface.ACK_BYTE,
269 CommandInterface.NACK_BYTE):
270 got += self._read(1)
271 if time.time() > stop:
272 raise CmdException("Timeout waiting for ACK/NACK after '%s'"
273 % (info,))
274
275 # Our bytearray's length is: 2 initial bytes + 2 bytes for the ACK/NACK
276 # plus a possible N-4 additional (buffered) bytes
277 mdebug(10, "Got %d additional bytes before ACK/NACK" % (len(got) - 4,))
278
279 # wait for ask
280 ask = got[-1]
281
282 if ask == CommandInterface.ACK_BYTE:
283 # ACK
284 return 1
285 elif ask == CommandInterface.NACK_BYTE:
286 # NACK
287 mdebug(10, "Target replied with a NACK during %s" % info)
288 return 0
289
290 # Unknown response
291 mdebug(10, "Unrecognised response 0x%x to %s" % (ask, info))
292 return 0
293
294 def _encode_addr(self, addr):
295 byte3 = (addr >> 0) & 0xFF
296 byte2 = (addr >> 8) & 0xFF
297 byte1 = (addr >> 16) & 0xFF
298 byte0 = (addr >> 24) & 0xFF
299 if PY3:
300 return bytes([byte0, byte1, byte2, byte3])
301 else:
302 return (chr(byte0) + chr(byte1) + chr(byte2) + chr(byte3))
303
304 def _decode_addr(self, byte0, byte1, byte2, byte3):
305 return ((byte3 << 24) | (byte2 << 16) | (byte1 << 8) | (byte0 << 0))
306
307 def _calc_checks(self, cmd, addr, size):
308 return ((sum(bytearray(self._encode_addr(addr)))
309 +sum(bytearray(self._encode_addr(size)))
310 +cmd)
311 &0xFF)
312
313 def _write(self, data, is_retry=False):
314 if PY3:
315 if type(data) == int:
316 assert data < 256
317 goal = 1
318 written = self.sp.write(bytes([data]))
319 elif type(data) == bytes or type(data) == bytearray:
320 goal = len(data)
321 written = self.sp.write(data)
322 else:
323 raise CmdException("Internal Error. Bad data type: {}".format(type(data)))
324 else:
325 if type(data) == int:
326 assert data < 256
327 goal = 1
328 written = self.sp.write(chr(data))
329 else:
330 goal = len(data)
331 written = self.sp.write(data)
332 if written < goal:
333 mdebug(10, "*** Only wrote {} of target {} bytes".format(written, goal))
334 if is_retry and written == 0:
335 raise CmdException("Failed to write data on the serial bus")
336 mdebug(10, "*** Retrying write for remainder")
337 if type(data) == int:
338 return self._write(data, is_retry=True)
339 else:
340 return self._write(data[written:], is_retry=True)
341
342 def _read(self, length):
343 return bytearray(self.sp.read(length))
344
345 def sendAck(self):
346 self._write(0x00)
347 self._write(0xCC)
348 return
349
350 def sendNAck(self):
351 self._write(0x00)
352 self._write(0x33)
353 return
354
355
356 def receivePacket(self):
357 # stop = time.time() + 5
358 # got = None
359 # while not got:
360 got = self._read(2)
361 # if time.time() > stop:
362 # break
363
364 # if not got:
365 # raise CmdException("No response to %s" % info)
366
367 size = got[0] #rcv size
368 chks = got[1] #rcv checksum
369 data = bytearray(self._read(size - 2)) # rcv data
370
371 mdebug(10, "*** received %x bytes" % size)
372 if chks == sum(data)&0xFF:
373 self.sendAck()
374 return data
375 else:
376 self.sendNAck()
377 #TODO: retry receiving!
378 raise CmdException("Received packet checksum error")
379 return 0
380
381 def sendSynch(self):
382 cmd = 0x55
383
384 self.sp.flushInput() #flush serial input buffer for first ACK reception
385
386 mdebug(10, "*** sending synch sequence")
387 self._write(cmd) # send U
388 self._write(cmd) # send U
389 return self._wait_for_ack("Synch (0x55 0x55)", 2)
390
391 def checkLastCmd(self):
392 stat = self.cmdGetStatus()
393 if not (stat):
394 raise CmdException("No response from target on status request. (Did you disable the bootloader?)")
395
396 if stat[0] == COMMAND_RET_SUCCESS:
397 mdebug(10, "Command Successful")
398 return 1
399 else:
400 stat_str = RETURN_CMD_STRS.get(stat[0], None)
401 if stat_str is None:
402 mdebug(0, 'Warning: unrecognized status returned 0x%x' % stat[0])
403 else:
404 mdebug(0, "Target returned: 0x%x, %s" % (stat[0], stat_str))
405 return 0
406
407
408 def cmdPing(self):
409 cmd = 0x20
410 lng = 3
411
412 self._write(lng) # send size
413 self._write(cmd) # send checksum
414 self._write(cmd) # send data
415
416 mdebug(10, "*** Ping command (0x20)")
417 if self._wait_for_ack("Ping (0x20)"):
418 return self.checkLastCmd()
419
420 def cmdReset(self):
421 cmd = 0x25
422 lng = 3
423
424 self._write(lng) # send size
425 self._write(cmd) # send checksum
426 self._write(cmd) # send data
427
428 mdebug(10, "*** Reset command (0x25)")
429 if self._wait_for_ack("Reset (0x25)"):
430 return 1
431
432 def cmdGetChipId(self):
433 cmd = 0x28
434 lng = 3
435
436 self._write(lng) # send size
437 self._write(cmd) # send checksum
438 self._write(cmd) # send data
439
440 mdebug(10, "*** GetChipId command (0x28)")
441 if self._wait_for_ack("Get ChipID (0x28)"):
442 version = self.receivePacket() # 4 byte answ, the 2 LSB hold chip ID
443 if self.checkLastCmd():
444 assert len(version) == 4, "Unreasonable chip id: %s" % repr(version)
445 mdebug(10, " Version 0x%02X%02X%02X%02X" % tuple(version))
446 chip_id = (version[2] << 8) | version[3]
447 return chip_id
448 else:
449 raise CmdException("GetChipID (0x28) failed")
450
451 def cmdGetStatus(self):
452 cmd = 0x23
453 lng = 3
454
455 self._write(lng) # send size
456 self._write(cmd) # send checksum
457 self._write(cmd) # send data
458
459 mdebug(10, "*** GetStatus command (0x23)")
460 if self._wait_for_ack("Get Status (0x23)"):
461 stat = self.receivePacket()
462 return stat
463
464 def cmdSetXOsc(self):
465 cmd = 0x29
466 lng = 3
467
468 self._write(lng) # send size
469 self._write(cmd) # send checksum
470 self._write(cmd) # send data
471
472 mdebug(10, "*** SetXOsc command (0x29)")
473 if self._wait_for_ack("SetXOsc (0x29)"):
474 return 1
475 # UART speed (needs) to be changed!
476
477 def cmdRun(self, addr):
478 cmd=0x22
479 lng=7
480
481 self._write(lng) # send length
482 self._write(self._calc_checks(cmd,addr,0)) # send checksum
483 self._write(cmd) # send cmd
484 self._write(self._encode_addr(addr)) # send addr
485
486 mdebug(10, "*** Run command(0x22)")
487 return 1
488
489 def cmdEraseMemory(self, addr, size):
490 cmd=0x26
491 lng=11
492
493 self._write(lng) # send length
494 self._write(self._calc_checks(cmd,addr,size)) # send checksum
495 self._write(cmd) # send cmd
496 self._write(self._encode_addr(addr)) # send addr
497 self._write(self._encode_addr(size)) # send size
498
499 mdebug(10, "*** Erase command(0x26)")
500 if self._wait_for_ack("Erase memory (0x26)",10):
501 return self.checkLastCmd()
502
503 def cmdBankErase(self):
504 cmd = 0x2C
505 lng = 3
506
507 self._write(lng) # send length
508 self._write(cmd) # send checksum
509 self._write(cmd) # send cmd
510
511 mdebug(10, "*** Bank Erase command(0x2C)")
512 if self._wait_for_ack("Bank Erase (0x2C)",10):
513 return self.checkLastCmd()
514
515 def cmdCRC32(self, addr, size):
516 cmd=0x27
517 lng=11
518
519 self._write(lng) # send length
520 self._write(self._calc_checks(cmd,addr,size)) # send checksum
521 self._write(cmd) # send cmd
522 self._write(self._encode_addr(addr)) # send addr
523 self._write(self._encode_addr(size)) # send size
524
525 mdebug(10, "*** CRC32 command(0x27)")
526 if self._wait_for_ack("Get CRC32 (0x27)",1):
527 crc=self.receivePacket()
528 if self.checkLastCmd():
529 return self._decode_addr(crc[3],crc[2],crc[1],crc[0])
530
531 def cmdCRC32CC26xx(self, addr, size):
532 cmd = 0x27
533 lng = 15
534
535 self._write(lng) # send length
536 self._write(self._calc_checks(cmd, addr, size)) # send checksum
537 self._write(cmd) # send cmd
538 self._write(self._encode_addr(addr)) # send addr
539 self._write(self._encode_addr(size)) # send size
540 self._write(self._encode_addr(0x00000000)) # send number of reads
541
542 mdebug(10, "*** CRC32 command(0x27)")
543 if self._wait_for_ack("Get CRC32 (0x27)", 1):
544 crc=self.receivePacket()
545 if self.checkLastCmd():
546 return self._decode_addr(crc[3], crc[2], crc[1], crc[0])
547
548 def cmdDownload(self, addr, size):
549 cmd=0x21
550 lng=11
551
552 if (size % 4) != 0: # check for invalid data lengths
553 raise Exception('Invalid data size: %i. Size must be a multiple of 4.' % size)
554
555 self._write(lng) # send length
556 self._write(self._calc_checks(cmd,addr,size)) # send checksum
557 self._write(cmd) # send cmd
558 self._write(self._encode_addr(addr)) # send addr
559 self._write(self._encode_addr(size)) # send size
560
561 mdebug(10, "*** Download command (0x21)")
562 if self._wait_for_ack("Download (0x21)",2):
563 return self.checkLastCmd()
564
565 def cmdSendData(self, data):
566 cmd=0x24
567 lng=len(data)+3
568 # TODO: check total size of data!! max 252 bytes!
569
570 self._write(lng) # send size
571 self._write((sum(bytearray(data))+cmd)&0xFF) # send checksum
572 self._write(cmd) # send cmd
573 self._write(bytearray(data)) # send data
574
575 mdebug(10, "*** Send Data (0x24)")
576 if self._wait_for_ack("Send data (0x24)",10):
577 return self.checkLastCmd()
578
579 def cmdMemRead(self, addr): # untested
580 cmd=0x2A
581 lng=8
582
583 self._write(lng) # send length
584 self._write(self._calc_checks(cmd,addr,4)) # send checksum
585 self._write(cmd) # send cmd
586 self._write(self._encode_addr(addr)) # send addr
587 self._write(4) # send width, 4 bytes
588
589 mdebug(10, "*** Mem Read (0x2A)")
590 if self._wait_for_ack("Mem Read (0x2A)",1):
591 data = self.receivePacket()
592 if self.checkLastCmd():
593 return data # self._decode_addr(ord(data[3]),ord(data[2]),ord(data[1]),ord(data[0]))
594
595 def cmdMemReadCC26xx(self, addr):
596 cmd = 0x2A
597 lng = 9
598
599 self._write(lng) # send length
600 self._write(self._calc_checks(cmd, addr, 2)) # send checksum
601 self._write(cmd) # send cmd
602 self._write(self._encode_addr(addr)) # send addr
603 self._write(1) # send width, 4 bytes
604 self._write(1) # send number of reads
605
606 mdebug(10, "*** Mem Read (0x2A)")
607 if self._wait_for_ack("Mem Read (0x2A)", 1):
608 data = self.receivePacket()
609 if self.checkLastCmd():
610 return data
611
612 def cmdMemWrite(self, addr, data, width): # untested
613 # TODO: check width for 1 or 4 and data size
614 cmd=0x2B
615 lng=10
616
617 self._write(lng) # send length
618 self._write(self._calc_checks(cmd,addr,0)) # send checksum
619 self._write(cmd) # send cmd
620 self._write(self._encode_addr(addr)) # send addr
621 self._write(bytearray(data)) # send data
622 self._write(width) # send width, 4 bytes
623
624 mdebug(10, "*** Mem write (0x2B)")
625 if self._wait_for_ack("Mem Write (0x2B)",2):
626 return checkLastCmd()
627
628
629# Complex commands section
630
631 def writeMemory(self, addr, data):
632 lng = len(data)
633 trsf_size = 248 # amount of data bytes transferred per packet (theory: max 252 + 3)
634 empty_packet = bytearray((0xFF,) * trsf_size)
635
636 # Boot loader enable check
637 # TODO: implement check for all chip sizes & take into account partial firmware uploads
638 if (lng == 524288): #check if file is for 512K model
639 if not ((data[524247] & (1 << 4)) >> 4): #check the boot loader enable bit (only for 512K model)
640 if not ( conf['force'] or query_yes_no("The boot loader backdoor is not enabled "\
641 "in the firmware you are about to write to the target. "\
642 "You will NOT be able to reprogram the target using this tool if you continue! "\
643 "Do you want to continue?","no") ):
644 raise Exception('Aborted by user.')
645
646 mdebug(5, "Writing %(lng)d bytes starting at address 0x%(addr)08X" %
647 { 'lng': lng, 'addr': addr})
648
649 offs = 0
650 addr_set = 0
651
652 while lng > trsf_size: #check if amount of remaining data is less then packet size
653 if data[offs:offs+trsf_size] != empty_packet: #skip packets filled with 0xFF
654 if addr_set != 1:
655 self.cmdDownload(addr,lng) #set starting address if not set
656 addr_set = 1
657 mdebug(5, " Write %(len)d bytes at 0x%(addr)08X" % {'addr': addr, 'len': trsf_size}, '\r')
658 sys.stdout.flush()
659
660 self.cmdSendData(data[offs:offs+trsf_size]) # send next data packet
661 else: # skipped packet, address needs to be set
662 addr_set = 0
663
664 offs = offs + trsf_size
665 addr = addr + trsf_size
666 lng = lng - trsf_size
667
668 mdebug(5, "Write %(len)d bytes at 0x%(addr)08X" % {'addr': addr, 'len': lng})
669 self.cmdDownload(addr,lng)
670 return self.cmdSendData(data[offs:offs+lng]) # send last data packet
671
672class Chip(object):
673 def __init__(self, command_interface):
674 self.command_interface = command_interface
675
676 # Some defaults. The child can override.
677 self.flash_start_addr = 0x00000000
678 self.has_cmd_set_xosc = False
679
680 def crc(self, address, size):
681 return getattr(self.command_interface, self.crc_cmd)(address, size)
682
683 def disable_bootloader(self):
684 if not (conf['force'] or query_yes_no("Disabling the bootloader will prevent you from "\
685 "using this script until you re-enable the bootloader "\
686 "using JTAG. Do you want to continue?", "no")):
687 raise Exception('Aborted by user.')
688
689 if PY3:
690 pattern = struct.pack('<L', self.bootloader_dis_val)
691 else:
692 pattern = [ord(b) for b in struct.pack('<L', self.bootloader_dis_val)]
693
694 if cmd.writeMemory(self.bootloader_address, pattern):
695 mdebug(5, " Set bootloader closed done ")
696 else:
697 raise CmdException("Set bootloader closed failed ")
698
699class CC2538(Chip):
700 def __init__(self, command_interface):
701 super(CC2538, self).__init__(command_interface)
702 self.flash_start_addr = 0x00200000
703 self.addr_ieee_address_secondary = 0x0027ffcc
704 self.has_cmd_set_xosc = True
705 self.bootloader_dis_val = 0xefffffff
706 self.crc_cmd = "cmdCRC32"
707
708 FLASH_CTRL_DIECFG0 = 0x400D3014
709 FLASH_CTRL_DIECFG2 = 0x400D301C
710 addr_ieee_address_primary = 0x00280028
711 ccfg_len = 44
712
713 #Read out primary IEEE address, flash and RAM size
714 model = self.command_interface.cmdMemRead(FLASH_CTRL_DIECFG0)
715 self.size = (model[3] & 0x70) >> 4
716 if 0 < self.size <= 4:
717 self.size *= 0x20000 # in bytes
718 else:
719 self.size = 0x10000 # in bytes
720 self.bootloader_address = self.flash_start_addr + self.size - ccfg_len
721
722 sram = (((model[2] << 8) | model[3]) & 0x380) >> 7
723 sram = (2 - sram) << 3 if sram <= 1 else 32 # in KB
724
725 pg = self.command_interface.cmdMemRead(FLASH_CTRL_DIECFG2)
726 pg_major = (pg[2] & 0xF0) >> 4
727 if pg_major == 0:
728 pg_major = 1
729 pg_minor = pg[2] & 0x0F
730
731 ti_oui = bytearray([0x00, 0x12, 0x4B])
732 ieee_addr = self.command_interface.cmdMemRead(addr_ieee_address_primary)
733 ieee_addr_end = self.command_interface.cmdMemRead(addr_ieee_address_primary + 4)
734 if ieee_addr[:3] == ti_oui:
735 ieee_addr += ieee_addr_end
736 else:
737 ieee_addr = ieee_addr_end + ieee_addr
738
739 mdebug(5, "CC2538 PG%d.%d: %dKB Flash, %dKB SRAM, CCFG at 0x%08X"
740 % (pg_major, pg_minor, self.size >> 10, sram,
741 self.bootloader_address))
742 mdebug(5, "Primary IEEE Address: %s" % (':'.join('%02X' % x for x in ieee_addr)))
743
744 def erase(self):
745 mdebug(5, "Erasing %s bytes starting at address 0x%08X" % (self.size, self.flash_start_addr))
746 return self.command_interface.cmdEraseMemory(self.flash_start_addr, self.size)
747
748 def read_memory(self, addr):
749 # CC2538's COMMAND_MEMORY_READ sends each 4-byte number in inverted
750 # byte order compared to what's written on the device
751 data = self.command_interface.cmdMemRead(addr)
752 return bytearray([data[x] for x in range(3, -1, -1)])
753
754class CC26xx(Chip):
755 # Class constants
756 MISC_CONF_1 = 0x500010A0
757 PROTO_MASK_BLE = 0x01
758 PROTO_MASK_IEEE = 0x04
759 PROTO_MASK_BOTH = 0x05
760
761 def __init__(self, command_interface):
762 super(CC26xx, self).__init__(command_interface)
763 self.bootloader_dis_val = 0x00000000
764 self.crc_cmd = "cmdCRC32CC26xx"
765
766 ICEPICK_DEVICE_ID = 0x50001318
767 FCFG_USER_ID = 0x50001294
768 PRCM_RAMHWOPT = 0x40082250
769 FLASH_SIZE = 0x4003002C
770 addr_ieee_address_primary = 0x500012F0
771 ccfg_len = 88
772 ieee_address_secondary_offset = 0x20
773 bootloader_dis_offset = 0x30
774 sram = "Unknown"
775
776 # Determine CC13xx vs CC26xx via ICEPICK_DEVICE_ID::WAFER_ID and store
777 # PG revision
778 device_id = self.command_interface.cmdMemReadCC26xx(ICEPICK_DEVICE_ID)
779 wafer_id = (((device_id[3] & 0x0F) << 16) +
780 (device_id[2] << 8) +
781 (device_id[1] & 0xF0)) >> 4
782 pg_rev = (device_id[3] & 0xF0) >> 4
783
784 # Read FCFG1_USER_ID to get the package and supported protocols
785 user_id = self.command_interface.cmdMemReadCC26xx(FCFG_USER_ID)
786 package = {0x00: '4x4mm', 0x01: '5x5mm', 0x02: '7x7mm'}.get(user_id[2] & 0x03, "Unknown")
787 protocols = user_id[1] >> 4
788
789 # We can now detect the exact device
790 if wafer_id == 0xB99A:
791 chip = self._identify_cc26xx(pg_rev, protocols)
792 elif wafer_id == 0xB9BE:
793 chip = self._identify_cc13xx(pg_rev, protocols)
794
795 # Read flash size, calculate and store bootloader disable address
796 self.size = self.command_interface.cmdMemReadCC26xx(FLASH_SIZE)[0] * 4096
797 self.bootloader_address = self.size - ccfg_len + bootloader_dis_offset
798 self.addr_ieee_address_secondary = self.size - ccfg_len + ieee_address_secondary_offset
799
800 # RAM size
801 ramhwopt_size = self.command_interface.cmdMemReadCC26xx(PRCM_RAMHWOPT)[0] & 3
802 if ramhwopt_size == 3:
803 sram = "20KB"
804 elif ramhwopt_size == 2:
805 sram = "16KB"
806 else:
807 sram = "Unknown"
808
809 # Primary IEEE address. Stored with the MSB at the high address
810 ieee_addr = self.command_interface.cmdMemReadCC26xx(addr_ieee_address_primary + 4)[::-1]
811 ieee_addr += self.command_interface.cmdMemReadCC26xx(addr_ieee_address_primary)[::-1]
812
813 mdebug(5, "%s (%s): %dKB Flash, %s SRAM, CCFG.BL_CONFIG at 0x%08X"
814 % (chip, package, self.size >> 10, sram,
815 self.bootloader_address))
816 mdebug(5, "Primary IEEE Address: %s" % (':'.join('%02X' % x for x in ieee_addr)))
817
818 def _identify_cc26xx(self, pg, protocols):
819 chips_dict = {
820 CC26xx.PROTO_MASK_IEEE: 'CC2630',
821 CC26xx.PROTO_MASK_BLE: 'CC2640',
822 CC26xx.PROTO_MASK_BOTH: 'CC2650',
823 }
824
825 chip_str = chips_dict.get(protocols & CC26xx.PROTO_MASK_BOTH, "Unknown")
826
827 if pg == 1:
828 pg_str = "PG1.0"
829 elif pg == 3:
830 pg_str = "PG2.0"
831 elif pg == 7:
832 pg_str = "PG2.1"
833 elif pg == 8:
834 rev_minor = self.command_interface.cmdMemReadCC26xx(CC26xx.MISC_CONF_1)[0]
835 if rev_minor == 0xFF:
836 rev_minor = 0x00
837 pg_str = "PG2.%d" % (2 + rev_minor,)
838
839 return "%s %s" % (chip_str, pg_str)
840
841 def _identify_cc13xx(self, pg, protocols):
842 chip_str = "CC1310"
843 if protocols & CC26xx.PROTO_MASK_IEEE == CC26xx.PROTO_MASK_IEEE:
844 chip_str = "CC1350"
845
846 if pg == 0:
847 pg_str = "PG1.0"
848 elif pg == 2:
849 rev_minor = self.command_interface.cmdMemReadCC26xx(CC26xx.MISC_CONF_1)[0]
850 if rev_minor == 0xFF:
851 rev_minor = 0x00
852 pg_str = "PG2.%d" % (rev_minor,)
853
854 return "%s %s" % (chip_str, pg_str)
855
856 def erase(self):
857 mdebug(5, "Erasing all main bank flash sectors")
858 return self.command_interface.cmdBankErase()
859
860 def read_memory(self, addr):
861 # CC26xx COMMAND_MEMORY_READ returns contents in the same order as
862 # they are stored on the device
863 return self.command_interface.cmdMemReadCC26xx(addr)
864
865def query_yes_no(question, default="yes"):
866 valid = {"yes":True, "y":True, "ye":True,
867 "no":False, "n":False}
868 if default == None:
869 prompt = " [y/n] "
870 elif default == "yes":
871 prompt = " [Y/n] "
872 elif default == "no":
873 prompt = " [y/N] "
874 else:
875 raise ValueError("invalid default answer: '%s'" % default)
876
877 while True:
878 sys.stdout.write(question + prompt)
879 if PY3:
880 choice = input().lower()
881 else:
882 choice = raw_input().lower()
883 if default is not None and choice == '':
884 return valid[default]
885 elif choice in valid:
886 return valid[choice]
887 else:
888 sys.stdout.write("Please respond with 'yes' or 'no' "\
889 "(or 'y' or 'n').\n")
890
891# Convert the entered IEEE address into an integer
892def parse_ieee_address (inaddr):
893 try:
894 return int(inaddr, 16)
895 except ValueError:
896 # inaddr is not a hex string, look for other formats
897 if ':' in inaddr:
898 bytes = inaddr.split(':')
899 elif '-' in inaddr:
900 bytes = inaddr.split('-')
901 if len(bytes) != 8:
902 raise ValueError("Supplied IEEE address does not contain 8 bytes")
903 addr = 0
904 for i,b in zip(range(8), bytes):
905 try:
906 addr += int(b, 16) << (56-(i*8))
907 except ValueError:
908 raise ValueError("IEEE address contains invalid bytes")
909 return addr
910
911def print_version():
912 # Get the version using "git describe".
913 try:
914 p = Popen(['git', 'describe', '--tags', '--match', '[0-9]*'],
915 stdout=PIPE, stderr=PIPE)
916 p.stderr.close()
917 line = p.stdout.readlines()[0]
918 version = line.strip()
919 except:
920 # We're not in a git repo, or git failed, use fixed version string.
921 version = VERSION_STRING
922 print('%s %s' % (sys.argv[0], version))
923
924def usage():
925 print("""Usage: %s [-DhqVfewvr] [-l length] [-p port] [-b baud] [-a addr] [-i addr] [--bootloader-active-high] [--bootloader-invert-lines] [file.bin]
926 -h, --help This help
927 -q Quiet
928 -V Verbose
929 -f Force operation(s) without asking any questions
930 -e Erase (full)
931 -w Write
932 -v Verify (CRC32 check)
933 -r Read
934 -l length Length of read
935 -p port Serial port (default: first USB-like port in /dev)
936 -b baud Baud speed (default: 500000)
937 -a addr Target address
938 -i, --ieee-address addr Set the secondary 64 bit IEEE address
939 --bootloader-active-high Use active high signals to enter bootloader
940 --bootloader-invert-lines Inverts the use of RTS and DTR to enter bootloader
941 -D, --disable-bootloader After finishing, disable the bootloader
942 --version Print script version
943
944Examples:
945 ./%s -e -w -v example/main.bin
946 ./%s -e -w -v --ieee-address 00:12:4b:aa:bb:cc:dd:ee example/main.bin
947
948 """ % (sys.argv[0],sys.argv[0],sys.argv[0]))
949
950if __name__ == "__main__":
951
952 conf = {
953 'port': 'auto',
954 'baud': 500000,
955 'force_speed' : 0,
956 'address': None,
957 'force': 0,
958 'erase': 0,
959 'write': 0,
960 'verify': 0,
961 'read': 0,
962 'len': 0x80000,
963 'fname':'',
964 'ieee_address': 0,
965 'bootloader_active_high': False,
966 'bootloader_invert_lines' : False,
967 'disable-bootloader': 0
968 }
969
970# http://www.python.org/doc/2.5.2/lib/module-getopt.html
971
972 try:
973 opts, args = getopt.getopt(sys.argv[1:], "DhqVfewvrp:b:a:l:i:", ['help', 'ieee-address=', 'disable-bootloader', 'bootloader-active-high', 'bootloader-invert-lines', 'version'])
974 except getopt.GetoptError as err:
975 # print help information and exit:
976 print(str(err)) # will print something like "option -a not recognized"
977 usage()
978 sys.exit(2)
979
980 for o, a in opts:
981 if o == '-V':
982 QUIET = 10
983 elif o == '-q':
984 QUIET = 0
985 elif o == '-h' or o == '--help':
986 usage()
987 sys.exit(0)
988 elif o == '-f':
989 conf['force'] = 1
990 elif o == '-e':
991 conf['erase'] = 1
992 elif o == '-w':
993 conf['write'] = 1
994 elif o == '-v':
995 conf['verify'] = 1
996 elif o == '-r':
997 conf['read'] = 1
998 elif o == '-p':
999 conf['port'] = a
1000 elif o == '-b':
1001 conf['baud'] = eval(a)
1002 conf['force_speed'] = 1
1003 elif o == '-a':
1004 conf['address'] = eval(a)
1005 elif o == '-l':
1006 conf['len'] = eval(a)
1007 elif o == '-i' or o == '--ieee-address':
1008 conf['ieee_address'] = str(a)
1009 elif o == '--bootloader-active-high':
1010 conf['bootloader_active_high'] = True
1011 elif o == '--bootloader-invert-lines':
1012 conf['bootloader_invert_lines'] = True
1013 elif o == '-D' or o == '--disable-bootloader':
1014 conf['disable-bootloader'] = 1
1015 elif o == '--version':
1016 print_version()
1017 sys.exit(0)
1018 else:
1019 assert False, "Unhandled option"
1020
1021 try:
1022 # Sanity checks
1023 if conf['write'] or conf['read'] or conf['verify']: # check for input/output file
1024 try:
1025 args[0]
1026 except:
1027 raise Exception('No file path given.')
1028
1029 if conf['write'] and conf['read']:
1030 if not ( conf['force'] or query_yes_no("You are reading and writing to the same file. This will overwrite your input file. "\
1031 "Do you want to continue?","no") ):
1032 raise Exception('Aborted by user.')
1033 if conf['erase'] and conf['read'] and not conf['write']:
1034 if not ( conf['force'] or query_yes_no("You are about to erase your target before reading. "\
1035 "Do you want to continue?","no") ):
1036 raise Exception('Aborted by user.')
1037
1038 if conf['read'] and not conf['write'] and conf['verify']:
1039 raise Exception('Verify after read not implemented.')
1040
1041 if conf['len'] < 0:
1042 raise Exception('Length must be positive but %d was provided'
1043 % (conf['len'],))
1044
1045 # Try and find the port automatically
1046 if conf['port'] == 'auto':
1047 ports = []
1048
1049 # Get a list of all USB-like names in /dev
1050 for name in ['tty.usbserial', 'ttyUSB', 'tty.usbmodem', 'tty.SLAB_USBtoUART']:
1051 ports.extend(glob.glob('/dev/%s*' % name))
1052
1053 ports = sorted(ports)
1054
1055 if ports:
1056 # Found something - take it
1057 conf['port'] = ports[0]
1058 else:
1059 raise Exception('No serial port found.')
1060
1061 cmd = CommandInterface()
1062 cmd.open(conf['port'], conf['baud'])
1063 cmd.invoke_bootloader(conf['bootloader_active_high'], conf['bootloader_invert_lines'])
1064 mdebug(5, "Opening port %(port)s, baud %(baud)d" % {'port':conf['port'],
1065 'baud':conf['baud']})
1066 if conf['write'] or conf['verify']:
1067 mdebug(5, "Reading data from %s" % args[0])
1068 firmware = FirmwareFile(args[0])
1069
1070 mdebug(5, "Connecting to target...")
1071
1072 if not cmd.sendSynch():
1073 raise CmdException("Can't connect to target. Ensure boot loader is started. (no answer on synch sequence)")
1074
1075 # if (cmd.cmdPing() != 1):
1076 # raise CmdException("Can't connect to target. Ensure boot loader is started. (no answer on ping command)")
1077
1078 chip_id = cmd.cmdGetChipId()
1079 chip_id_str = CHIP_ID_STRS.get(chip_id, None)
1080
1081 if chip_id_str is None:
1082 mdebug(10, ' Unrecognized chip ID. Trying CC13xx/CC26xx')
1083 device = CC26xx(cmd)
1084 else:
1085 mdebug(10, " Target id 0x%x, %s" % (chip_id, chip_id_str))
1086 device = CC2538(cmd)
1087
1088 # Choose a good default address unless the user specified -a
1089 if conf['address'] is None:
1090 conf['address'] = device.flash_start_addr
1091
1092 if conf['force_speed'] != 1 and device.has_cmd_set_xosc:
1093 if cmd.cmdSetXOsc(): #switch to external clock source
1094 cmd.close()
1095 conf['baud'] = 1000000
1096 cmd.open(conf['port'], conf['baud'])
1097 mdebug(6, "Opening port %(port)s, baud %(baud)d" % {'port':conf['port'], 'baud':conf['baud']})
1098 mdebug(6, "Reconnecting to target at higher speed...")
1099 if (cmd.sendSynch() != 1):
1100 raise CmdException("Can't connect to target after clock source switch. (Check external crystal)")
1101 else:
1102 raise CmdException("Can't switch target to external clock source. (Try forcing speed)")
1103
1104 if conf['erase']:
1105 # we only do full erase for now
1106 if device.erase():
1107 mdebug(5, " Erase done")
1108 else:
1109 raise CmdException("Erase failed")
1110
1111 if conf['write']:
1112 # TODO: check if boot loader back-door is open, need to read flash size first to get address
1113 if cmd.writeMemory(conf['address'], firmware.bytes):
1114 mdebug(5, " Write done ")
1115 else:
1116 raise CmdException("Write failed ")
1117
1118 if conf['verify']:
1119 mdebug(5,"Verifying by comparing CRC32 calculations.")
1120
1121 crc_local = firmware.crc32()
1122 crc_target = device.crc(conf['address'], len(firmware.bytes)) #CRC of target will change according to length input file
1123
1124 if crc_local == crc_target:
1125 mdebug(5, " Verified (match: 0x%08x)" % crc_local)
1126 else:
1127 cmd.cmdReset()
1128 raise Exception("NO CRC32 match: Local = 0x%x, Target = 0x%x" % (crc_local,crc_target))
1129
1130 if conf['ieee_address'] != 0:
1131 ieee_addr = parse_ieee_address(conf['ieee_address'])
1132 if PY3:
1133 mdebug(5, "Setting IEEE address to %s" % (':'.join(['%02x' % b for b in struct.pack('>Q', ieee_addr)])))
1134 ieee_addr_bytes = struct.pack('<Q', ieee_addr)
1135 else:
1136 mdebug(5, "Setting IEEE address to %s" % (':'.join(['%02x' % ord(b) for b in struct.pack('>Q', ieee_addr)])))
1137 ieee_addr_bytes = [ord(b) for b in struct.pack('<Q', ieee_addr)]
1138
1139 if cmd.writeMemory(device.addr_ieee_address_secondary, ieee_addr_bytes):
1140 mdebug(5, " Set address done ")
1141 else:
1142 raise CmdException("Set address failed ")
1143
1144 if conf['read']:
1145 length = conf['len']
1146
1147 # Round up to a 4-byte boundary
1148 length = (length + 3) & ~0x03
1149
1150 mdebug(5, "Reading %s bytes starting at address 0x%x" % (length, conf['address']))
1151 with open(args[0], 'wb') as f:
1152 for i in range(0, length >> 2):
1153 rdata = device.read_memory(conf['address'] + (i * 4)) #reading 4 bytes at a time
1154 mdebug(5, " 0x%x: 0x%02x%02x%02x%02x" % (conf['address'] + (i * 4), rdata[0], rdata[1], rdata[2], rdata[3]), '\r')
1155 f.write(rdata)
1156 f.close()
1157 mdebug(5, " Read done ")
1158
1159 if conf['disable-bootloader']:
1160 device.disable_bootloader()
1161
1162 cmd.cmdReset()
1163
1164 except Exception as err:
1165 if QUIET >= 10:
1166 traceback.print_exc()
1167 exit('ERROR: %s' % str(err))