latest updates including GoodFETARM.py "DEPRECATED" message
[goodfet] / client / GoodFETARM.py
1 #!/usr/bin/env python
2 # GoodFET ARM Client Library
3
4 #
5 # Good luck with alpha / beta code.
6 # Contributions and bug reports welcome.
7 #
8
9
10
11 raise Exception("DEPRECATED.  USE GoodFETARM7")
12
13 import sys, binascii, struct, time
14 import atlasutils.smartprint as asp
15 from GoodFET import GoodFET
16 from intelhex import IntelHex
17
18 platforms = {
19     "at91sam7": {0:(0x100000, "Flash before remap, SRAM after remap"),
20                  0x100000: (0x100000, "Internal Flash"),
21                  0x200000: (0x100000, "Internal SRAM"),
22                  },
23     }
24                 
25
26 #Global Commands
27 READ  = 0x00
28 WRITE = 0x01
29 PEEK  = 0x02
30 POKE  = 0x03
31 SETUP = 0x10
32 START = 0x20
33 STOP  = 0x21
34 CALL  = 0x30
35 EXEC  = 0x31
36 NOK   = 0x7E
37 OK    = 0x7F
38
39 # ARM7TDMI JTAG commands
40 GET_DEBUG_CTRL      = 0x80
41 SET_DEBUG_CTRL      = 0x81
42 GET_PC              = 0x82
43 SET_PC              = 0x83
44 GET_CHIP_ID         = 0x84
45 GET_DEBUG_STATE     = 0x85
46 GET_WATCHPOINT      = 0x86
47 SET_WATCHPOINT      = 0x87
48 GET_REGISTER        = 0x88
49 SET_REGISTER        = 0x89
50 GET_REGISTERS       = 0x8a
51 SET_REGISTERS       = 0x8b
52 HALTCPU             = 0x8c
53 RESUMECPU           = 0x8d
54 DEBUG_INSTR         = 0x8e      #
55 STEP_INSTR          = 0x8f      #
56 STEP_REPLACE        = 0x90      #
57 PROGRAM_FLASH       = 0x95
58 LOCKCHIP            = 0x96      # ??
59 CHIP_ERASE          = 0x97      # can do?
60 # Really ARM specific stuff
61 GET_CPSR            = 0x98
62 SET_CPSR            = 0x99
63 GET_SPSR            = 0x9a
64 SET_SPSR            = 0x9b
65 SET_MODE_THUMB      = 0x9c
66 SET_MODE_ARM        = 0x9d
67 SET_IR              = 0x9e
68 WAIT_DBG            = 0x9f
69 SHIFT_DR            = 0xa0
70 SETWATCH0           = 0xa1
71 SETWATCH1           = 0xa2
72 CHAIN0              = 0xa3
73
74
75 MSB         = 0
76 LSB         = 1
77 NOEND       = 2
78 NORETIDLE   = 4
79
80 PM_usr = 0b10000
81 PM_fiq = 0b10001
82 PM_irq = 0b10010
83 PM_svc = 0b10011
84 PM_abt = 0b10111
85 PM_und = 0b11011
86 PM_sys = 0b11111
87 proc_modes = {
88     PM_usr: ("User Processor Mode", "usr", "Normal program execution mode"),
89     PM_fiq: ("FIQ Processor Mode", "fiq", "Supports a high-speed data transfer or channel process"),
90     PM_irq: ("IRQ Processor Mode", "irq", "Used for general-purpose interrupt handling"),
91     PM_svc: ("Supervisor Processor Mode", "svc", "A protected mode for the operating system"),
92     PM_irq: ("Abort Processor Mode", "irq", "Implements virtual memory and/or memory protection"),
93     PM_und: ("Undefined Processor Mode", "und", "Supports software emulation of hardware coprocessor"),
94     PM_sys: ("System Processor Mode", "sys", "Runs privileged operating system tasks (ARMv4 and above)"),
95 }
96
97 PSR_bits = [ 
98     None,
99     None,
100     None,
101     None,
102     None,
103     "Thumb",
104     "nFIQ_int",
105     "nIRQ_int",
106     "nImprDataAbort_int",
107     "BIGendian",
108     None,
109     None,
110     None,
111     None,
112     None,
113     None,
114     "GE_0",
115     "GE_1",
116     "GE_2",
117     "GE_3",
118     None,
119     None,
120     None,
121     None,
122     "Jazelle",
123     None,
124     None,
125     "Q (DSP-overflow)",
126     "oVerflow",
127     "Carry",
128     "Zero",
129     "Neg",
130     ]
131
132
133
134 ARM_INSTR_NOP =             0xe1a00000L
135 ARM_INSTR_BX_R0 =           0xe12fff10L
136 ARM_INSTR_STR_Rx_r14 =      0xe58f0000L # from atmel docs
137 ARM_READ_REG =              ARM_INSTR_STR_Rx_r14
138 ARM_INSTR_LDR_Rx_r14 =      0xe59f0000L # from atmel docs
139 ARM_WRITE_REG =             ARM_INSTR_LDR_Rx_r14
140 ARM_INSTR_LDR_R1_r0_4 =     0xe4901004L
141 ARM_READ_MEM =              ARM_INSTR_LDR_R1_r0_4
142 ARM_INSTR_STR_R1_r0_4 =     0xe4801004L
143 ARM_WRITE_MEM =             ARM_INSTR_STR_R1_r0_4
144 ARM_INSTR_MRS_R0_CPSR =     0xe10f0000L
145 ARM_INSTR_MSR_cpsr_cxsf_R0 =0xe12ff000L
146 ARM_INSTR_STMIA_R14_r0_rx = 0xE88E0000L      # add up to 65k to indicate which registers...
147 ARM_STORE_MULTIPLE =        ARM_INSTR_STMIA_R14_r0_rx
148 ARM_INSTR_SKANKREGS =       0xE88F7fffL
149 ARM_INSTR_CLOBBEREGS =      0xE89F7fffL
150
151 ARM_INSTR_B_PC =            0xea000000L
152 ARM_INSTR_BX_PC =           0xe1200010L      # need to set r0 to the desired address
153 THUMB_INSTR_STR_R0_r0 =     0x60006000L
154 THUMB_INSTR_MOV_R0_PC =     0x46b846b8L
155 THUMB_INSTR_BX_PC =         0x47784778L
156 THUMB_INSTR_NOP =           0x1c001c00L
157 ARM_REG_PC =                15
158
159 ARM7TDMI_IR_EXTEST =            0x0
160 ARM7TDMI_IR_SCAN_N =            0x2
161 ARM7TDMI_IR_SAMPLE =            0x3
162 ARM7TDMI_IR_RESTART =           0x4
163 ARM7TDMI_IR_CLAMP =             0x5
164 ARM7TDMI_IR_HIGHZ =             0x7
165 ARM7TDMI_IR_CLAMPZ =            0x9
166 ARM7TDMI_IR_INTEST =            0xC
167 ARM7TDMI_IR_IDCODE =            0xE
168 ARM7TDMI_IR_BYPASS =            0xF
169
170
171 def PSRdecode(psrval):
172     output = [ "(%s mode)"%proc_modes[psrval&0x1f][1] ]
173     for x in xrange(5,32):
174         if psrval & (1<<x):
175             output.append(PSR_bits[x])
176     return " ".join(output)
177    
178 fmt = [None, "B", "<H", None, "<L", None, None, None, "<Q"]
179 def chop(val,byts):
180     s = struct.pack(fmt[byts], val)
181     return [ord(b) for b in s ]
182         
183 class GoodFETARM(GoodFET):
184     """A GoodFET variant for use with ARM7TDMI microprocessor."""
185     def ARMhaltcpu(self):
186         """Halt the CPU."""
187         self.writecmd(0x13,HALTCPU,0,self.data)
188         print "CPSR: (%s) %s"%(self.ARMget_regCPSRstr())
189     halt=ARMhaltcpu
190     def ARMreleasecpu(self):
191         """Resume the CPU."""
192         self.writecmd(0x13,RESUMECPU,0,self.data)
193     def ARMsetModeArm(self, restart=0):
194         self.writecmd(0x13,SET_MODE_ARM,0,[restart])
195     def ARMsetModeThumb(self, restart=0):
196         self.writecmd(0x13,SET_MODE_THUMB,0,[restart])
197     def ARMtest(self):
198         #self.ARMreleasecpu()
199         #self.ARMhaltcpu()
200         print "Status: %s" % self.ARMstatusstr()
201         
202         #Grab ident three times, should be equal.
203         ident1=self.ARMident()
204         ident2=self.ARMident()
205         ident3=self.ARMident()
206         if(ident1!=ident2 or ident2!=ident3):
207             print "Error, repeated ident attempts unequal."
208             print "%04x, %04x, %04x" % (ident1, ident2, ident3)
209         
210         #Set and Check Registers
211         regs = [1024+x for x in range(0,15)]
212         regr = []
213         for x in range(len(regs)):
214             self.ARMset_register(x, regs[x])
215
216         for x in range(len(regs)):
217             regr.append(self.ARMget_register(x))
218         
219         for x in range(len(regs)):
220             if regs[x] != regr[x]:
221                 print "Error, R%d fail: %x != %x"%(x,regs[x],regr[x])
222
223         return
224
225
226
227
228         #Single step, printing PC.
229         print "Tracing execution at startup."
230         for i in range(15):
231             pc=self.ARMgetPC()
232             byte=self.ARMpeekcodebyte(i)
233             #print "PC=%04x, %02x" % (pc, byte)
234             self.ARMstep_instr()
235         
236         print "Verifying that debugging a NOP doesn't affect the PC."
237         for i in range(1,15):
238             pc=self.ARMgetPC()
239             self.ARMdebuginstr([NOP])
240             if(pc!=self.ARMgetPC()):
241                 print "ERROR: PC changed during ARMdebuginstr([NOP])!"
242         
243         print "Checking pokes to XRAM."
244         for i in range(0xf000,0xf020):
245             self.ARMpokedatabyte(i,0xde)
246             if(self.ARMpeekdatabyte(i)!=0xde):
247                 print "Error in DATA at 0x%04x" % i
248         
249         #print "Status: %s." % self.ARMstatusstr()
250         #Exit debugger
251         self.stop()
252         print "Done."
253
254     def setup(self):
255         """Move the FET into the JTAG ARM application."""
256         #print "Initializing ARM."
257         self.writecmd(0x13,SETUP,0,self.data)
258     def ARMget_dbgstate(self):
259         """Read the config register of an ARM."""
260         self.writecmd(0x13,GET_DEBUG_STATE,0,self.data)
261         retval = struct.unpack("<L", self.data[:4])[0]
262         return retval
263     def ARMget_dbgctrl(self):
264         """Read the config register of an ARM."""
265         self.writecmd(0x13,GET_DEBUG_CTRL,0,self.data)
266         retval = struct.unpack("B", self.data)[0]
267         return retval
268     def ARMset_dbgctrl(self,config):
269         """Write the config register of an ARM."""
270         self.writecmd(0x13,SET_DEBUG_CTRL,1,[config&7])
271     def ARMlockchip(self):
272         """Set the flash lock bit in info mem.
273         Chip-Specific.  Not implemented"""
274         #self.writecmd(0x13, LOCKCHIP, 0, [])
275         raise Exception("Unimplemented: lockchip.  This is chip specific and must be implemented for each chip.")
276     
277
278     def ARMidentstr(self):
279         ident=self.ARMident()
280         ver     = ident >> 28
281         partno  = (ident >> 12) & 0x10
282         mfgid   = ident & 0xfff
283         return "mfg: %x\npartno: %x\nver: %x\n(%x)" % (ver, partno, mfgid, ident); 
284     def ARMident(self):
285         """Get an ARM's ID."""
286         self.writecmd(0x13,GET_CHIP_ID,0,[])
287         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
288         return retval
289     def ARMsetPC(self, val):
290         """Set an ARM's PC.  Note: real PC gets all wonky in debug mode, this changes the "saved" PC which is used when exiting debug mode"""
291         self.writecmd(0x13,SET_PC,0,chop(val,4))
292     def ARMgetPC(self):
293         """Get an ARM's PC. Note: real PC gets all wonky in debug mode, this is the "saved" PC"""
294         self.writecmd(0x13,GET_PC,0,[])
295         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
296         return retval
297     def ARMget_register(self, reg):
298         """Get an ARM's Register"""
299         self.writecmd(0x13,GET_REGISTER,1,[reg&0xff])
300         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
301         return retval
302     def ARMset_register(self, reg, val):
303         """Get an ARM's Register"""
304         self.writecmd(0x13,SET_REGISTER,8,[val&0xff, (val>>8)&0xff, (val>>16)&0xff, val>>24, reg,0,0,0])
305         #self.writecmd(0x13,SET_REGISTER,8,[reg,0,0,0, (val>>16)&0xff, val>>24, val&0xff, (val>>8)&0xff])
306         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
307         return retval
308     def ARMget_registers(self):
309         """Get ARM Registers"""
310         regs = [ self.ARMget_register(x) for x in range(15) ]
311         regs.append(self.ARMgetPC())            # make sure we snag the "static" version of PC
312         return regs
313     def ARMset_registers(self, regs, mask):
314         """Set ARM Registers"""
315         for x in xrange(15):
316           if (1<<x) & mask:
317             self.ARMset_register(x,regs.pop())
318         if (1<<15) & mask:                      # make sure we set the "static" version of PC or changes will be lost
319           self.ARMsetPC(regs.pop())
320     def ARMget_regCPSRstr(self):
321         psr = self.ARMget_regCPSR()
322         return hex(psr), PSRdecode(psr)
323     def ARMget_regCPSR(self):
324         """Get an ARM's Register"""
325         self.writecmd(0x13,GET_CPSR,0,[])
326         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
327         return retval
328     def ARMset_regCPSR(self, val):
329         """Get an ARM's Register"""
330         self.writecmd(0x13,SET_CPSR,4,[val&0xff, (val>>8)&0xff, (val>>16)&0xff, val>>24])
331     def ARMcmd(self,phrase):
332         self.writecmd(0x13,READ,len(phrase),phrase)
333         val=ord(self.data[0])
334         print "Got %02x" % val
335         return val
336     def ARMdebuginstr(self,instr,bkpt):
337         if type (instr) == int or type(instr) == long:
338             instr = struct.pack("<L", instr)
339         instr = [int("0x%x"%ord(x),16) for x in instr]
340         instr.extend([bkpt])
341         self.writecmd(0x13,DEBUG_INSTR,len(instr),instr)
342         return (self.data)
343     def ARM_nop(self, bkpt):
344         return self.ARMdebuginstr(ARM_INSTR_NOP, bkpt)
345     def ARMset_IR(self, IR, noretidle=0):
346         self.writecmd(0x13,SET_IR,2, [IR, LSB|noretidle])
347         return self.data
348     def ARMshiftDR(self, data, bits, flags):
349         self.writecmd(0x13,SHIFT_DR,8,[bits&0xff, flags&0xff, 0, 0, data&0xff,(data>>8)&0xff,(data>>16)&0xff,(data>>24)&0xff])
350         return self.data
351     def ARMwaitDBG(self, timeout=0xff):
352         self.writecmd(0x13,WAIT_DBG,2,[timeout&0xf,timeout>>8])
353         return self.data
354     def ARMrestart(self):
355         #self.ARMset_IR(ARM7TDMI_IR_BYPASS)
356         self.ARMset_IR(ARM7TDMI_IR_RESTART)
357     def ARMset_watchpoint0(self, addr, addrmask, data, datamask, ctrl, ctrlmask):
358         self.data = []
359         self.data.extend(chop(addr,4))
360         self.data.extend(chop(addrmask,4))
361         self.data.extend(chop(data,4))
362         self.data.extend(chop(datamask,4))
363         self.data.extend(chop(ctrl,4))
364         self.data.extend(chop(ctrlmask,4))
365         self.writecmd(0x13,SETWATCH0,24,self.data)
366         return self.data
367     def ARMset_watchpoint1(self, addr, addrmask, data, datamask, ctrl, ctrlmask):
368         self.data = []
369         self.data.extend(chop(addr,4))
370         self.data.extend(chop(addrmask,4))
371         self.data.extend(chop(data,4))
372         self.data.extend(chop(datamask,4))
373         self.data.extend(chop(ctrl,4))
374         self.data.extend(chop(ctrlmask,4))
375         self.writecmd(0x13,SETWATCH1,24,self.data)
376         return self.data
377     def ARMreadMem(self, adr, wrdcount):
378         retval = [] 
379         r0 = self.ARMget_register(0);        # store R0 and R1
380         r1 = self.ARMget_register(1);
381         #print >>sys.stderr,("CPSR:\t%x"%self.ARMget_regCPSR())
382         for word in range(adr, adr+(wrdcount*4), 4):
383             sys.stdin.readline()
384             self.ARMset_register(0, word);        # write address into R0
385             #time.sleep(1)
386             self.ARMset_register(1, 0xdeadbeef)
387             #time.sleep(1)
388             self.ARM_nop(0)
389             #time.sleep(1)
390             self.ARM_nop(1)
391             #time.sleep(1)
392             self.ARMdebuginstr(ARM_READ_MEM, 0); # push LDR R1, [R0], #4 into instruction pipeline  (autoincrements for consecutive reads)
393             #time.sleep(1)
394             self.ARM_nop(0)
395             #time.sleep(1)
396             self.ARMrestart()
397             #time.sleep(1)
398             self.ARMwaitDBG()
399             #time.sleep(1)
400             print hex(self.ARMget_register(1))
401
402
403             # FIXME: this may end up changing te current debug-state.  should we compare to current_dbgstate?
404             #print repr(self.data[4])
405             if (len(self.data)>4 and self.data[4] == '\x00'):
406               print >>sys.stderr,("FAILED TO READ MEMORY/RE-ENTER DEBUG MODE")
407               raise Exception("FAILED TO READ MEMORY/RE-ENTER DEBUG MODE")
408               return (-1);
409             else:
410               retval.append( self.ARMget_register(1) )  # read memory value from R1 register
411               #print >>sys.stderr,("CPSR: %x\t\tR0: %x\t\tR1: %x"%(self.ARMget_regCPSR(),self.ARMget_register(0),self.ARMget_register(1)))
412         self.ARMset_register(1, r1);       # restore R0 and R1 
413         self.ARMset_register(0, r0);
414         return retval
415
416     def ARMwriteMem(self, adr, wordarray):
417         r0 = self.ARMget_register(0);        # store R0 and R1
418         r1 = self.ARMget_register(1);
419         #print >>sys.stderr,("CPSR:\t%x"%self.ARMget_regCPSR())
420         for word in xrange(adr, adr+len(string), 4):
421             self.ARMset_register(0, word);        # write address into R0
422             self.ARM_nop(0)
423             self.ARM_nop(1)
424             self.ARMdebuginstr(ARM_WRITE_MEM, 0); # push STR R1, [R0], #4 into instruction pipeline  (autoincrements for consecutive writes)
425             self.ARM_nop(0)
426             self.ARMrestart()
427             self.ARMwaitDBG()
428             print hex(self.ARMget_register(1))
429
430
431             # FIXME: this may end up changing te current debug-state.  should we compare to current_dbgstate?
432             #print repr(self.data[4])
433             if (len(self.data)>4 and self.data[4] == '\x00'):
434               print >>sys.stderr,("FAILED TO READ MEMORY/RE-ENTER DEBUG MODE")
435               raise Exception("FAILED TO READ MEMORY/RE-ENTER DEBUG MODE")
436               return (-1);
437             else:
438               retval.append( self.ARMget_register(1) )  # read memory value from R1 register
439               #print >>sys.stderr,("CPSR: %x\t\tR0: %x\t\tR1: %x"%(self.ARMget_regCPSR(),self.ARMget_register(0),self.ARMget_register(1)))
440         self.ARMset_register(1, r1);       # restore R0 and R1 
441         self.ARMset_register(0, r0);
442         return retval
443
444     def ARMpeekcodewords(self,adr,words):
445         """Read the contents of code memory at an address."""
446         self.data=[adr&0xff, (adr>>8)&0xff, (adr>>16)&0xff, (adr>>24)&0xff, words&0xff, (words>>8)&0xff, (words>>16)&0xff, (words>>24)&0xff ]
447         self.writecmd(0x13,READ_CODE_MEMORY,8,self.data)
448         retval = []
449         retval.append(self.serialport.read(words*4))
450         #retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
451         return "".join(retval)
452     def ARMpeekdatabyte(self,adr):
453         """Read the contents of data memory at an address."""
454         self.data=[ adr&0xff, (adr>>8)&0xff, (adr>>16)&0xff, (adr>>24)&0xff ]
455         self.writecmd(0x13, PEEK, 4, self.data)
456         #retval.append(self.serialport.read(words*4))
457         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
458         return retval
459     def ARMpokedatabyte(self,adr,val):
460         """Write a byte to data memory."""
461         self.data=[adr&0xff, (adr>>8)&0xff, (adr>>16)&0xff, (adr>>24)&0xff, val&0xff, (val>>8)&0xff, (val>>16)&0xff, (val>>24)&0xff ]
462         self.writecmd(0x13, POKE, 8, self.data)
463         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
464         return retval
465     #def ARMchiperase(self):
466     #    """Erase all of the target's memory."""
467     #    self.writecmd(0x13,CHIP_ERASE,0,[])
468     def ARMstatus(self):
469         """Check the status."""
470         self.writecmd(0x13,GET_DEBUG_STATE,0,[])
471         return ord(self.data[0])
472     ARMstatusbits={
473                   0x10 : "TBIT",
474                   0x08 : "cgenL",
475                   0x04 : "Interrupts Enabled (or not?)",
476                   0x02 : "DBGRQ",
477                   0x01 : "DGBACK"
478                   }
479     ARMctrlbits={
480                   0x04 : "disable interrupts",
481                   0x02 : "force dbgrq",
482                   0x01 : "force dbgack"
483                   }
484                   
485     def ARMstatusstr(self):
486         """Check the status as a string."""
487         status=self.ARMstatus()
488         str=""
489         i=1
490         while i<0x100:
491             if(status&i):
492                 str="%s %s" %(self.ARMstatusbits[i],str)
493             i*=2
494         return str
495     def ARMchain0(self, address, bits, data):
496         bulk = chop(address,4)
497         bulk.extend(chop(bits,8))
498         bulk.extend(chop(data,4))
499         print (repr(bulk))
500         self.writecmd(0x13,CHAIN0,16,bulk)
501         d1,b1,a1 = struct.unpack("<LQL",self.data)
502         return (a1,b1,d1)
503     def start(self):
504         """Start debugging."""
505         self.writecmd(0x13,START,0,self.data)
506         ident=self.ARMidentstr()
507         print "Target identifies as %s." % ident
508         print "Debug Status: %s." % self.ARMstatusstr()
509         #print "System State: %x." % self.ARMget_regCPSRstr()
510         #self.ARMreleasecpu()
511         #self.ARMhaltcpu()
512         
513     def stop(self):
514         """Stop debugging."""
515         self.writecmd(0x13,STOP,0,self.data)
516     #def ARMstep_instr(self):
517     #    """Step one instruction."""
518     #    self.writecmd(0x13,STEP_INSTR,0,self.data)
519     #def ARMflashpage(self,adr):
520     #    """Flash 2kB a page of flash from 0xF000 in XDATA"""
521     #    data=[adr&0xFF,
522     #          (adr>>8)&0xFF,
523     #          (adr>>16)&0xFF,
524     #          (adr>>24)&0xFF]
525     #    print "Flashing buffer to 0x%06x" % adr
526     #    self.writecmd(0x13,MASS_FLASH_PAGE,4,data)
527
528     def writecmd(self, app, verb, count=0, data=[]):
529         """Write a command and some data to the GoodFET."""
530         self.serialport.write(chr(app))
531         self.serialport.write(chr(verb))
532         count = len(data)
533         #if data!=None:
534         #    count=len(data); #Initial count ignored.
535
536         #print "TX %02x %02x %04x" % (app,verb,count)
537
538         #little endian 16-bit length
539         self.serialport.write(chr(count&0xFF))
540         self.serialport.write(chr(count>>8))
541
542         #print "count=%02x, len(data)=%04x" % (count,len(data))
543
544         if count!=0:
545             if(isinstance(data,list)):
546                 for i in range(0,count):
547                     #print "Converting %02x at %i" % (data[i],i)
548                     data[i]=chr(data[i])
549             #print type(data)
550             outstr=''.join(data)
551             self.serialport.write(outstr)
552         if not self.besilent:
553             self.readcmd()
554
555
556