diff --git a/src/target/dsp563xx.c b/src/target/dsp563xx.c index 4dfb66edf..1211ac562 100644 --- a/src/target/dsp563xx.c +++ b/src/target/dsp563xx.c @@ -25,6 +25,7 @@ #include #include "target.h" +#include "breakpoints.h" #include "target_type.h" #include "algorithm.h" #include "register.h" @@ -94,6 +95,29 @@ #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 }; diff --git a/src/target/dsp563xx.h b/src/target/dsp563xx.h index b2ee9d1f6..446ad3f53 100644 --- a/src/target/dsp563xx.h +++ b/src/target/dsp563xx.h @@ -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 { diff --git a/src/target/dsp563xx_once.c b/src/target/dsp563xx_once.c index 193c7152c..aa8c96910 100644 --- a/src/target/dsp563xx_once.c +++ b/src/target/dsp563xx_once.c @@ -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; } diff --git a/src/target/dsp563xx_once.h b/src/target/dsp563xx_once.h index dcf3bc055..b0be95bd1 100644 --- a/src/target/dsp563xx_once.h +++ b/src/target/dsp563xx_once.h @@ -23,6 +23,10 @@ #include +#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)