A major refactor of the GoodFET firmware build system and apps to give better
[goodfet] / firmware / apps / jtag / jtagarm7.c
index 2ef7dad..8cd4c26 100644 (file)
@@ -4,8 +4,39 @@
 
 #include "platform.h"
 #include "command.h"
+#include "jtag.h"
 #include "jtagarm7.h"
 
+//! Handles ARM7TDMI JTAG commands.  Forwards others to JTAG.
+void jtagarm7_handle_fn( uint8_t const app,
+                                                uint8_t const verb,
+                                                uint32_t const len);
+
+// define the jtagarm7 app's app_t
+app_t const jtagarm7_app = {
+
+       /* app number */
+       JTAGARM7,
+
+       /* handle fn */
+       jtagarm7_handle_fn,
+
+       /* name */
+       "JTAGARM7",
+
+       /* desc */
+       "\tThe JTAGARM7 app extends the basic JTAG app with support\n"
+       "\tfor JTAG'ing ARM7TDMI based devices.\n"
+};
+
+unsigned long last_instr = -1;
+unsigned char last_sysstate = 0;
+unsigned char last_ir = -1;
+unsigned char last_scanchain = -1;
+unsigned char tapstate = 15;
+unsigned char current_dbgstate = -1;
+//unsigned char last_halt_debug_state = -1;
+//unsigned long last_halt_pc = -1;
 
 /**** 20-pin Connection Information (pin1 is on top-right for both connectors)****
 GoodFET  ->  7TDMI 20-pin connector (HE-10 connector)
@@ -35,7 +66,7 @@ http://hri.sourceforge.net/tools/jtag_faq_org.html
 
 /*  WHAT SHOULD THIS MODULE DO?
  *     *start
- *     *shift_ir
+ *     *jtagarm_shift_ir
  *     *shift_dr
  *      reset_tap
  *     *scanchain0
@@ -55,10 +86,14 @@ void jtagarm7tdmi_start() {
 }
 
 
-u8 shift_ir(u8 ir, u8 flags){
-  u8 retval;
-  jtag_goto_shift_ir();
-  retval = jtagtransn(ir, 4, LSB|flags); 
+u8 jtagarm_shift_ir(u8 ir, u8 flags){
+  u8 retval = 0;
+  if (last_ir != ir){
+    jtag_goto_shift_ir();
+    retval = jtagtransn(ir, 4, LSB|flags); 
+    tapstate = RunTest_Idle;
+    last_ir = ir;
+  }
   return retval;
 }
 
@@ -70,11 +105,15 @@ wise, when in debug state, the core will not be correctly isolated and intrusive
 commands occur. Therefore, it is recommended to pass directly from the “Update”
 state” to the “Select DR” state each time the “Update” state is reached.
 */
-  unsigned long retval;
-  shift_ir(ARM7TDMI_IR_SCAN_N, NORETIDLE);
-  jtag_goto_shift_dr();
-  retval = jtagtransn(chain, 4, LSB | NORETIDLE);
-  shift_ir(testmode, NORETIDLE); 
+  unsigned long retval = 0;
+  if (last_scanchain != chain){
+    jtagarm_shift_ir(ARM7TDMI_IR_SCAN_N, NORETIDLE);
+    last_scanchain = chain;
+    jtag_goto_shift_dr();
+    retval = jtagtransn(chain, 4, LSB | NORETIDLE);
+    tapstate = Update_DR;
+  }
+  jtagarm_shift_ir(testmode, NORETIDLE); 
   return(retval);
 }
 
@@ -88,6 +127,7 @@ unsigned long eice_write(unsigned char reg, unsigned long data){
   retval = jtagtransn(data, 32, LSB| NOEND| NORETIDLE);         // send in the data - 32-bits lsb
   temp = jtagtransn(reg, 5, LSB| NOEND| NORETIDLE);             // send in the register address - 5 bits lsb
   jtagtransn(1, 1, LSB);                                        // send in the WRITE bit
+  tapstate = RunTest_Idle;
   return(retval); 
 }
 
@@ -100,85 +140,147 @@ unsigned long eice_read(unsigned char reg){               // PROVEN
   jtagtransn(0L, 1, LSB);                                       // clear TDI to select "read only"
   jtag_goto_shift_dr();                                         // Now shift out the 32 bits
   retval = jtagtransn(0L, 32, LSB);                             // atmel arm jtag docs pp.10-11: LSB first
+  tapstate = RunTest_Idle;
   return(retval);
   
 }
 
 //! push an instruction into the pipeline
 unsigned long jtagarm7tdmi_instr_primitive(unsigned long instr, char breakpt){  // PROVEN
-  unsigned long retval;
+  unsigned long retval = 0;
   jtagarm7tdmi_scan(1, ARM7TDMI_IR_INTEST);
 
-  jtag_goto_shift_dr();
-  // if the next instruction is to run using MCLK (master clock), set TDI
-  if (breakpt)
-    {
-    SETMOSI;
-    } 
-  else
-    {
-    CLRMOSI; 
-    }
-  jtag_tcktock();
-  
-  // Now shift in the 32 bits
-  retval = jtagtransn(instr, 32, 0);    // Must return to RUN-TEST/IDLE state for instruction to enter pipeline, and causes debug clock.
+  //debughex32(instr);
+  if (last_instr != instr && last_sysstate != breakpt){
+    jtag_goto_shift_dr();
+    // if the next instruction is to run using MCLK (master clock), set TDI
+    if (breakpt)
+      {
+      SETMOSI;
+      } 
+    else
+      {
+      CLRMOSI; 
+      }
+    jtag_tcktock();
+    
+    // Now shift in the 32 bits
+    retval = jtagtransn(instr, 32, 0);    // Must return to RUN-TEST/IDLE state for instruction to enter pipeline, and causes debug clock.
+    tapstate = RunTest_Idle;
+    last_instr = instr;
+    last_sysstate = breakpt;
+  } else
+    jtag_tcktock();
   return(retval);
 }
 
 u32 jtagarm7tdmi_nop(u8 brkpt){
+    //  WARNING: current_dbgstate must be up-to-date before calling this function!!!!!
+    if (current_dbgstate & JTAG_ARM7TDMI_DBG_TBIT)
+        return jtagarm7tdmi_instr_primitive(THUMB_INSTR_NOP, brkpt);
     return jtagarm7tdmi_instr_primitive(ARM_INSTR_NOP, brkpt);
 }
 
 /******************** Complex Commands **************************/
 
 //! Retrieve a 32-bit Register value
-unsigned long jtagarm7tdmi_get_register(unsigned long reg) {                    //PROVEN
-  unsigned long retval=0L, instr;
-  if (eice_read(EICE_DBGSTATUS)& JTAG_ARM7TDMI_DBG_TBIT)
-    instr = THUMB_READ_REG | reg | (reg<<16);
-  else
-    instr = (unsigned long)(reg<<12L) | (unsigned long)ARM_READ_REG;   // STR Rx, [R14] 
-
-  jtagarm7tdmi_nop( 0);
+unsigned long jtagarm7_get_reg_prim(unsigned long instr){
   jtagarm7tdmi_nop( 0);
   jtagarm7tdmi_instr_primitive(instr, 0);
   jtagarm7tdmi_nop( 0);
   jtagarm7tdmi_nop( 0);
   jtagarm7tdmi_nop( 0);
-  retval = jtagarm7tdmi_nop( 0);                        // recover 32-bit word
-  return retval;
+  return jtagarm7tdmi_nop( 0);                          // recover 32-bit word
 }
 
 //! Set a 32-bit Register value
-void jtagarm7tdmi_set_register(unsigned long reg, unsigned long val) {          // PROVEN (assuming target reg is word aligned)
-  unsigned long instr;
-  if (eice_read(EICE_DBGSTATUS) & JTAG_ARM7TDMI_DBG_TBIT)
-    instr = THUMB_WRITE_REG | (reg&7) | ((reg&7)<<16);
-  else
-    instr = (unsigned long)(((unsigned long)reg<<12L) | ARM_WRITE_REG); //  LDR Rx, [R14]
-  
-  jtagarm7tdmi_nop( 0);            // push nop into pipeline - clean out the pipeline...
-  jtagarm7tdmi_nop( 0);            // push nop into pipeline - clean out the pipeline...
-  jtagarm7tdmi_instr_primitive(instr, 0); // push instr into pipeline - fetch
+void jtagarm7_set_reg_prim(unsigned long instr, unsigned long reg, unsigned long val){      // PROVEN - 100827 (non-PC)
+  jtagarm7tdmi_nop( 0);                                 // push nop into pipeline - executed 
+  jtagarm7tdmi_instr_primitive(instr, 0);               // push instr into pipeline - fetch
+    jtagarm7tdmi_nop( 0);                               // push nop into pipeline - decode 
+    jtagarm7tdmi_nop( 0);                               // push nop into pipeline - execute 
+    jtagarm7tdmi_instr_primitive(val, 0);               // push 32-bit word on data bus
   if (reg == ARM_REG_PC){
-    jtagarm7tdmi_instr_primitive(val, 0); // push 32-bit word on data bus
-    jtagarm7tdmi_nop( 0);            // push nop into pipeline - executed 
-    jtagarm7tdmi_nop( 0);            // push nop into pipeline - executed 
+    //debugstr("setting pc...");
+    jtagarm7tdmi_nop( 0);                               // push nop into pipeline - refill 
+    jtagarm7tdmi_nop( 0);                               // push nop into pipeline - refill 
+  }
+  jtagarm7tdmi_nop( 0);                               // push nop into pipeline - decode 
+  jtagarm7tdmi_nop( 0);                               // push nop into pipeline - execute 
+}
+
+void jtagarm7_thumb_swap_reg(unsigned char dir, unsigned long reg){                         // PROVEN - 100827
+  reg = reg & 7;
+  jtagarm7tdmi_nop( 0);
+  if (dir){
+    jtagarm7tdmi_instr_primitive((unsigned long)(THUMB_INSTR_MOV_LoHi | (reg) | (reg<<16)), 0);
+    //debughex32((unsigned long)(THUMB_INSTR_MOV_LoHi | (reg) | (reg<<16)));
   } else {
-    jtagarm7tdmi_nop( 0);            // push nop into pipeline - decode
-    jtagarm7tdmi_nop( 0);            // push nop into pipeline - execute
-    jtagarm7tdmi_instr_primitive(val, 0); // push 32-bit word on data bus
+    jtagarm7tdmi_instr_primitive((unsigned long)(THUMB_INSTR_MOV_HiLo | (reg<<3) | (reg<<19)), 0);
+    //debughex32((unsigned long)(THUMB_INSTR_MOV_HiLo | (reg<<3) | (reg<<19)));
   }
-  jtagarm7tdmi_nop( 0);            // push nop into pipeline - executed 
-  jtagarm7tdmi_nop( 0);            // push nop into pipeline - executed 
   jtagarm7tdmi_nop( 0);
+  jtagarm7tdmi_nop( 0);
+  jtagarm7tdmi_nop( 0);
+}
+  
+unsigned long jtagarm7tdmi_get_register(unsigned long reg) {                                // PROVEN - 100827
+  unsigned long retval=0L, instr, r0;
+  current_dbgstate = eice_read(EICE_DBGSTATUS);
+  if (current_dbgstate & JTAG_ARM7TDMI_DBG_TBIT){
+    if (reg > 7){
+      //debugstr("debug: jtagarm7tdmi_get_register: thumb reg > 15");
+      reg = reg & 7;
+      r0 = jtagarm7_get_reg_prim( THUMB_READ_REG);          // save reg0
+      jtagarm7_thumb_swap_reg(THUMB_SWAP_HiLo, reg);        // clobber reg0 with hi reg
+      retval = jtagarm7_get_reg_prim( THUMB_READ_REG);      // recover 32-bit word
+      jtagarm7_set_reg_prim( THUMB_WRITE_REG, 0, r0);       // restore r0
+      return retval;
+    } else {
+      instr = (unsigned long)(THUMB_READ_REG | (unsigned long)reg | (unsigned long)(reg<<16L));
+    }
+  } else
+    instr = (reg<<12L) | ARM_READ_REG;    // STR Rx, [R14] 
+  return jtagarm7_get_reg_prim(instr);
+}
+
+//! Set a 32-bit Register value
+//  writing to a register is a problem child in ARM, actually.  if the register you are using as the indirect offset register is misaligned, your results are misaligned.
+//  this set_register implementation normalizes this process at the cost of performance.  since we don't know what's in the register, we set it to 0 first
+//  we could use r14 and hope all is well, but only for arm, not thumb mode, and not always is all well then either.  this is a performance trade-off we may have to revisit later
+//
+void jtagarm7tdmi_set_register(unsigned long reg, unsigned long val) {                      // PROVEN - 100827
+  unsigned long instr, r0;
+  current_dbgstate = eice_read(EICE_DBGSTATUS);
+  if (current_dbgstate & JTAG_ARM7TDMI_DBG_TBIT){
+    if (reg > 7){
+      
+      r0 = jtagarm7_get_reg_prim(THUMB_READ_REG);
+      jtagarm7_set_reg_prim(THUMB_WRITE_REG, 0, 0);
+      instr = (unsigned long)(THUMB_WRITE_REG | (unsigned long)reg | (unsigned long)(reg<<16L));
+      jtagarm7_set_reg_prim(instr, reg, val);
+      jtagarm7_thumb_swap_reg(THUMB_SWAP_LoHi, reg);                // place 32-bit word into a high register
+      jtagarm7_set_reg_prim( THUMB_WRITE_REG, 0, r0);               // restore r0
+    } else
+      instr = THUMB_WRITE_REG | (reg) | ((reg)<<16) | ((reg)<<3) | ((reg)<<19);
+  } else {
+    instr = ARM_WRITE_REG | (reg<<12L) | (reg<<16); //  LDR Rx, [R14]
+  }
+  
+  //debughex32(instr);
+  //  --- first time to clear the register... this ensures the write is not 8-bit offset ---
+  jtagarm7_set_reg_prim(instr, reg, 0);
+  //  --- now we actually write to the register ---
+  jtagarm7_set_reg_prim(instr, reg, val);
 }
 
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 //! Handles ARM7TDMI JTAG commands.  Forwards others to JTAG.
-void jtagarm7tdmihandle(unsigned char app, unsigned char verb, unsigned long len){
+void jtagarm7_handle_fn( uint8_t const app,
+                                                uint8_t const verb,
+                                                uint32_t const len)
+{
   unsigned int val;
  
   switch(verb){
@@ -188,12 +290,13 @@ void jtagarm7tdmihandle(unsigned char app, unsigned char verb, unsigned long len
     txdata(app,verb,0);
     break;
   case JTAG_IR_SHIFT:
-    cmddataword[0] = shift_ir(cmddata[0], cmddata[1]);
+    cmddataword[0] = jtagarm_shift_ir(cmddata[0], cmddata[1]);
     txdata(app,verb,1);
     break;
   case JTAG_DR_SHIFT:
     jtag_goto_shift_dr();
     cmddatalong[0] = jtagtransn(cmddatalong[1],cmddata[0],cmddata[1]);
+    tapstate = (cmddata[1]&NORETIDLE)>0?Update_DR:RunTest_Idle;
     txdata(app,verb,4);
     break;
   case JTAGARM7_CHAIN0:
@@ -207,12 +310,13 @@ void jtagarm7tdmihandle(unsigned char app, unsigned char verb, unsigned long len
     cmddatalong[2] = jtagtransn(cmddataword[4], 9, MSB| NOEND| NORETIDLE);
     cmddatalong[1] = jtagtransn(cmddatalong[1], 32, MSB| NOEND| NORETIDLE);
     cmddatalong[3] = jtagtransn(cmddatalong[3], 32, MSB);
+    tapstate = RunTest_Idle;
     txdata(app,verb,16);
     break;
   case JTAGARM7_SCANCHAIN1:
   case JTAGARM7_DEBUG_INSTR:
     cmddatalong[0] = jtagarm7tdmi_instr_primitive(cmddatalong[0],cmddata[4]);
-    txdata(app,verb,8);
+    txdata(app,verb,4);
     break;
   case JTAGARM7_EICE_READ:
     cmddatalong[0] = eice_read(cmddata[0]);
@@ -232,9 +336,12 @@ void jtagarm7tdmihandle(unsigned char app, unsigned char verb, unsigned long len
     txdata(app,verb,4);
     break;
   case JTAG_RESETTARGET:
+    //FIXME: BORKEN
+    debugstr("RESET TARGET");
     CLRTST;
-    delay(10);
+    delay(cmddataword[0]);
     SETTST;
+    txdata(app,verb,4);
     break;
 
 
@@ -248,7 +355,7 @@ void jtagarm7tdmihandle(unsigned char app, unsigned char verb, unsigned long len
   case JTAGARM7_CHIP_ERASE:
   */
   default:
-    jtaghandle(app,verb,len);
+    (*(jtag_app.handle))(app,verb,len);
   }
 }