make: check if git is available before calculate version
[pyfrprog.git] / frprog.py
1 #!/usr/bin/env python
2 """
3 This file realize a simple programmer, which communicates with our "pkernel"
4 firmware.
5 """
6 import sys, time
7 from SerialPort_linux import SerialPort, SerialPortException
8
9 # baudrate used for initialization
10 INIT_BAUDRATE = 9600
11 # baudrate used for communication with the internal bootloader after init
12 BOOTLOADER_BAUDRATE = 38400
13 # baudrate used for communication with the pkernel program that does the
14 # flashing eventually
15 KERNEL_BAUDRATE = 115200
16 # constant for output
17 SPLIT = 30
18
19 # contains the last received checksum from a READ, WRITE or CHECKSUM command
20 lastchecksum = 0
21
22 class FlashSequence(object):
23         def __init__(self, address, data):
24                 self.address = address
25                 self.data = data
26
27 def sendbyte(byte):
28         """
29         send a byte to the TTY-device
30         """
31         tty.write(chr(byte))
32
33 def sendword(word):
34         """
35         send a word to the TTY-device
36         """
37         sendbyte(word & 0xFF)
38         sendbyte((word >> 8) & 0xFF)
39
40 def senddword(dword):
41         """
42         send a dword to the TTY-device
43         """
44         sendbyte(dword & 0xFF)
45         sendbyte((dword >> 8) & 0xFF)
46         sendbyte((dword >> 16) & 0xFF)
47         sendbyte((dword >> 24) & 0xFF)
48
49 def recvbyte():
50         """
51         receive a byte from the TTY-device
52         """
53         return ord(tty.read())
54
55 def recvchecksum():
56         """
57         receive checksum from the bootROM firmware
58         """
59         global lastchecksum
60         lastchecksum = recvbyte()
61         lastchecksum |= (recvbyte() << 8)
62
63 def bootromread(address, size):
64         """
65         send a READ-command to the bootROM-firmware
66         """
67         # send READ command
68         sendbyte(0x01)
69         if (recvbyte() != 0xF1):
70                 raise Exception
71         sendbyte(0x02)
72         if (recvbyte() != 0x82):
73                 raise Exception
74         # tell desired address and size
75         senddword(address)
76         sendword(size)
77         # get binary stream of data
78         data = []
79         for _ in range(0, size):
80                 data.append(recvbyte())
81         # get checksum
82         recvchecksum()
83         return data
84
85 def bootromwrite(address, size, data):
86         """
87         send a WRITE-command to the bootROM-firmware
88         """
89         # send WRITE command
90         sendbyte(0x01)
91         if (recvbyte() != 0xF1):
92                 raise Exception
93         sendbyte(0x03)
94         if (recvbyte() != 0x83):
95                 raise Exception
96         # tell desired address and size
97         senddword(address)
98         sendword(size)
99         # write binary stream of data
100         for i in range(0, size):
101                 sendbyte(data[i])
102         # get checksum
103         recvchecksum()
104
105 def bootromcall(address):
106         """
107         send a CALL-command to the bootROM-firmware
108         """
109         # send CALL command
110         sendbyte(0x01)
111         if (recvbyte() != 0xF1):
112                 raise Exception
113         sendbyte(0x04)
114         if (recvbyte() != 0x84):
115                 raise Exception
116         # tell desired address
117         senddword(address)
118         # wait for return parameter - not needed here!
119         #return recvbyte()
120
121 # TODO: test this function!
122 def bootromchecksum():
123         """
124         send a CHECKSUM-command to the bootROM-firmware
125         """
126         # call CHECKSUM command
127         sendbyte(0x01)
128         if (recvbyte() != 0xF1):
129                 raise Exception
130         sendbyte(0x05)
131         if (recvbyte() != 0x84):
132                 raise Exception
133         # get checksum
134         recvchecksum()
135
136 def bootrombaudrate(baudrate):
137         """
138         send a BAUDRAME-command to the bootROM-firmware
139         """
140         # send BAUDRATE command
141         sendbyte(0x01)
142         if (recvbyte() != 0xF1):
143                 raise Exception
144         sendbyte(0x06)
145         if (recvbyte() != 0x86):
146                 raise Exception
147         # send desired baudrate
148         senddword(baudrate)
149
150 def pkernchiperase():
151         """
152         send a CHIPERASE-command to the pkernel-firmware
153         """
154         sendbyte(0x15)
155         if (recvbyte() != 0x45):
156                 raise Exception
157         # wait till completion...
158         if (recvbyte() != 0x23):
159                 raise Exception
160
161 def pkernerase(address, size):
162         """
163         send a ERASE-command to the pkernel-firmware
164         """
165         sendbyte(0x12)
166         if (recvbyte() != 0x11):
167                 raise Exception
168         senddword(address)
169         sendword(size)
170         if (recvbyte() != 0x18):
171                 raise Exception
172
173 def pkernwrite(address, size, data):
174         """
175         send a WRITE-command to the pkernel-firmware
176         """
177         # send WRITE command
178         sendbyte(0x13)
179         if (recvbyte() != 0x37):
180                 raise Exception
181         # tell desired address and size
182         senddword(address)
183         sendword(size)
184
185         # write binary stream of data
186         for i in range(0, size):
187                 sendbyte(data[i])
188
189         if (recvbyte() != 0x28):
190                 raise Exception
191
192 def readmhxfile(filename): # desired mhx filename
193         """
194         proceeds a MHX-File
195         """
196         filep = open(filename, "r")
197         retval = [] # returns a list of FlashSequence objects
198         linecount = 0
199         for line in filep:
200                 linecount += 1
201                 # get rid of newline characters
202                 line = line.strip()
203
204                 # we're only interested in S2 (data sequence with 3 address bytes)
205                 # records by now
206                 if line[0:2] == "S2":
207                         byte_count = int(line[2:4], 16)
208                         # just to get sure, check if byte count field is valid
209                         if (len(line)-4) != (byte_count*2):
210                                 print sys.argv[0] + ": Warning - inavlid byte count field in " + \
211                                         sys.argv[1] + ":" + str(linecount) + ", skipping line!"
212                                 continue
213
214                         # address and checksum bytes are not needed
215                         byte_count -= 4
216                         address = int(line[4:10], 16)
217                         datastr = line[10:10+byte_count*2]
218
219                         # convert data hex-byte-string to real byte data list
220                         data = []
221                         for i in range(0, len(datastr)/2):
222                                 data.append(int(datastr[2*i:2*i+2], 16))
223
224                         # add flash sequence to our list
225                         retval.append(FlashSequence(address, data))
226         filep.close()
227         return retval
228
229 def usage(execf):
230         """
231         print usage of frprog
232         """
233         print "Usage: " + execf + " <target mhx-file> [-d DEVICE]"
234
235 def main(argv=None):
236         """
237         main function of frprog
238         """
239         # check command line arguments
240         if argv is None:
241                 argv = sys.argv
242
243         if len(argv) == 2 and (argv[1] == "-v" or argv[1] == "--version"):
244                 print "Version: %VERSION%"
245                 return 0
246
247         if len(argv) != 2 and len(argv) != 4:
248                 usage(argv[0])
249                 return 1
250
251         # standard serial device to communicate with
252         device = "/dev/ttyUSB0"
253
254         # overrule standard device if provided with -d
255         if len(argv) == 4:
256                 if argv[2] == "-d":
257                         device = argv[3]
258                 else:
259                         usage(argv[0])
260                         return 1
261
262         # read in data from mhx-files before starting
263         try:
264                 try:
265                         bootloaderseqs = readmhxfile("pkernel/pkernel.mhx")
266                 except IOError as _:
267                         bootloaderseqs = readmhxfile("%PREFIX%/share/frprog/pkernel.mhx")
268                 pkernelseqs = readmhxfile(argv[1])
269         except IOError as error:
270                 print argv[0] + ": Error - couldn't open file " + error.filename + "!"
271                 return 1
272
273         print "Initializing serial port..."
274         global tty
275         tty = SerialPort(device, 100, INIT_BAUDRATE)
276
277         print "Please press RESET on your board..."
278
279         while True:
280                 tty.write('V')
281                 tty.flush()
282                 try:
283                         if tty.read() == 'F':
284                                 break
285                 except SerialPortException:
286                         # timeout happened, who cares ;-)
287                         pass
288
289         # save time at this point for evaluating the duration at the end
290         starttime = time.time()
291
292         print "OK, trying to set baudrate..."
293         # set baudrate
294         try:
295                 bootrombaudrate(BOOTLOADER_BAUDRATE)
296         except SerialPortException:
297                 print "timeout exception: try again ->"
298                 bootrombaudrate(BOOTLOADER_BAUDRATE)
299         # just to get sure that the bootloader is really running in new baudrate mode!
300         time.sleep(0.1)
301         del tty
302         tty = SerialPort(device, 100, BOOTLOADER_BAUDRATE)
303
304         sdots = SPLIT
305         print "Transfering pkernel program to IRAM",
306         # let the fun begin!
307         for seq in bootloaderseqs:
308                 if(seq.address <= 0x40000):
309                         addr = seq.address
310                 else:
311                         continue
312                 #print "RAMing", len(seq.data), "bytes at address", hex(addr)
313                 bootromwrite(addr, len(seq.data), seq.data)
314                 tty.flush()
315
316                 sdots = sdots - 1
317                 if sdots == 0:
318                         sys.stdout.write(".")
319                         sys.stdout.flush()
320                         sdots = SPLIT
321         print
322
323         # execute our pkernel finally and set pkernel conform baudrate
324         bootromcall(0x30000)
325         time.sleep(0.1) # just to get sure that the pkernel is really running!
326         del tty
327         tty = SerialPort(device, None, KERNEL_BAUDRATE)
328
329         print "Performing ChipErase..."
330         pkernchiperase()
331
332         sdots = SPLIT
333         print "Flashing",
334         for seq in pkernelseqs:
335                 # skip seqs only consisting of 0xffs
336                 seqset = list(set(seq.data))
337                 if len(seqset) == 1 and seqset[0] == 0xff:
338                         continue
339                 #print "Flashing", len(seq.data), "bytes at address", hex(seq.address)
340                 pkernwrite(seq.address, len(seq.data), seq.data)
341                 tty.flush()
342
343                 sdots = sdots - 1
344                 if sdots == 0:
345                         sys.stdout.write(".")
346                         sys.stdout.flush()
347                         sdots = SPLIT
348         print
349
350         duration = time.time() - starttime
351         print "Procedure complete, took", round(duration, 2), "seconds."
352
353         sendbyte(0x97) # exit and restart
354         print "Program was started. Have fun!"
355
356
357 if __name__ == '__main__':
358         sys.exit(main())