1 | #!/usr/bin/python
|
2 |
|
3 | # 2013-Jul-25 Lars Ole Belhage, belhage@midibel.com
|
4 | # 2014-Jan-01 Axel Heider, axelheider@gmx.de
|
5 | #
|
6 | # A simple script to program a LPC810 via ISP serial interface
|
7 | # It also calculates the signature and that checks code-read-protection is off
|
8 |
|
9 | import sys
|
10 | import argparse
|
11 | import serial
|
12 |
|
13 | #
|
14 | # http://www.nxp.com/documents/user_manual/UM10601.pdf
|
15 | #
|
16 | # Memory map:
|
17 | # 0x00000000
|
18 | # Flash (4 Kb)
|
19 | # 0x00001000
|
20 | # Flash (on devices with 8 Kb)
|
21 | # 0x00002000
|
22 | # Flash (on devices with 16 Kb)
|
23 | # 0x00004000
|
24 | # ...
|
25 | # 0x10000000
|
26 | # SRAM (1 Kb)
|
27 | # 0x10000400
|
28 | # SRAM (on devices with 2 Kb)
|
29 | # 0x10000800
|
30 | # SRAM (on devices with 4 Kb)
|
31 | # 0x10001000
|
32 | # ...
|
33 | # 0x14000000
|
34 | # Micro Trace Buffer (1 Kb)
|
35 | # 0x14000400
|
36 | # ...
|
37 | # 0x1FFF0000
|
38 | # Boot ROM (8 Kb)
|
39 | # 0x1FFF2000
|
40 | # ....
|
41 | #
|
42 | # Flash:
|
43 | # page size: 64 Byte
|
44 | # sector size: 1 kB (16 pages)
|
45 | #
|
46 | # Sector Page Address 4 KB 8 KB 16 Kb
|
47 | # 0 0 - 15 0x00000000 - 0x000003FF x x x
|
48 | # 1 16 - 31 0x00000400 - 0x000007FF x x x
|
49 | # 2 32 - 47 0x00000800 - 0x00000BFF x x x
|
50 | # 3 48 - 63 0x00000C00 - 0x00000FFF x x x
|
51 | # 4 64 - 79 0x00001000 - 0x000013FF - x x
|
52 | # 5 80 - 95 0x00001400 - 0x000017FF - x x
|
53 | # 6 96 - 111 0x00001800 - 0x00001BFF - x x
|
54 | # 7 112 - 127 0x00001C00 - 0x00001FFF - x x
|
55 | # 8 128 - 143 0x00002000 - 0x000023FF - - x
|
56 | # 9 144 - 159 0x00002400 - 0x000027FF - - x
|
57 | # 10 160 - 175 0x00002800 - 0x00002BFF - - x
|
58 | # 11 176 - 191 0x00002C00 - 0x00002FFF - - x
|
59 | # 12 192 - 207 0x00003000 - 0x000033FF - - x
|
60 | # 13 208 - 223 0x00003400 - 0x000037FF - - x
|
61 | # 14 224 - 239 0x00003800 - 0x00003BFF - - x
|
62 | # 15 240 - 255 0x00003C00 - 0x00003FFF - - x
|
63 | #
|
64 | # 80 byte of SRAM from 0x10000000 to 0x10000050 are not used by the bootloader,
|
65 | # thus content in this area is retained during reset. SRAM memory is not
|
66 | # retained for deep power-down mode for full power.
|
67 | #
|
68 |
|
69 | ADDR_CHECKSUM = 0x001C
|
70 |
|
71 | ADDR_CRP = 0x02fc
|
72 | NO_ISP = 0x4E697370
|
73 | CRP1 = 0x12345678
|
74 | CRP2 = 0x87654321
|
75 | CRP3 = 0x43218765
|
76 |
|
77 |
|
78 |
|
79 | #-------------------------------------------------------------------------------
|
80 | def printVerbose(level,string):
|
81 | if (args.verbose >= level):
|
82 | print(string)
|
83 |
|
84 | #-------------------------------------------------------------------------------
|
85 | def hexdump(data, indentStr="", printOffs=0, length=16, sep='.'):
|
86 | FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or sep for x in range(256)])
|
87 | lines = []
|
88 | for c in xrange(0, len(data), length):
|
89 | chars = data[c:c+length]
|
90 | hexStr = ' '.join(["%02x"%x for x in chars])
|
91 | if len(hexStr) > 24:
|
92 | hexStr = "%s %s"%(hexStr[:24], hexStr[24:])
|
93 | printable = ''.join(["%s"%((x<=127 and FILTER[x]) or sep) for x in chars])
|
94 | lines.append("%s%08x: %-*s | %s"%(indentStr,printOffs+c, length*3, hexStr, printable))
|
95 | print "\n".join(lines)
|
96 |
|
97 | #-------------------------------------------------------------------------------
|
98 | def get_uint32(data, offset):
|
99 | a = data[offset:offset+4]
|
100 | v = sum(a[i]<<(i*8) for i in range(4))
|
101 | return v
|
102 |
|
103 |
|
104 | #-------------------------------------------------------------------------------
|
105 | def sendSer(data):
|
106 | printVerbose(2,"sendRaw: <"+data+">")
|
107 | ser.write(data)
|
108 | ser.flush()
|
109 |
|
110 | #-------------------------------------------------------------------------------
|
111 | def sendSerCrLf(data):
|
112 | printVerbose(2,"sendCrLf: <"+data+"> CR LF")
|
113 | ser.write(data+"\r\n")
|
114 | ser.flush()
|
115 |
|
116 | #-------------------------------------------------------------------------------
|
117 | def readSer():
|
118 | ret = ser.readline().rstrip()
|
119 | printVerbose(2,"read: <"+ret+">")
|
120 | return ret
|
121 |
|
122 | #-------------------------------------------------------------------------------
|
123 | def sendCmd(cmd, dataResp=0):
|
124 |
|
125 | CMD_SUCCESS = 0
|
126 | ERR_INVALID_CMD = 1
|
127 | ERR_SRC_ADDR = 2 # not on word boundary.
|
128 | ERR_DST_ADDR = 3 # not on a correct boundary.
|
129 | ERR_SRC_ADDR_NOT_MAPPED = 4 # not mapped in memory map. Count value is taken in to consideration where applicable.
|
130 | ERR_DST_ADDR_NOT_MAPPED = 5 # not mapped in memory map. Count value is taken in to consideration where applicable.
|
131 | ERR_COUNT = 6 # Byte count is not multiple of 4 or is not a permitted value.
|
132 | ERR_INVALID_SECTOR = 7 # Sector number is invalid or end sector number is greater than start sector number.
|
133 | ERR_SECTOR_NOT_BLANK = 8
|
134 | ERR_SECTOR_NOT_PREPARED = 9 # Command to prepare sector for write operation was not executed.
|
135 | ERR_COMPARE = 10
|
136 | ERR_BUSY = 11 # Flash programming hardware interface is busy.
|
137 | ERR_PARAM = 12
|
138 | ERR_ADDR = 13 # not on word boundary
|
139 | ERR_ADDR_NOT_MAPPED = 14 # Address is not mapped in the memory map. Count value is taken in to consideration where applicable.
|
140 | ERR_CMD_LOCKED = 15
|
141 | ERR_INVALID_CODE = 16
|
142 | ERR_INVALID_BAUD_RATE = 17
|
143 | ERR_INVALID_STOP_BIT = 18
|
144 | ERR_CODE_READ_PROTECTION_ENABLED = 19
|
145 |
|
146 | sendSerCrLf(cmd)
|
147 | if (echo != 0):
|
148 | cmdEcho = readSer()
|
149 | printVerbose(2,"ECHO: <"+cmdEcho+">")
|
150 |
|
151 | cmdRetStr = readSer()
|
152 | printVerbose(2,"cmdRetStr = <"+cmdRetStr+">")
|
153 | if (cmdRetStr != "0"):
|
154 | print("ERROR: command <"+cmd+"> failed, code= <"+cmdRetStr+">")
|
155 |
|
156 | return int(cmdRetStr)
|
157 |
|
158 | #-------------------------------------------------------------------------------
|
159 | def Cmd_A(val):
|
160 | printVerbose(1,"Set Echo to %d"%(val))
|
161 | cmdStr = "A %d"%(val)
|
162 | cmdRet = sendCmd(cmdStr)
|
163 | if (cmdRet != 0):
|
164 | quit()
|
165 |
|
166 | global echo
|
167 | echo = val
|
168 |
|
169 | #-------------------------------------------------------------------------------
|
170 | def Cmd_C(addrFlash, addrRam, numBytes):
|
171 | printVerbose(1,"Copy %d byte from 0x%08x in RAM to 0x%08x in flash"%(numBytes,addrRam,addrFlash))
|
172 | cmdStr = "C %d %d %d"%(addrFlash,addrRam,numBytes)
|
173 | cmdRet = sendCmd(cmdStr)
|
174 | if (cmdRet != 0):
|
175 | quit()
|
176 |
|
177 | #-------------------------------------------------------------------------------
|
178 | def Cmd_E(startSector,endSector):
|
179 | printVerbose(1,"Erasing sector %d - %d"%(startSector,endSector))
|
180 | cmdStr = "E %d %d"%(startSector,endSector)
|
181 | cmdRet = sendCmd(cmdStr)
|
182 | if (cmdRet != 0):
|
183 | quit()
|
184 |
|
185 | #-------------------------------------------------------------------------------
|
186 | def Cmd_G(addr):
|
187 | # execute a program residing in RAM or flash memory. address must beof 0x00000200 or
|
188 | # greater. "T" is thumb mode
|
189 | printVerbose(1,"Execure code at 0x08x"%(addr))
|
190 | cmdStr = "G %d T"%(addr)
|
191 | cmdRet = sendCmd(cmdStr)
|
192 |
|
193 | #-------------------------------------------------------------------------------
|
194 | def Cmd_J():
|
195 | cmdRet = sendCmd("J")
|
196 | if (cmdRet != 0):
|
197 | quit()
|
198 | partID = int(readSer())
|
199 | return partID
|
200 |
|
201 |
|
202 | #-------------------------------------------------------------------------------
|
203 | def Cmd_K():
|
204 | cmdRet = sendCmd("K")
|
205 | if (cmdRet != 0):
|
206 | quit()
|
207 | bootRomMinor = int(readSer())
|
208 | bootRomMajor = int(readSer())
|
209 |
|
210 | return (bootRomMajor, bootRomMinor)
|
211 |
|
212 | #-------------------------------------------------------------------------------
|
213 | def Cmd_M(addr1,addr2,numBytes):
|
214 | printVerbose(1,"Compare %d bytes at 0x%08x and 0x%08x"%(numBytes,addr1,addr2))
|
215 | cmdStr = "M %d %d %d"%(addr1,addr2,numBytes)
|
216 | cmdRet = sendCmd(cmdStr)
|
217 | return cmdRet
|
218 |
|
219 | #-------------------------------------------------------------------------------
|
220 | def Cmd_N():
|
221 | cmdRet = sendCmd("N")
|
222 | if (cmdRet != 0):
|
223 | quit()
|
224 | uid1 = int(readSer())
|
225 | uid2 = int(readSer())
|
226 | uid3 = int(readSer())
|
227 | uid4 = int(readSer())
|
228 | return (uid1,uid2,uid3,uid4)
|
229 |
|
230 | #-------------------------------------------------------------------------------
|
231 | def Cmd_P(startSector,endSector):
|
232 | printVerbose(1,"Prepare sector %d - %d for write operation"%(startSector,endSector))
|
233 | cmdStr = "P %d %d"%(startSector,endSector)
|
234 | cmdRet = sendCmd(cmdStr)
|
235 | if (cmdRet != 0):
|
236 | quit()
|
237 |
|
238 | #-------------------------------------------------------------------------------
|
239 | def Cmd_R(addr,numBytes):
|
240 | printVerbose(1,"Read %d bytes from 0x%08x"%(numBytes,addr))
|
241 | cmdStr = "R %d %d"%(addr,numBytes)
|
242 | cmdRet = sendCmd(cmdStr,numBytes)
|
243 | if (cmdRet != 0):
|
244 | quit()
|
245 |
|
246 | data = []
|
247 | while (numBytes != 0):
|
248 | c = ord(ser.read())
|
249 | data.append(c)
|
250 | numBytes -= 1
|
251 |
|
252 | if (args.verbose > 0):
|
253 | hexdump(data)
|
254 |
|
255 | return data
|
256 |
|
257 | #-------------------------------------------------------------------------------
|
258 | def Cmd_S(ramAddr,numBytes):
|
259 | printVerbose(1,"Read CRC checksum of block at 0x%08x len &d"%(ramAddr,numBytes))
|
260 | cmdStr = "S %d %d"%(ramAddr,numBytes)
|
261 | cmdRet = sendCmd(cmdStr)
|
262 | if (cmdRet != 0):
|
263 | quit()
|
264 | crc = int(readSer())
|
265 | return crc
|
266 |
|
267 | #-------------------------------------------------------------------------------
|
268 | def Cmd_U(val):
|
269 | printVerbose(1,"unlocking with code %d"%(val))
|
270 | cmdStr = "U %d"%(val)
|
271 | cmdRet = sendCmd(cmdStr)
|
272 | if (cmdRet != 0):
|
273 | quit()
|
274 |
|
275 | #-------------------------------------------------------------------------------
|
276 | def Cmd_W(addr,data):
|
277 | numBytes = len(data)
|
278 | printVerbose(1,"Write %d byte to RAM at 0x%08x"%(numBytes,addr))
|
279 |
|
280 | if (args.verbose > 0):
|
281 | hexdump(data)
|
282 |
|
283 | cmdStr = "W %d %d"%(addr,numBytes)
|
284 | cmdRet = sendCmd(cmdStr)
|
285 | if (cmdRet != 0):
|
286 | quit()
|
287 |
|
288 | ser.write(data)
|
289 |
|
290 | #-------------------------------------------------------------------------------
|
291 | def getPartName(partID):
|
292 | return { 0x00008100: "LPC810M021FN8",
|
293 | # 0x00008110: "LPC811M001JDH16",
|
294 | # 0x00008120: "LPC812M101JDH16",
|
295 | # 0x00008121: "LPC812M101JD20",
|
296 | # 0x00008122: "LPC812M101JDH20"
|
297 | }.get(partID, "")
|
298 |
|
299 | #-------------------------------------------------------------------------------
|
300 | def unlock():
|
301 | Cmd_U(23130) # magic
|
302 |
|
303 | #-------------------------------------------------------------------------------
|
304 | def getInfo():
|
305 | partID = Cmd_J()
|
306 | (uid1,uid2,uid3,uid4) = Cmd_N()
|
307 | (bootRomMajor, bootRomMinor) = Cmd_K()
|
308 |
|
309 | partName = getPartName(partID);
|
310 | partNameStr = partName
|
311 | if (partNameStr == ""):
|
312 | partNameStr = "unknown"
|
313 |
|
314 | print("Device:")
|
315 | print(" PartID: 0x%08x (%s)"%(partID,partNameStr))
|
316 | print(" UID: %08x-%08x-%08x-%08x"%(uid1,uid2,uid3,uid4))
|
317 | print(" Boot Loder: V%d.%d"%(bootRomMajor,bootRomMinor))
|
318 |
|
319 | if (partName == ""):
|
320 | print("aborting due to unknown device")
|
321 | quit()
|
322 |
|
323 |
|
324 |
|
325 | #-------------------------------------------------------------------------------
|
326 | def init():
|
327 | # serial setting: 8 data bits, 1 stop bit, no parity.
|
328 | # Device detects baud rate automatically when host sends "?" (0x3F) as the
|
329 | # first character. ASCII string "Synchronized"+CR+LF is send back then with
|
330 | # the detected baud rate. Host must send the same "Synchronized"+CR+LF
|
331 | # back, so device can verify the baud rate. If synchronization is verified
|
332 | # then device sends "OK"+CR+LF.
|
333 | # Then host must send the crystal frequency (in kHz) at which the part is
|
334 | # running. The response is required for backward compatibility of the boot
|
335 | # loader code. On the LPC800 it is ignored, boot loader configures 12 MHz
|
336 | # IRC frequency.
|
337 | # Now ISP command handler is invoked. For safety reasons an "Unlock"
|
338 | # command is required before executing the commands resulting in flash
|
339 | # erase/write operations and the "Go" command. The rest of the commands
|
340 | # can be executed without the unlock command. The Unlock command is
|
341 | # required to be executed once per ISP session.
|
342 |
|
343 | isEcho = 1
|
344 | syncStr = "Synchronized"
|
345 |
|
346 | ser.timeout = 2
|
347 | sendSer("?")
|
348 | ser.timeout = 1
|
349 | j = readSer()
|
350 | if j == syncStr:
|
351 | print("LPC8xx ISP setup")
|
352 | sendSerCrLf(syncStr)
|
353 | readSer() # cmd echo
|
354 | readSer() # "OK"
|
355 | sendSerCrLf("12") # backwards compatibility dummy
|
356 | readSer() # cmd echo
|
357 | readSer() # "OK"
|
358 | else:
|
359 | sendSerCrLf("")
|
360 | c = readSer()
|
361 | if (j != "?"):
|
362 | # already in ISP more with echo off?
|
363 | isEcho = 0
|
364 | else:
|
365 | # already in ISP more with echo on?
|
366 | if (c != ""):
|
367 | print("LPC8xx ISP mode error 1, read: <"+c+">")
|
368 | quit()
|
369 | c = readSer()
|
370 |
|
371 | if (c != "1"):
|
372 | print("LPC8xx ISP mode error 2, read: <"+c+">")
|
373 | quit()
|
374 |
|
375 | global echo
|
376 | echo = isEcho
|
377 |
|
378 | # try echo off command
|
379 | Cmd_A(0)
|
380 |
|
381 | print("LPC8xx ISP ready")
|
382 |
|
383 |
|
384 | #-------------------------------------------------------------------------------
|
385 | def getWordFromImage(data,addr):
|
386 | return (data[addr+3] << 24) \
|
387 | | (data[addr+2] << 16) \
|
388 | | (data[addr+1] << 8) \
|
389 | | data[addr]
|
390 |
|
391 | #-------------------------------------------------------------------------------
|
392 | def setWordInImage(data,addr,val):
|
393 | data[addr] = (val >> 0) & 0xff
|
394 | data[addr+1] = (val >> 8) & 0xff
|
395 | data[addr+2] = (val >> 16) & 0xff
|
396 | data[addr+3] = (val >> 24) & 0xff
|
397 |
|
398 |
|
399 |
|
400 | #-------------------------------------------------------------------------------
|
401 | def calcChecksum(data):
|
402 |
|
403 | # The reserved Cortex-M0+ exception vector (offset 0x1C) contains the
|
404 | # 2-complement of the check-sum of vector table entries 0 through 6. This
|
405 | # causes the checksum of the first 8 table entries to be 0. The bootloader
|
406 | # code checksums the first 8 locations in sector 0 of the flash. If the
|
407 | # result is 0, then execution control is transferred to the user code.
|
408 |
|
409 | dlen = len(data)
|
410 | if (dlen < ADDR_CHECKSUM+4):
|
411 | print("Error: not enough data to insert the checksum")
|
412 | quit()
|
413 |
|
414 | sig = 0
|
415 | for v in range(0, 7):
|
416 | sig += getWordFromImage(data, 4*v)
|
417 |
|
418 | sig ^= 0xffffffff
|
419 | sig += 1
|
420 |
|
421 | print("setting checksum: 0x%08x"%(sig))
|
422 |
|
423 | setWordInImage(data,ADDR_CHECKSUM,sig)
|
424 |
|
425 |
|
426 | #-------------------------------------------------------------------------------
|
427 | def checkCrp(data):
|
428 |
|
429 | # Code Read Protection (CRP)
|
430 | # any CRP change becomes effective in the next power cycle.
|
431 | #
|
432 | # CRP User ISP entry SWD Enters partial flash
|
433 | # Code pin at enabled ISP mode update in
|
434 | # Valid reset ISP mode
|
435 | #
|
436 | # None No x Yes Yes Yes
|
437 | # None Yes High Yes No NA
|
438 | # None Yes Low Yes Yes Yes
|
439 | # CRP1 Yes High No No NA
|
440 | # CRP1 Yes Low No Yes Yes
|
441 | # CRP2 Yes High No No NA
|
442 | # CRP2 Yes Low No Yes No
|
443 | # CRP3 Yes x No No NA
|
444 | # CRP1 No x No Yes Yes
|
445 | # CRP2 No x No Yes No
|
446 | # CRP3 No x No Yes No
|
447 |
|
448 |
|
449 | dlen = len(data)
|
450 | if (dlen >= ADDR_CRP+4):
|
451 | magic = getWordFromImage(data,ADDR_CRP)
|
452 | if (magic in [NO_ISP, CRP1, CRP2, CRP3]):
|
453 | print("Error: found CRP-Magic word 0x%08x"%(magic))
|
454 | quit()
|
455 |
|
456 |
|
457 | #-------------------------------------------------------------------------------
|
458 | def flash(image):
|
459 |
|
460 | data = bytearray(image.read())
|
461 | image.close()
|
462 |
|
463 | dlen = len(data)
|
464 | sect = dlen/1024
|
465 | rlen = ((dlen + 63) / 64) * 64
|
466 | printVerbose(1,"dlen=%d, rlen=%d, sect=%d"%(dlen, rlen, sect))
|
467 |
|
468 | # padd
|
469 | data += bytearray(rlen-dlen)
|
470 |
|
471 | checkCrp(data)
|
472 |
|
473 | calcChecksum(data)
|
474 |
|
475 | unlock()
|
476 | Cmd_P(0,sect)
|
477 | Cmd_E(0,sect)
|
478 |
|
479 | tmpRamAddr = 0x10000000
|
480 | dataOffset = 0
|
481 | flashAddr = 0
|
482 | blockLen = 64
|
483 |
|
484 |
|
485 | if (args.verbose == 0):
|
486 | sys.stdout.write("writing")
|
487 | sys.stdout.flush()
|
488 |
|
489 | while (dataOffset < dlen):
|
490 | if (args.verbose == 0):
|
491 | sys.stdout.write(".")
|
492 | sys.stdout.flush()
|
493 | else:
|
494 | printVerbose(1,"Write block: 0x%08x"%(flashAddr))
|
495 |
|
496 | Cmd_P(0,sect)
|
497 |
|
498 | Cmd_W(tmpRamAddr,data[dataOffset:dataOffset+blockLen])
|
499 | Cmd_R(tmpRamAddr,blockLen)
|
500 |
|
501 | Cmd_C(flashAddr,tmpRamAddr,blockLen)
|
502 | Cmd_R(flashAddr,blockLen)
|
503 |
|
504 | flashAddr += blockLen
|
505 | dataOffset += blockLen
|
506 |
|
507 | if (args.verbose == 0):
|
508 | print("done")
|
509 |
|
510 | print("%d bytes were written"%(dlen))
|
511 |
|
512 |
|
513 |
|
514 | #-------------------------------------------------------------------------------
|
515 | if __name__ == '__main__':
|
516 | parser = argparse.ArgumentParser()
|
517 | parser.add_argument("-p", dest="port", default="/dev/ttyUSB0", help="serial/USB port, default is /dev/ttyUSB0")
|
518 | parser.add_argument('--verbose', '-v', default=0, action='count', help="verbosity level, -v or -vv or -vvv ...")
|
519 | parser.add_argument("image", nargs="?", type=argparse.FileType('rt'), help="image to flash")
|
520 | args = parser.parse_args()
|
521 | if (args.verbose > 0):
|
522 | print(args)
|
523 |
|
524 | ser = serial.Serial(args.port,
|
525 | baudrate=115200, bytesize=8, parity='N', stopbits=1,
|
526 | timeout=10, xonxoff=0, rtscts=0)
|
527 | init()
|
528 | getInfo()
|
529 |
|
530 | if (args.image):
|
531 | print("RAM dump of contents surviving a reset:")
|
532 | data = Cmd_R(0x10000000, 80)
|
533 | hexdump(data," ")
|
534 | flash(args.image)
|
535 | else:
|
536 | print("RAM dump (first 0x80 survive reset):")
|
537 | data = Cmd_R(0x10000000, 0x400)
|
538 | hexdump(data," ")
|
539 | print("Flash dump:")
|
540 | data = Cmd_R(0x00000000, 0x1000)
|
541 | print(" Image checksum at 0x%08x: 0x%08x"%(ADDR_CHECKSUM,get_uint32(data,ADDR_CHECKSUM)))
|
542 | print(" CRP at 0x%08x: 0x%08x"%(ADDR_CRP,get_uint32(data,ADDR_CRP)))
|
543 | hexdump(data," ")
|