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