#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,
MEM_L = 3,
};
+enum watchpoint_condition {
+ EQUAL,
+ NOT_EQUAL,
+ GREATER,
+ LESS_THAN
+};
+
#define INSTR_JUMP 0x0AF080
/* Effective Addressing Mode Encoding */
#define EAME_R0 0x10
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;
}
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;
}
}
+ 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;
}
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,
}
}
+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);
.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
};