use this (http://svenpeter.blogspot.com/2009/08/pyftdi.html) wrapper for
[pyfrprog.git] / frprog.py
1 #!/usr/bin/env python
2 import sys, time, pyftdi
3
4 # serial device to communicate with
5 DEVICE1=0x0403
6 DEVICE2=0x6001
7 # baudrate used for initialization
8 INIT_BAUDRATE=9600
9 # baudrate used for communication with the internal bootloader after init
10 BOOTLOADER_BAUDRATE=38400
11 # baudrate used for communication with the pkernel program that does the flashing eventually
12 KERNEL_BAUDRATE=1000000
13
14 # contains the last received checksum from a READ, WRITE or CHECKSUM command
15 last_checksum = 0
16
17 class FlashSequence(object):
18         def __init__(self, address, data):
19                 self.address = address
20                 self.data = data
21
22 def sendByte(byte):
23         tty.write_data(chr(byte))
24
25 def sendWord(word):
26         sendByte(word & 0xFF)
27         sendByte((word >> 8) & 0xFF)
28
29 def sendDWord(dword):
30         sendByte(dword & 0xFF)
31         sendByte((dword >> 8) & 0xFF)
32         sendByte((dword >> 16) & 0xFF)
33         sendByte((dword >> 24) & 0xFF)
34
35 def recvByte():
36         bla = tty.read_data(1)
37         while(len(bla) < 1):
38                 bla = tty.read_data(1)
39         return ord(bla)
40
41 def recvChecksum():
42         global last_checksum
43         last_checksum = recvByte()
44         last_checksum |= (recvByte() << 8)
45
46 def bootromREAD(address, size):
47         # send READ command
48         sendByte(0x01)
49         if (recvByte() != 0xF1):
50                 raise Exception
51         sendByte(0x02)
52         if (recvByte() != 0x82):
53                 raise Exception
54         # tell desired address and size
55         sendDWord(address)
56         sendWord(size)
57         # get binary stream of data
58         data = []
59         for i in range(0, size):
60                 data.append(recvByte())
61         # get checksum
62         recvChecksum()
63         return data
64
65 def bootromWRITE(address, size, data):
66         # send WRITE command
67         sendByte(0x01)
68         if (recvByte() != 0xF1):
69                 raise Exception
70         sendByte(0x03)
71         if (recvByte() != 0x83):
72                 raise Exception
73         # tell desired address and size
74         sendDWord(address)
75         sendWord(size)
76         # write binary stream of data
77         for i in range(0, size):
78                 sendByte(data[i])
79         # get checksum
80         recvChecksum()
81
82 # TODO: test this function!
83 def bootromCALL(address):
84         # send CALL command
85         sendByte(0x01)
86         if (recvByte() != 0xF1):
87                 raise Exception
88         sendByte(0x04)
89         if (recvByte() != 0x84):
90                 raise Exception
91         # tell desired address
92         sendDWord(address)
93         # wait for return parameter - not needed here!
94         #return recvByte()
95
96 # TODO: test this function!
97 def bootromCHECKSUM():
98         # call CHECKSUM command
99         sendByte(0x01)
100         if (recvByte() != 0xF1):
101                 raise Exception
102         sendByte(0x05)
103         if (recvByte() != 0x84):
104                 raise Exception
105         # get checksum
106         recvChecksum()
107
108 def bootromBAUDRATE(baudrate):
109         # send BAUDRATE command
110         sendByte(0x01)
111         if (recvByte() != 0xF1):
112                 raise Exception
113         sendByte(0x06)
114         if (recvByte() != 0x86):
115                 raise Exception
116         # send desired baudrate
117         sendDWord(baudrate)
118
119 def pkernCHIPERASE():
120         sendByte(0x15)
121         if (recvByte() != 0x45):
122                 raise Exception
123         # wait till completion...
124         if (recvByte() != 0x23):
125                 raise Exception
126
127 def pkernERASE(address, size):
128         sendByte(0x12)
129         if (recvByte() != 0x11):
130                 raise Exception
131         sendDWord(address)
132         sendWord(size)
133         if (recvByte() != 0x18):
134                 raise Exception
135
136 def pkernWRITE(address, size, data):
137         # send WRITE command
138         sendByte(0x13)
139         if (recvByte() != 0x37):
140                 raise Exception
141         # tell desired address and size
142         sendDWord(address)
143         sendWord(size)
144
145         # write binary stream of data
146         for i in range(0, size):
147                 sendByte(data[i])
148
149         if (recvByte() != 0x28):
150                 raise Exception
151
152 def readMHXFile(filename): # desired mhx filename
153         fp = open(filename, "r")
154         retval = [] # returns a list of FlashSequence objects
155         linecount = 0
156         for line in fp:
157                 linecount += 1
158                 # get rid of newline characters
159                 line = line.strip()
160
161                 # we're only interested in S2 (data sequence with 3 address bytes) records by now
162                 if line[0:2] == "S2":
163                         byte_count = int(line[2:4], 16)
164                         # just to get sure, check if byte count field is valid
165                         if (len(line)-4) != (byte_count*2):
166                                 print sys.argv[0] + ": Warning - inavlid byte count field in " + \
167                                         sys.argv[1] + ":" + str(linecount) + ", skipping line!"
168                                 continue
169
170                         # address and checksum bytes are not needed
171                         byte_count -= 4
172                         address = int(line[4:10], 16)
173                         datastr = line[10:10+byte_count*2]
174
175                         # convert data hex-byte-string to real byte data list
176                         data = []
177                         for i in range(0, len(datastr)/2):
178                                 data.append(int(datastr[2*i:2*i+2], 16))
179
180                         # add flash sequence to our list
181                         retval.append(FlashSequence(address, data))
182         fp.close()
183         return retval
184
185 def main(argv=None):
186         # check command line arguments
187         if argv is None:
188                 argv = sys.argv
189         if len(argv) != 2:
190                 print "Usage: " + argv[0] + " [target mhx-file]"
191                 return 1
192
193         # read in data from mhx-files before starting
194         try:
195                 try:
196                         bootloaderseqs = readMHXFile("pkernel/pkernel.mhx")
197                 except IOError as error1:
198                         bootloaderseqs = readMHXFile("%PREFIX%/share/frprog/pkernel.mhx")
199                 pkernelseqs = readMHXFile(argv[1])
200         except IOError as error:
201                 print argv[0] + ": Error - couldn't open file " + error.filename + "!"
202                 return 1
203
204         print "Initializing serial port..."
205         global tty
206         tty = pyftdi.FTDI()
207         tty.usb_open(DEVICE1, DEVICE2)
208         tty.set_baudrate(INIT_BAUDRATE)
209         #tty.set_latency_timer(100)
210         #print "latency:", tty.get_latency_timer()
211
212         print "Please press RESET on your board..."
213
214         while True:
215                 tty.write_data('V')
216                 try:
217                         if tty.read_data(1) == 'F':
218                                 break
219                 except Exception: # hm... thats not true atm :/ no timeout here i think
220                         # timeout happened, who cares ;-)
221                         pass
222
223         starttime = time.time() # save time at this point for evaluating the duration at the end
224
225         print "OK, trying to set baudrate..."
226         # set baudrate
227         bootromBAUDRATE(BOOTLOADER_BAUDRATE)
228         time.sleep(0.1) # just to get sure that the bootloader is really running in new baudrate mode!
229         del tty
230         tty = pyftdi.FTDI()
231         tty.usb_open(DEVICE1, DEVICE2)
232         tty.set_baudrate(BOOTLOADER_BAUDRATE)
233
234         print "Transfering pkernel program to IRAM",
235         # let the fun begin!
236         for seq in bootloaderseqs:
237                 if(seq.address <= 0x40000):
238                         addr = seq.address
239                 else:
240                         continue
241                 #print "RAMing", len(seq.data), "bytes at address", hex(addr)
242                 bootromWRITE(addr, len(seq.data), seq.data)
243                 sys.stdout.write(".")
244                 sys.stdout.flush()
245         print
246
247         # execute our pkernel finally and set pkernel conform baudrate
248         bootromCALL(0x30000)
249         time.sleep(0.1) # just to get sure that the pkernel is really running!
250         del tty
251         tty = pyftdi.FTDI()
252         tty.usb_open(DEVICE1, DEVICE2)
253         tty.set_baudrate(KERNEL_BAUDRATE)
254
255         print "Performing ChipErase..."
256         pkernCHIPERASE()
257
258         print "Flashing",
259         for seq in pkernelseqs:
260                 # skip seqs only consisting of 0xffs
261                 seqset = list(set(seq.data))
262                 if len(seqset) == 1 and seqset[0] == 0xff:
263                         continue
264                 #print "Flashing", len(seq.data), "bytes at address", hex(seq.address)
265                 pkernWRITE(seq.address, len(seq.data), seq.data)
266                 sys.stdout.write(".")
267                 sys.stdout.flush()
268         print
269
270         duration = time.time() - starttime
271         print "Procedure complete, took", round(duration, 2), "seconds."
272
273         sendByte(0x97) # exit and restart
274         print "Program was started. Have fun!"
275
276
277 if __name__ == '__main__':
278         sys.exit(main())