omj, i can't believe i left this one out.
[goodfet] / client / GoodFETARM.py
index 3305820..a2212d3 100644 (file)
@@ -1,13 +1,27 @@
 #!/usr/bin/env python
-# GoodFET Client Library
+# GoodFET ARM Client Library
 # 
 #
 # Good luck with alpha / beta code.
 # Contributions and bug reports welcome.
 #
 
-import sys, binascii, struct
+
+
+raise Exception("DEPRECATED.  USE GoodFETARM7")
+
+import sys, binascii, struct, time
 import atlasutils.smartprint as asp
+from GoodFET import GoodFET
+from intelhex import IntelHex
+
+platforms = {
+    "at91sam7": {0:(0x100000, "Flash before remap, SRAM after remap"),
+                 0x100000: (0x100000, "Internal Flash"),
+                 0x200000: (0x100000, "Internal SRAM"),
+                 },
+    }
+                
 
 #Global Commands
 READ  = 0x00
@@ -40,10 +54,6 @@ RESUMECPU           = 0x8d
 DEBUG_INSTR         = 0x8e      #
 STEP_INSTR          = 0x8f      #
 STEP_REPLACE        = 0x90      #
-READ_CODE_MEMORY    = 0x91      # ??
-WRITE_FLASH_PAGE    = 0x92      # ??
-READ_FLASH_PAGE     = 0x93      # ??
-MASS_ERASE_FLASH    = 0x94      # ??
 PROGRAM_FLASH       = 0x95
 LOCKCHIP            = 0x96      # ??
 CHIP_ERASE          = 0x97      # can do?
@@ -54,23 +64,136 @@ GET_SPSR            = 0x9a
 SET_SPSR            = 0x9b
 SET_MODE_THUMB      = 0x9c
 SET_MODE_ARM        = 0x9d
+SET_IR              = 0x9e
+WAIT_DBG            = 0x9f
+SHIFT_DR            = 0xa0
+SETWATCH0           = 0xa1
+SETWATCH1           = 0xa2
+CHAIN0              = 0xa3
 
-from GoodFET import GoodFET
-from intelhex import IntelHex
 
+MSB         = 0
+LSB         = 1
+NOEND       = 2
+NORETIDLE   = 4
 
+PM_usr = 0b10000
+PM_fiq = 0b10001
+PM_irq = 0b10010
+PM_svc = 0b10011
+PM_abt = 0b10111
+PM_und = 0b11011
+PM_sys = 0b11111
+proc_modes = {
+    PM_usr: ("User Processor Mode", "usr", "Normal program execution mode"),
+    PM_fiq: ("FIQ Processor Mode", "fiq", "Supports a high-speed data transfer or channel process"),
+    PM_irq: ("IRQ Processor Mode", "irq", "Used for general-purpose interrupt handling"),
+    PM_svc: ("Supervisor Processor Mode", "svc", "A protected mode for the operating system"),
+    PM_irq: ("Abort Processor Mode", "irq", "Implements virtual memory and/or memory protection"),
+    PM_und: ("Undefined Processor Mode", "und", "Supports software emulation of hardware coprocessor"),
+    PM_sys: ("System Processor Mode", "sys", "Runs privileged operating system tasks (ARMv4 and above)"),
+}
 
+PSR_bits = [ 
+    None,
+    None,
+    None,
+    None,
+    None,
+    "Thumb",
+    "nFIQ_int",
+    "nIRQ_int",
+    "nImprDataAbort_int",
+    "BIGendian",
+    None,
+    None,
+    None,
+    None,
+    None,
+    None,
+    "GE_0",
+    "GE_1",
+    "GE_2",
+    "GE_3",
+    None,
+    None,
+    None,
+    None,
+    "Jazelle",
+    None,
+    None,
+    "Q (DSP-overflow)",
+    "oVerflow",
+    "Carry",
+    "Zero",
+    "Neg",
+    ]
 
+
+
+ARM_INSTR_NOP =             0xe1a00000L
+ARM_INSTR_BX_R0 =           0xe12fff10L
+ARM_INSTR_STR_Rx_r14 =      0xe58f0000L # from atmel docs
+ARM_READ_REG =              ARM_INSTR_STR_Rx_r14
+ARM_INSTR_LDR_Rx_r14 =      0xe59f0000L # from atmel docs
+ARM_WRITE_REG =             ARM_INSTR_LDR_Rx_r14
+ARM_INSTR_LDR_R1_r0_4 =     0xe4901004L
+ARM_READ_MEM =              ARM_INSTR_LDR_R1_r0_4
+ARM_INSTR_STR_R1_r0_4 =     0xe4801004L
+ARM_WRITE_MEM =             ARM_INSTR_STR_R1_r0_4
+ARM_INSTR_MRS_R0_CPSR =     0xe10f0000L
+ARM_INSTR_MSR_cpsr_cxsf_R0 =0xe12ff000L
+ARM_INSTR_STMIA_R14_r0_rx = 0xE88E0000L      # add up to 65k to indicate which registers...
+ARM_STORE_MULTIPLE =        ARM_INSTR_STMIA_R14_r0_rx
+ARM_INSTR_SKANKREGS =       0xE88F7fffL
+ARM_INSTR_CLOBBEREGS =      0xE89F7fffL
+
+ARM_INSTR_B_PC =            0xea000000L
+ARM_INSTR_BX_PC =           0xe1200010L      # need to set r0 to the desired address
+THUMB_INSTR_STR_R0_r0 =     0x60006000L
+THUMB_INSTR_MOV_R0_PC =     0x46b846b8L
+THUMB_INSTR_BX_PC =         0x47784778L
+THUMB_INSTR_NOP =           0x1c001c00L
+ARM_REG_PC =                15
+
+ARM7TDMI_IR_EXTEST =            0x0
+ARM7TDMI_IR_SCAN_N =            0x2
+ARM7TDMI_IR_SAMPLE =            0x3
+ARM7TDMI_IR_RESTART =           0x4
+ARM7TDMI_IR_CLAMP =             0x5
+ARM7TDMI_IR_HIGHZ =             0x7
+ARM7TDMI_IR_CLAMPZ =            0x9
+ARM7TDMI_IR_INTEST =            0xC
+ARM7TDMI_IR_IDCODE =            0xE
+ARM7TDMI_IR_BYPASS =            0xF
+
+
+def PSRdecode(psrval):
+    output = [ "(%s mode)"%proc_modes[psrval&0x1f][1] ]
+    for x in xrange(5,32):
+        if psrval & (1<<x):
+            output.append(PSR_bits[x])
+    return " ".join(output)
+   
+fmt = [None, "B", "<H", None, "<L", None, None, None, "<Q"]
+def chop(val,byts):
+    s = struct.pack(fmt[byts], val)
+    return [ord(b) for b in s ]
+        
 class GoodFETARM(GoodFET):
     """A GoodFET variant for use with ARM7TDMI microprocessor."""
     def ARMhaltcpu(self):
         """Halt the CPU."""
         self.writecmd(0x13,HALTCPU,0,self.data)
+        print "CPSR: (%s) %s"%(self.ARMget_regCPSRstr())
+    halt=ARMhaltcpu
     def ARMreleasecpu(self):
         """Resume the CPU."""
         self.writecmd(0x13,RESUMECPU,0,self.data)
-    def ARMsetModeArm(self):
-        self.writecmd(0x13,SET_MODE_ARM,0,self.data)
+    def ARMsetModeArm(self, restart=0):
+        self.writecmd(0x13,SET_MODE_ARM,0,[restart])
+    def ARMsetModeThumb(self, restart=0):
+        self.writecmd(0x13,SET_MODE_THUMB,0,[restart])
     def ARMtest(self):
         #self.ARMreleasecpu()
         #self.ARMhaltcpu()
@@ -146,8 +269,10 @@ class GoodFETARM(GoodFET):
         """Write the config register of an ARM."""
         self.writecmd(0x13,SET_DEBUG_CTRL,1,[config&7])
     def ARMlockchip(self):
-        """Set the flash lock bit in info mem."""
-        self.writecmd(0x13, LOCKCHIP, 0, [])
+        """Set the flash lock bit in info mem.
+        Chip-Specific.  Not implemented"""
+        #self.writecmd(0x13, LOCKCHIP, 0, [])
+        raise Exception("Unimplemented: lockchip.  This is chip specific and must be implemented for each chip.")
     
 
     def ARMidentstr(self):
@@ -161,8 +286,11 @@ class GoodFETARM(GoodFET):
         self.writecmd(0x13,GET_CHIP_ID,0,[])
         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
         return retval
+    def ARMsetPC(self, val):
+        """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"""
+        self.writecmd(0x13,SET_PC,0,chop(val,4))
     def ARMgetPC(self):
-        """Get an ARM's PC."""
+        """Get an ARM's PC. Note: real PC gets all wonky in debug mode, this is the "saved" PC"""
         self.writecmd(0x13,GET_PC,0,[])
         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
         return retval
@@ -179,52 +307,164 @@ class GoodFETARM(GoodFET):
         return retval
     def ARMget_registers(self):
         """Get ARM Registers"""
-        self.writecmd(0x13,GET_REGISTERS,0, [])
-        retval = []
-        for x in range(0,len(self.data), 4):
-          retval.append(struct.unpack("<L", self.data[x:x+4])[0])
-        return retval
-    def ARMset_registers(self, regs):
+        regs = [ self.ARMget_register(x) for x in range(15) ]
+        regs.append(self.ARMgetPC())            # make sure we snag the "static" version of PC
+        return regs
+    def ARMset_registers(self, regs, mask):
         """Set ARM Registers"""
-        regarry = []
-        for reg in regs:
-          regarry.extend([reg&0xff, (reg>>8)&0xff, (reg>>16)&0xff, reg>>24])
-        self.writecmd(0x13,SET_REGISTERS,16*4,regarry)
+        for x in xrange(15):
+          if (1<<x) & mask:
+            self.ARMset_register(x,regs.pop())
+        if (1<<15) & mask:                      # make sure we set the "static" version of PC or changes will be lost
+          self.ARMsetPC(regs.pop())
+    def ARMget_regCPSRstr(self):
+        psr = self.ARMget_regCPSR()
+        return hex(psr), PSRdecode(psr)
+    def ARMget_regCPSR(self):
+        """Get an ARM's Register"""
+        self.writecmd(0x13,GET_CPSR,0,[])
         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
         return retval
+    def ARMset_regCPSR(self, val):
+        """Get an ARM's Register"""
+        self.writecmd(0x13,SET_CPSR,4,[val&0xff, (val>>8)&0xff, (val>>16)&0xff, val>>24])
     def ARMcmd(self,phrase):
         self.writecmd(0x13,READ,len(phrase),phrase)
         val=ord(self.data[0])
         print "Got %02x" % val
         return val
-    def ARMdebuginstr(self,instr):
-        if type (instr) == int:
+    def ARMdebuginstr(self,instr,bkpt):
+        if type (instr) == int or type(instr) == long:
             instr = struct.pack("<L", instr)
+        instr = [int("0x%x"%ord(x),16) for x in instr]
+        instr.extend([bkpt])
         self.writecmd(0x13,DEBUG_INSTR,len(instr),instr)
-        return (self.data[0])
-    def ARMpeekcodebyte(self,adr,words):
+        return (self.data)
+    def ARM_nop(self, bkpt):
+        return self.ARMdebuginstr(ARM_INSTR_NOP, bkpt)
+    def ARMset_IR(self, IR, noretidle=0):
+        self.writecmd(0x13,SET_IR,2, [IR, LSB|noretidle])
+        return self.data
+    def ARMshiftDR(self, data, bits, flags):
+        self.writecmd(0x13,SHIFT_DR,8,[bits&0xff, flags&0xff, 0, 0, data&0xff,(data>>8)&0xff,(data>>16)&0xff,(data>>24)&0xff])
+        return self.data
+    def ARMwaitDBG(self, timeout=0xff):
+        self.writecmd(0x13,WAIT_DBG,2,[timeout&0xf,timeout>>8])
+        return self.data
+    def ARMrestart(self):
+        #self.ARMset_IR(ARM7TDMI_IR_BYPASS)
+        self.ARMset_IR(ARM7TDMI_IR_RESTART)
+    def ARMset_watchpoint0(self, addr, addrmask, data, datamask, ctrl, ctrlmask):
+        self.data = []
+        self.data.extend(chop(addr,4))
+        self.data.extend(chop(addrmask,4))
+        self.data.extend(chop(data,4))
+        self.data.extend(chop(datamask,4))
+        self.data.extend(chop(ctrl,4))
+        self.data.extend(chop(ctrlmask,4))
+        self.writecmd(0x13,SETWATCH0,24,self.data)
+        return self.data
+    def ARMset_watchpoint1(self, addr, addrmask, data, datamask, ctrl, ctrlmask):
+        self.data = []
+        self.data.extend(chop(addr,4))
+        self.data.extend(chop(addrmask,4))
+        self.data.extend(chop(data,4))
+        self.data.extend(chop(datamask,4))
+        self.data.extend(chop(ctrl,4))
+        self.data.extend(chop(ctrlmask,4))
+        self.writecmd(0x13,SETWATCH1,24,self.data)
+        return self.data
+    def ARMreadMem(self, adr, wrdcount):
+        retval = [] 
+        r0 = self.ARMget_register(0);        # store R0 and R1
+        r1 = self.ARMget_register(1);
+        #print >>sys.stderr,("CPSR:\t%x"%self.ARMget_regCPSR())
+        for word in range(adr, adr+(wrdcount*4), 4):
+            sys.stdin.readline()
+            self.ARMset_register(0, word);        # write address into R0
+            #time.sleep(1)
+            self.ARMset_register(1, 0xdeadbeef)
+            #time.sleep(1)
+            self.ARM_nop(0)
+            #time.sleep(1)
+            self.ARM_nop(1)
+            #time.sleep(1)
+            self.ARMdebuginstr(ARM_READ_MEM, 0); # push LDR R1, [R0], #4 into instruction pipeline  (autoincrements for consecutive reads)
+            #time.sleep(1)
+            self.ARM_nop(0)
+            #time.sleep(1)
+            self.ARMrestart()
+            #time.sleep(1)
+            self.ARMwaitDBG()
+            #time.sleep(1)
+            print hex(self.ARMget_register(1))
+
+
+            # FIXME: this may end up changing te current debug-state.  should we compare to current_dbgstate?
+            #print repr(self.data[4])
+            if (len(self.data)>4 and self.data[4] == '\x00'):
+              print >>sys.stderr,("FAILED TO READ MEMORY/RE-ENTER DEBUG MODE")
+              raise Exception("FAILED TO READ MEMORY/RE-ENTER DEBUG MODE")
+              return (-1);
+            else:
+              retval.append( self.ARMget_register(1) )  # read memory value from R1 register
+              #print >>sys.stderr,("CPSR: %x\t\tR0: %x\t\tR1: %x"%(self.ARMget_regCPSR(),self.ARMget_register(0),self.ARMget_register(1)))
+        self.ARMset_register(1, r1);       # restore R0 and R1 
+        self.ARMset_register(0, r0);
+        return retval
+
+    def ARMwriteMem(self, adr, wordarray):
+        r0 = self.ARMget_register(0);        # store R0 and R1
+        r1 = self.ARMget_register(1);
+        #print >>sys.stderr,("CPSR:\t%x"%self.ARMget_regCPSR())
+        for word in xrange(adr, adr+len(string), 4):
+            self.ARMset_register(0, word);        # write address into R0
+            self.ARM_nop(0)
+            self.ARM_nop(1)
+            self.ARMdebuginstr(ARM_WRITE_MEM, 0); # push STR R1, [R0], #4 into instruction pipeline  (autoincrements for consecutive writes)
+            self.ARM_nop(0)
+            self.ARMrestart()
+            self.ARMwaitDBG()
+            print hex(self.ARMget_register(1))
+
+
+            # FIXME: this may end up changing te current debug-state.  should we compare to current_dbgstate?
+            #print repr(self.data[4])
+            if (len(self.data)>4 and self.data[4] == '\x00'):
+              print >>sys.stderr,("FAILED TO READ MEMORY/RE-ENTER DEBUG MODE")
+              raise Exception("FAILED TO READ MEMORY/RE-ENTER DEBUG MODE")
+              return (-1);
+            else:
+              retval.append( self.ARMget_register(1) )  # read memory value from R1 register
+              #print >>sys.stderr,("CPSR: %x\t\tR0: %x\t\tR1: %x"%(self.ARMget_regCPSR(),self.ARMget_register(0),self.ARMget_register(1)))
+        self.ARMset_register(1, r1);       # restore R0 and R1 
+        self.ARMset_register(0, r0);
+        return retval
+
+    def ARMpeekcodewords(self,adr,words):
         """Read the contents of code memory at an address."""
         self.data=[adr&0xff, (adr>>8)&0xff, (adr>>16)&0xff, (adr>>24)&0xff, words&0xff, (words>>8)&0xff, (words>>16)&0xff, (words>>24)&0xff ]
-        self.writecmd(0x13,PEEK,2,self.data)
+        self.writecmd(0x13,READ_CODE_MEMORY,8,self.data)
         retval = []
         retval.append(self.serialport.read(words*4))
         #retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
         return "".join(retval)
     def ARMpeekdatabyte(self,adr):
         """Read the contents of data memory at an address."""
-        self.data=[adr&0xff, (adr&0xff00)>>8, (adr&0xff0000)>>16, (adr&0xff000000)>>24]
-        self.writecmd(0x13, PEEK, 2, self.data)
+        self.data=[ adr&0xff, (adr>>8)&0xff, (adr>>16)&0xff, (adr>>24)&0xff ]
+        self.writecmd(0x13, PEEK, 4, self.data)
+        #retval.append(self.serialport.read(words*4))
         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
         return retval
     def ARMpokedatabyte(self,adr,val):
         """Write a byte to data memory."""
-        self.data=[adr&0xff, (adr&0xff00)>>8, val]
-        self.writecmd(0x13, POKE, 3, self.data)
+        self.data=[adr&0xff, (adr>>8)&0xff, (adr>>16)&0xff, (adr>>24)&0xff, val&0xff, (val>>8)&0xff, (val>>16)&0xff, (val>>24)&0xff ]
+        self.writecmd(0x13, POKE, 8, self.data)
         retval = struct.unpack("<L", "".join(self.data[0:4]))[0]
         return retval
-    def ARMchiperase(self):
-        """Erase all of the target's memory."""
-        self.writecmd(0x13,CHIP_ERASE,0,[])
+    #def ARMchiperase(self):
+    #    """Erase all of the target's memory."""
+    #    self.writecmd(0x13,CHIP_ERASE,0,[])
     def ARMstatus(self):
         """Check the status."""
         self.writecmd(0x13,GET_DEBUG_STATE,0,[])
@@ -252,30 +492,38 @@ class GoodFETARM(GoodFET):
                 str="%s %s" %(self.ARMstatusbits[i],str)
             i*=2
         return str
+    def ARMchain0(self, address, bits, data):
+        bulk = chop(address,4)
+        bulk.extend(chop(bits,8))
+        bulk.extend(chop(data,4))
+        print (repr(bulk))
+        self.writecmd(0x13,CHAIN0,16,bulk)
+        d1,b1,a1 = struct.unpack("<LQL",self.data)
+        return (a1,b1,d1)
     def start(self):
         """Start debugging."""
         self.writecmd(0x13,START,0,self.data)
-        #ident=self.ARMidentstr()
-        #print "Target identifies as %s." % ident
-        #print "Status: %s." % self.ARMstatusstr()
+        ident=self.ARMidentstr()
+        print "Target identifies as %s." % ident
+        print "Debug Status: %s." % self.ARMstatusstr()
+        #print "System State: %x." % self.ARMget_regCPSRstr()
         #self.ARMreleasecpu()
         #self.ARMhaltcpu()
-        #print "Status: %s." % self.ARMstatusstr()
         
     def stop(self):
         """Stop debugging."""
         self.writecmd(0x13,STOP,0,self.data)
-    def ARMstep_instr(self):
-        """Step one instruction."""
-        self.writecmd(0x13,STEP_INSTR,0,self.data)
-    def ARMflashpage(self,adr):
-        """Flash 2kB a page of flash from 0xF000 in XDATA"""
-        data=[adr&0xFF,
-              (adr>>8)&0xFF,
-              (adr>>16)&0xFF,
-              (adr>>24)&0xFF]
-        print "Flashing buffer to 0x%06x" % adr
-        self.writecmd(0x13,MASS_FLASH_PAGE,4,data)
+    #def ARMstep_instr(self):
+    #    """Step one instruction."""
+    #    self.writecmd(0x13,STEP_INSTR,0,self.data)
+    #def ARMflashpage(self,adr):
+    #    """Flash 2kB a page of flash from 0xF000 in XDATA"""
+    #    data=[adr&0xFF,
+    #          (adr>>8)&0xFF,
+    #          (adr>>16)&0xFF,
+    #          (adr>>24)&0xFF]
+    #    print "Flashing buffer to 0x%06x" % adr
+    #    self.writecmd(0x13,MASS_FLASH_PAGE,4,data)
 
     def writecmd(self, app, verb, count=0, data=[]):
         """Write a command and some data to the GoodFET."""
@@ -305,3 +553,4 @@ class GoodFETARM(GoodFET):
             self.readcmd()
 
 
+