flushmagic.py


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,"  ")