555141f868e9fb5dd5161bb91bf0d856fdc622b7
[aversive.git] / projects / microb2010 / microb_cmd / microbcmd.py
1 #! /usr/bin/env python
2
3 import os,sys,termios,atexit
4 import serial
5 from select import select
6 import cmd
7 #import pylab
8 from  matplotlib import pylab
9 from math import *
10
11 import struct
12 import numpy
13 import shlex
14 import time
15 import math
16 import warnings
17 warnings.filterwarnings("ignore","tempnam",RuntimeWarning, __name__)
18
19 import logging
20 log = logging.getLogger("MicrobShell")
21 _handler = logging.StreamHandler()
22 _handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
23 log.addHandler(_handler)
24 log.setLevel(1)
25
26 MICROB_PATH=os.path.dirname(sys.argv[0])
27
28 SPM_PAGE_SIZE = 256
29 METADATA_ADDR = 256
30
31 def crc_ccitt_update (crc, data):
32     """crc argument is the previous value of 16 bits crc (the initial
33     value is 0xffff). 'data' is the 8 bits value added to crc. The
34     function returns the new crc value."""
35
36     data ^= (crc & 0xff)
37     data ^= (data << 4)
38     data &= 0xff
39
40     ret = (data << 8) & 0xffff
41     ret |= ((crc >> 8) & 0xff)
42     ret ^= ((data >> 4) & 0xff)
43     ret ^= ((data << 3) & 0xffff)
44     return ret
45
46 def do_crc(buf):
47     i = 0
48     crc = 0xffff
49     sum = 0
50     while i < len(buf):
51         crc = crc_ccitt_update(crc, ord(buf[i]))
52         sum +=  ord(buf[i])
53         i += 1
54     return (crc << 16) + (sum & 0xffff)
55
56 def prog_page(ser, addr, buf):
57     """program a page from buf at addr"""
58
59     # switch in program mode
60     ser.flushInput()
61     ser.write('p')
62
63     # send address
64     s = ser.readline()
65     if not s.endswith("addr?\r\n"):
66         print "failed (don't match addr)"
67         return -1
68     ser.write("%x\n"%addr)
69     s = ser.readline()
70     if not s.startswith("ok"):
71         print "failed"
72         return -1
73
74     # fill page with buf data
75     page = [ '\xff' ] * SPM_PAGE_SIZE
76     i = 0
77     while i < SPM_PAGE_SIZE and i < len(buf):
78         page[i] = buf[i]
79         i += 1
80
81     # send data
82     i = 0
83     while i < SPM_PAGE_SIZE:
84         c = page[i]
85         ser.write(c)
86         i += 1
87
88     sys.stdout.write(".")
89     sys.stdout.flush()
90
91     # compare crc
92     avr_crc = int(ser.readline()[0:8], 16)
93
94     crc = do_crc(page)
95     if crc != avr_crc:
96         print "failed: bad crc %x %x"%(crc, avr_crc)
97         ser.write('n')
98         return -1
99
100     ser.write('y')
101     s = ser.readline()
102     if not s.startswith("OK"):
103         print "failed"
104         return -1
105     return 0
106
107 def prog_metadata(ser, addr, buf):
108     length = len(buf)
109     crc = do_crc(buf)
110     page = struct.pack("<LL", length, crc)
111     filename = os.path.join(MICROB_PATH, "binaries/%x_%x.bin"%(length, crc))
112     print "saving in %s"%(filename)
113     f = open(filename, "w")
114     f.write(buf)
115     f.close()
116     return prog_page(ser, addr, page)
117
118 def get_same_bin_file(ser):
119     # hack because dump32 does not work
120     l1 = read32(ser, METADATA_ADDR) & 0xFFFF
121     l2 = read32(ser, METADATA_ADDR + 2) & 0xFFFF
122     c1 = read32(ser, METADATA_ADDR + 4) & 0xFFFF
123     c2 = read32(ser, METADATA_ADDR + 6) & 0xFFFF
124     l = l1 + l2 << 16
125     c = c1 + c2 << 16
126     filename = os.path.join(MICROB_PATH,
127                             "binaries/%x_%x.bin"%(l, c))
128     print filename
129     try:
130         f = open(filename, "r")
131     except:
132         return None
133     buf = f.read()
134     f.close()
135     print "found old bin matching <%s>"%(filename)
136     return buf
137
138 def read32(ser, addr):
139     """read a 32 bits value at addr"""
140
141     ser.write('\n')
142     ser.write('\n')
143     ser.write('\n')
144     time.sleep(0.2)
145     ser.flushInput()
146
147     # switch in program mode
148     ser.write('d')
149
150     # send address
151     time.sleep(0.1)
152
153     s = ser.readline()
154     print repr(s)
155     if not s.endswith("addr?\r\n"):
156         print "failed (don't match addr)"
157         return -1
158     print addr
159     ser.write("%x\n"%addr)
160     s = ser.readline()
161     print repr(s)
162     return int(s, 16)
163
164 def check_crc(ser, buf, offset, size):
165     """Process the crc of buf, ask for a crc of the flash, and check
166     that value is correct"""
167     if size <= 0:
168         return 0
169
170     # go in CRC mode
171     ser.flushInput()
172     ser.write('c')
173
174     # send addr
175     s = ser.readline()
176     if not s.endswith("addr?\r\n"):
177         print "failed <%s>"%s
178         return -1
179     ser.write("%x\n"%offset)
180
181     # send size
182     s = ser.readline()
183     if not s.startswith("size?"):
184         print "failed"
185         return -1
186     ser.write("%x\n"%size)
187
188     # compare CRC
189     crc = do_crc(buf[offset:offset+size])
190     avr_crc = int(ser.readline()[0:8], 16)
191     if crc != avr_crc:
192         return -1
193     return 0
194
195 class SerialLogger:
196     def __init__(self, ser, filein, fileout=None):
197         self.ser = ser
198         self.filein = filein
199         self.fin = open(filein, "a", 0)
200         if fileout:
201             self.fileout = fileout
202             self.fout = open(fileout, "a", 0)
203         else:
204             self.fileout = filein
205             self.fout = self.fin
206     def fileno(self):
207         return self.ser.fileno()
208     def read(self, *args):
209         res = self.ser.read(*args)
210         self.fin.write(res)
211         return res
212     def write(self, s):
213         self.fout.write(s)
214         self.ser.write(s)
215
216 class Interp(cmd.Cmd):
217     prompt = "Microb> "
218     def __init__(self, tty, baudrate=57600):
219         cmd.Cmd.__init__(self)
220         self.ser = serial.Serial(tty,baudrate=baudrate)
221         self.escape  = "\x01" # C-a
222         self.quitraw = "\x02" # C-b
223         self.serial_logging = False
224         self.default_in_log_file = "/tmp/microb.in.log"
225         self.default_out_log_file = "/tmp/microb.out.log"
226
227     def do_quit(self, args):
228         return True
229
230     def do_log(self, args):
231         """Activate serial logs.
232         log <filename>           logs input and output to <filename>
233         log <filein> <fileout>   logs input to <filein> and output to <fileout>
234         log                      logs to /tmp/microb.log or the last used file"""
235
236         if self.serial_logging:
237             log.error("Already logging to %s and %s" % (self.ser.filein,
238                                                         self.ser.fileout))
239         else:
240             self.serial_logging = True
241             files = [os.path.expanduser(x) for x in args.split()]
242             if len(files) == 0:
243                 files = [self.default_in_log_file, self.default_out_log_file]
244             elif len(files) == 1:
245                 self.default_in_log_file = files[0]
246                 self.default_out_log_file = None
247             elif len(files) == 2:
248                 self.default_in_log_file = files[0]
249                 self.default_out_log_file = files[1]
250             else:
251                 print "Can't parse arguments"
252
253             self.ser = SerialLogger(self.ser, *files)
254             log.info("Starting serial logging to %s and %s" % (self.ser.filein,
255                                                                self.ser.fileout))
256
257
258     def do_unlog(self, args):
259         if self.serial_logging:
260             log.info("Stopping serial logging to %s and %s" % (self.ser.filein,
261                                                                self.ser.fileout))
262             self.ser = self.ser.ser
263             self.serial_logging = False
264         else:
265             log.error("No log to stop")
266
267
268     def do_raw(self, args):
269         "Switch to RAW mode"
270         stdin = os.open("/dev/stdin",os.O_RDONLY)
271         stdout = os.open("/dev/stdout",os.O_WRONLY)
272
273         stdin_termios = termios.tcgetattr(stdin)
274         raw_termios = stdin_termios[:]
275
276         try:
277             log.info("Switching to RAW mode")
278
279             # iflag
280             raw_termios[0] &= ~(termios.IGNBRK | termios.BRKINT |
281                                 termios.PARMRK | termios.ISTRIP |
282                                 termios.INLCR | termios.IGNCR |
283                                 termios.ICRNL | termios.IXON)
284             # oflag
285             raw_termios[1] &= ~termios.OPOST;
286             # cflag
287             raw_termios[2] &= ~(termios.CSIZE | termios.PARENB);
288             raw_termios[2] |= termios.CS8;
289             # lflag
290             raw_termios[3] &= ~(termios.ECHO | termios.ECHONL |
291                                 termios.ICANON | termios.ISIG |
292                                 termios.IEXTEN);
293
294             termios.tcsetattr(stdin, termios.TCSADRAIN, raw_termios)
295
296             mode = "normal"
297             while True:
298                 ins,outs,errs=select([stdin,self.ser],[],[])
299                 for x in ins:
300                     if x == stdin:
301                         c = os.read(stdin,1)
302                         if mode  == "escape":
303                             mode =="normal"
304                             if c == self.escape:
305                                 self.ser.write(self.escape)
306                             elif c == self.quitraw:
307                                 return
308                             else:
309                                 self.ser.write(self.escape)
310                                 self.ser.write(c)
311                         else:
312                             if c == self.escape:
313                                 mode = "escape"
314                             else:
315                                 self.ser.write(c)
316                     elif x == self.ser:
317                         os.write(stdout,self.ser.read())
318         finally:
319             termios.tcsetattr(stdin, termios.TCSADRAIN, stdin_termios)
320             log.info("Back to normal mode")
321
322     def bootloader(self, filename, boardnum):
323         self.ser.write("\ 3")
324         time.sleep(0.4)
325         self.ser.write("bootloader\n")
326         time.sleep(0.4)
327         self.ser.write("\n")
328
329         print "start programming"
330         self.ser.flushInput()
331         f = open(filename)
332         buf = f.read()
333         f.close()
334
335         addr = 0
336
337         #old_buf = get_same_bin_file(self.ser)
338         old_buf = None
339
340         while addr < len(buf):
341             if addr > METADATA_ADDR and old_buf != None and \
342                     old_buf[addr:addr+SPM_PAGE_SIZE] == buf[addr:addr+SPM_PAGE_SIZE]:
343                 sys.stdout.write("-")
344                 sys.stdout.flush()
345                 addr += SPM_PAGE_SIZE
346                 continue
347             time.sleep(0.1)
348             if check_crc(self.ser, buf, addr, SPM_PAGE_SIZE) == 0:
349                 sys.stdout.write("*")
350                 sys.stdout.flush()
351             elif prog_page(self.ser, addr,
352                          buf[addr:addr+SPM_PAGE_SIZE]) != 0:
353                 return
354             addr += SPM_PAGE_SIZE
355         if check_crc(self.ser, buf, 0, len(buf)):
356             print "crc failed"
357             return
358         print
359         if prog_metadata(self.ser, METADATA_ADDR, buf) != 0:
360             print "metadata failed"
361             return
362         print "Done."
363         self.ser.write("x")
364         self.do_raw("")
365
366     def do_bootloader(self, args):
367         self.bootloader(args, 0)
368
369     def do_mainboard(self, args):
370         filename = os.path.join(MICROB_PATH, "../mainboard/main.bin")
371         self.bootloader(filename, 1)
372
373     def do_cobboard(self, args):
374         filename = os.path.join(MICROB_PATH, "../cobboard/main.bin")
375         self.bootloader(filename, 2)
376
377     def do_ballboard(self, args):
378         filename = os.path.join(MICROB_PATH, "../ballboard/main.bin")
379         self.bootloader(filename, 3)
380
381     def do_toto(self, args):
382         for i in range(10):
383             time.sleep(1)
384             self.ser.write("pwm s3(3C) 200\n")
385             time.sleep(1)
386             self.ser.write("pwm s3(3C) 250\n")
387
388 if __name__ == "__main__":
389     try:
390         import readline,atexit
391     except ImportError:
392         pass
393     else:
394         histfile = os.path.join(os.environ["HOME"], ".microb_history")
395         atexit.register(readline.write_history_file, histfile)
396         try:
397             readline.read_history_file(histfile)
398         except IOError:
399             pass
400
401     device = "/dev/ttyS0"
402     if len(sys.argv) > 1:
403         device = sys.argv[1]
404     interp = Interp(device)
405     while 1:
406         try:
407             interp.cmdloop()
408         except KeyboardInterrupt:
409             print
410         except Exception,e:
411             l = str(e).strip()
412             if l:
413                 log.exception("%s" % l.splitlines()[-1])
414             continue
415         break