+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 (%" PRIu32 ")", 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);
+}
+