dsp563xx: Adding breakpoint/watchpoint support. 68/1468/3
authorBernhard Kiesbauer <bernhard@kiesbauer.com>
Mon, 1 Jul 2013 19:27:05 +0000 (21:27 +0200)
committerSpencer Oliver <spen@spen-soft.co.uk>
Mon, 15 Jul 2013 10:05:15 +0000 (10:05 +0000)
Added missing breakpoint/watchpoint implementation to dsp563xx target.
Implementation is not yet complete, which means it does not leverage all
available features of the once debug interface.
This does NOT use the openocd breakpoint/watchpoint command because of
the "special" memory address spaces (X/Y/P/L) of the 56k DSP series.

Change-Id: I6840a3ff1e6fdebb38ab7758f164886aff773af6
Signed-off-by: Bernhard Kiesbauer <bernhard@kiesbauer.com>
Reviewed-on: http://openocd.zylin.com/1468
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
src/target/dsp563xx.c
src/target/dsp563xx.h
src/target/dsp563xx_once.c
src/target/dsp563xx_once.h

index 4dfb66e..1211ac5 100644 (file)
@@ -25,6 +25,7 @@
 #include <jim.h>
 
 #include "target.h"
+#include "breakpoints.h"
 #include "target_type.h"
 #include "algorithm.h"
 #include "register.h"
 #define ASM_REG_W_AAR2  0xFFFFF7
 #define ASM_REG_W_AAR3  0xFFFFF6
 
+/*
+ * OBCR Register bit definitions
+ */
+#define OBCR_b0_and_b1            ((0x0) << 10)
+#define OBCR_b0_or_b1             ((0x1) << 10)
+#define OBCR_b1_after_b0          ((0x2) << 10)
+#define OBCR_b0_after_b1          ((0x3) << 10)
+
+#define OBCR_BP_DISABLED          (0x0)
+#define OBCR_BP_MEM_P             (0x1)
+#define OBCR_BP_MEM_X             (0x2)
+#define OBCR_BP_MEM_Y             (0x3)
+#define OBCR_BP_ON_READ           ((0x2) << 0)
+#define OBCR_BP_ON_WRITE          ((0x1) << 0)
+#define OBCR_BP_CC_NOT_EQUAL      ((0x0) << 2)
+#define OBCR_BP_CC_EQUAL          ((0x1) << 2)
+#define OBCR_BP_CC_LESS_THAN      ((0x2) << 2)
+#define OBCR_BP_CC_GREATER_THAN   ((0x3) << 2)
+
+#define OBCR_BP_0(x)              ((x)<<2)
+#define OBCR_BP_1(x)              ((x)<<6)
+
+
 enum once_reg_idx {
        ONCE_REG_IDX_OSCR = 0,
        ONCE_REG_IDX_OMBC = 1,
@@ -290,6 +314,13 @@ enum memory_type {
        MEM_L = 3,
 };
 
+enum watchpoint_condition {
+       EQUAL,
+       NOT_EQUAL,
+       GREATER,
+       LESS_THAN
+};
+
 #define INSTR_JUMP      0x0AF080
 /* Effective Addressing Mode Encoding */
 #define EAME_R0         0x10
@@ -880,6 +911,10 @@ static int dsp563xx_init_target(struct command_context *cmd_ctx, struct target *
        LOG_DEBUG("%s", __func__);
 
        dsp563xx_build_reg_cache(target);
+       struct dsp563xx_common *dsp563xx = target_to_dsp563xx(target);
+
+       dsp563xx->hardware_breakpoints_cleared = 0;
+       dsp563xx->hardware_breakpoint[0].used = BPU_NONE;
 
        return ERROR_OK;
 }
@@ -903,6 +938,9 @@ static int dsp563xx_examine(struct target *target)
                        chip += 300;
 
                LOG_INFO("DSP56%03d device found", chip);
+
+               /* Clear all breakpoints */
+               dsp563xx_once_reg_write(target->tap, 1, DSP563XX_ONCE_OBCR, 0);
        }
 
        return ERROR_OK;
@@ -1045,6 +1083,13 @@ static int dsp563xx_poll(struct target *target)
                }
        }
 
+       if (!dsp563xx->hardware_breakpoints_cleared) {
+               err = dsp563xx_once_reg_write(target->tap, 1, DSP563XX_ONCE_OBCR, 0);
+               err = dsp563xx_once_reg_write(target->tap, 1, DSP563XX_ONCE_OMLR0, 0);
+               err = dsp563xx_once_reg_write(target->tap, 1, DSP563XX_ONCE_OMLR1, 0);
+               dsp563xx->hardware_breakpoints_cleared = 1;
+       }
+
        return ERROR_OK;
 }
 
@@ -1814,24 +1859,22 @@ static int dsp563xx_write_buffer_default(struct target *target,
                        buffer);
 }
 
-static int dsp563xx_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
-{
-       return ERROR_OK;
-}
-
-static int dsp563xx_remove_breakpoint(struct target *target, struct breakpoint *breakpoint)
-{
-       return ERROR_OK;
-}
-
+/*
+ * Exit with error here, because we support watchpoints over a custom command.
+ * This is because the DSP has separate X,Y,P memspace which is not compatible to the
+ * traditional watchpoint logic.
+ */
 static int dsp563xx_add_watchpoint(struct target *target, struct watchpoint *watchpoint)
 {
-       return ERROR_OK;
+       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
 }
 
+/*
+ * @see dsp563xx_add_watchpoint
+ */
 static int dsp563xx_remove_watchpoint(struct target *target, struct watchpoint *watchpoint)
 {
-       return ERROR_OK;
+       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
 }
 
 static void handle_md_output(struct command_context *cmd_ctx,
@@ -1895,6 +1938,217 @@ static void handle_md_output(struct command_context *cmd_ctx,
        }
 }
 
+static int dsp563xx_add_custom_watchpoint(struct target *target, uint32_t address, uint32_t memType,
+               enum watchpoint_rw rw, enum watchpoint_condition cond)
+{
+       int err = ERROR_OK;
+       struct dsp563xx_common *dsp563xx = target_to_dsp563xx(target);
+
+       bool wasRunning = false;
+       /* Only set breakpoint when halted */
+       if (target->state != TARGET_HALTED) {
+               dsp563xx_halt(target);
+               wasRunning = true;
+       }
+
+       if (dsp563xx->hardware_breakpoint[0].used) {
+               LOG_ERROR("Cannot add watchpoint. Hardware resource already used.");
+               err = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       uint32_t obcr_value = 0;
+       if      (err == ERROR_OK) {
+               obcr_value |= OBCR_b0_or_b1;
+               switch (memType) {
+                       case MEM_X:
+                               obcr_value |= OBCR_BP_MEM_X;
+                               break;
+                       case MEM_Y:
+                               obcr_value |= OBCR_BP_MEM_Y;
+                               break;
+                       case MEM_P:
+                               obcr_value |= OBCR_BP_MEM_P;
+                               break;
+                       default:
+                               LOG_ERROR("Unknown memType parameter (%d)", memType);
+                               err = ERROR_TARGET_INVALID;
+               }
+       }
+
+       if (err == ERROR_OK) {
+               switch (rw) {
+                       case WPT_READ:
+                               obcr_value |= OBCR_BP_0(OBCR_BP_ON_READ);
+                               break;
+                       case WPT_WRITE:
+                               obcr_value |= OBCR_BP_0(OBCR_BP_ON_WRITE);
+                               break;
+                       case WPT_ACCESS:
+                               obcr_value |= OBCR_BP_0(OBCR_BP_ON_READ|OBCR_BP_ON_WRITE);
+                               break;
+                       default:
+                               LOG_ERROR("Unsupported write mode (%d)", rw);
+                               err = ERROR_TARGET_INVALID;
+               }
+       }
+
+       if (err == ERROR_OK) {
+               switch (cond) {
+                       case EQUAL:
+                               obcr_value |= OBCR_BP_0(OBCR_BP_CC_EQUAL);
+                               break;
+                       case NOT_EQUAL:
+                               obcr_value |= OBCR_BP_0(OBCR_BP_CC_NOT_EQUAL);
+                               break;
+                       case LESS_THAN:
+                               obcr_value |= OBCR_BP_0(OBCR_BP_CC_LESS_THAN);
+                               break;
+                       case GREATER:
+                               obcr_value |= OBCR_BP_0(OBCR_BP_CC_GREATER_THAN);
+                               break;
+                       default:
+                               LOG_ERROR("Unsupported condition code (%d)", cond);
+                               err = ERROR_TARGET_INVALID;
+               }
+       }
+
+       if (err == ERROR_OK)
+               err = dsp563xx_once_reg_write(target->tap, 1, DSP563XX_ONCE_OMLR0, address);
+
+       if (err == ERROR_OK)
+               err = dsp563xx_once_reg_write(target->tap, 1, DSP563XX_ONCE_OMLR1, 0x0);
+
+       if (err == ERROR_OK)
+               err = dsp563xx_once_reg_write(target->tap, 1, DSP563XX_ONCE_OBCR, obcr_value);
+
+       if (err == ERROR_OK) {
+               /* You should write the memory breakpoint counter to 0 */
+               err = dsp563xx_once_reg_write(target->tap, 1, DSP563XX_ONCE_OMBC, 0);
+       }
+
+       if (err == ERROR_OK) {
+               /* You should write the memory breakpoint counter to 0 */
+               err = dsp563xx_once_reg_write(target->tap, 1, DSP563XX_ONCE_OTC, 0);
+       }
+
+       if (err == ERROR_OK)
+               dsp563xx->hardware_breakpoint[0].used = BPU_WATCHPOINT;
+
+       if (err == ERROR_OK && wasRunning) {
+               /* Resume from current PC */
+               err = dsp563xx_resume(target, 1, 0x0, 0, 0);
+       }
+
+       return err;
+}
+
+static int dsp563xx_remove_custom_watchpoint(struct target *target)
+{
+       int err = ERROR_OK;
+       struct dsp563xx_common *dsp563xx = target_to_dsp563xx(target);
+
+       if (dsp563xx->hardware_breakpoint[0].used != BPU_WATCHPOINT) {
+               LOG_ERROR("Cannot remove watchpoint, as no watchpoint is currently configured!");
+               err = ERROR_TARGET_INVALID;
+       }
+
+       if (err == ERROR_OK) {
+               /* Clear watchpoint by clearing OBCR. */
+               err = dsp563xx_once_reg_write(target->tap, 1, DSP563XX_ONCE_OBCR, 0);
+       }
+
+       if (err == ERROR_OK)
+               dsp563xx->hardware_breakpoint[0].used = BPU_NONE;
+
+       return err;
+}
+
+COMMAND_HANDLER(dsp563xx_add_watchpoint_command)
+{
+       int err = ERROR_OK;
+       struct target *target = get_current_target(CMD_CTX);
+
+       uint32_t mem_type = 0;
+       switch (CMD_NAME[2]) {
+               case 'x':
+                       mem_type = MEM_X;
+                       break;
+               case 'y':
+                       mem_type = MEM_Y;
+                       break;
+               case 'p':
+                       mem_type = MEM_P;
+                       break;
+               default:
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       if (CMD_ARGC < 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       uint32_t address = 0;
+       if (CMD_ARGC > 2)
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], address);
+
+       enum watchpoint_condition cond;
+       switch (CMD_ARGV[0][0]) {
+               case '>':
+                       cond = GREATER;
+                       break;
+               case '<':
+                       cond = LESS_THAN;
+                       break;
+               case '=':
+                       cond = EQUAL;
+                       break;
+               case '!':
+                       cond = NOT_EQUAL;
+                       break;
+               default:
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       enum watchpoint_rw rw;
+       switch (CMD_ARGV[1][0]) {
+               case 'r':
+                       rw = WPT_READ;
+                       break;
+               case 'w':
+                       rw = WPT_WRITE;
+                       break;
+               case 'a':
+                       rw = WPT_ACCESS;
+                       break;
+               default:
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       err = dsp563xx_add_custom_watchpoint(target, address, mem_type, rw, cond);
+
+       return err;
+}
+
+/* Adding a breakpoint using the once breakpoint logic.
+ * Note that this mechanism is a true hw breakpoint and is share between the watchpoint logic.
+ * This means, you can only have one breakpoint/watchpoint at any time.
+ */
+static int dsp563xx_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
+{
+       return dsp563xx_add_custom_watchpoint(target, breakpoint->address, MEM_P, WPT_READ, EQUAL);
+}
+
+static int dsp563xx_remove_breakpoint(struct target *target, struct breakpoint *breakpoint)
+{
+       return dsp563xx_remove_custom_watchpoint(target);
+}
+
+COMMAND_HANDLER(dsp563xx_remove_watchpoint_command)
+{
+       struct target *target = get_current_target(CMD_CTX);
+
+       return dsp563xx_remove_custom_watchpoint(target);
+}
+
 COMMAND_HANDLER(dsp563xx_mem_command)
 {
        struct target *target = get_current_target(CMD_CTX);
@@ -1985,42 +2239,73 @@ static const struct command_registration dsp563xx_command_handlers[] = {
                .handler = dsp563xx_mem_command,
                .mode = COMMAND_EXEC,
                .help = "write x memory words",
-               .usage = "mwwx address value [count]",
+               .usage = "address value [count]",
        },
        {
                .name = "mwwy",
                .handler = dsp563xx_mem_command,
                .mode = COMMAND_EXEC,
                .help = "write y memory words",
-               .usage = "mwwy address value [count]",
+               .usage = "address value [count]",
        },
        {
                .name = "mwwp",
                .handler = dsp563xx_mem_command,
                .mode = COMMAND_EXEC,
                .help = "write p memory words",
-               .usage = "mwwp address value [count]",
+               .usage = "address value [count]",
        },
        {
                .name = "mdwx",
                .handler = dsp563xx_mem_command,
                .mode = COMMAND_EXEC,
                .help = "display x memory words",
-               .usage = "mdwx address [count]",
+               .usage = "address [count]",
        },
        {
                .name = "mdwy",
                .handler = dsp563xx_mem_command,
                .mode = COMMAND_EXEC,
                .help = "display y memory words",
-               .usage = "mdwy address [count]",
+               .usage = "address [count]",
        },
        {
                .name = "mdwp",
                .handler = dsp563xx_mem_command,
                .mode = COMMAND_EXEC,
                .help = "display p memory words",
-               .usage = "mdwp address [count]",
+               .usage = "address [count]",
+       },
+  /*
+   * Watchpoint commands
+   */
+       {
+               .name = "wpp",
+               .handler = dsp563xx_add_watchpoint_command,
+               .mode = COMMAND_EXEC,
+               .help = "Create p memspace watchpoint",
+               .usage = "(>|<|=|!) (r|w|a) address",
+       },
+       {
+               .name = "wpx",
+               .handler = dsp563xx_add_watchpoint_command,
+               .mode = COMMAND_EXEC,
+               .help = "Create x memspace watchpoint",
+               .usage = "(>|<|=|!) (r|w|a) address",
+       },
+       {
+               .name = "wpy",
+               .handler = dsp563xx_add_watchpoint_command,
+               .mode = COMMAND_EXEC,
+               .help = "Create y memspace watchpoint",
+               .usage = "(>|<|=|!) (r|w|a) address",
+       },
+       {
+               .name = "rwpc",
+               .handler = dsp563xx_remove_watchpoint_command,
+               .mode = COMMAND_EXEC,
+               .help = "remove watchpoint custom",
+               .usage = " ",
        },
        COMMAND_REGISTRATION_DONE
 };
index b2ee9d1..446ad3f 100644 (file)
@@ -31,6 +31,16 @@ struct mcu_jtag {
        struct jtag_tap *tap;
 };
 
+enum breakpoint_usage {
+       BPU_NONE = 0,
+       BPU_BREAKPOINT,
+       BPU_WATCHPOINT
+};
+
+struct hardware_breakpoint {
+       enum breakpoint_usage used;
+};
+
 struct dsp563xx_common {
        struct mcu_jtag jtag_info;
        struct reg_cache *core_cache;
@@ -40,6 +50,11 @@ struct dsp563xx_common {
        /* register cache to processor synchronization */
        int (*read_core_reg) (struct target *target, int num);
        int (*write_core_reg) (struct target *target, int num);
+
+       struct hardware_breakpoint hardware_breakpoint[1];
+
+       /*Were the hardware breakpoints cleared on startup?*/
+       int hardware_breakpoints_cleared;
 };
 
 struct dsp563xx_core_reg {
index 193c715..aa8c969 100644 (file)
@@ -209,6 +209,7 @@ int dsp563xx_once_reg_read_ex(struct jtag_tap *tap, int flush, uint8_t reg, uint
                return err;
        if (flush)
                err = jtag_execute_queue();
+
        return err;
 }
 
@@ -225,6 +226,7 @@ int dsp563xx_once_reg_read(struct jtag_tap *tap, int flush, uint8_t reg, uint32_
                return err;
        if (flush)
                err = jtag_execute_queue();
+
        return err;
 }
 
index dcf3bc0..b0be95b 100644 (file)
 
 #include <jtag/jtag.h>
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 #define DSP563XX_ONCE_OCR_EX   (1<<5)
 #define DSP563XX_ONCE_OCR_GO   (1<<6)
 #define DSP563XX_ONCE_OCR_RW   (1<<7)