target/arc: Introduce Actionpoints support
authorEvgeniy Didin <didin@synopsys.com>
Fri, 10 Jul 2020 11:52:35 +0000 (14:52 +0300)
committerAntonio Borneo <borneo.antonio@gmail.com>
Sun, 26 Jul 2020 19:08:21 +0000 (20:08 +0100)
Actionpoint mechanism allows to setup HW breakpoints and watchpoints on Synopsys ARC CPUs.
This mechanism is controlled by DEBUG register and by a set of auxilary registers.
Each actionpoint is controlled by 3 aux registers: Actionpoint(AP) match mask(AP_AMM),
AP match value(AP_AMV) and AP control(AC).

Note: some fields of actionpoint_t structure will be used in further
support of watchpoints.

Change-Id: I4efb24675f247cc19d9122501c9e63c3126fcab4
Signed-off-by: Evgeniy Didin <didin@synopsys.com>
Reviewed-on: http://openocd.zylin.com/5763
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
src/target/arc.c
src/target/arc.h
src/target/arc_cmd.c
tcl/cpu/arc/v2.tcl

index 1ac4a43355419ed756e9ed9df9ee4f89a4f2786d..db338031f704d03a23102b66cf217f0968045fbe 100644 (file)
@@ -603,6 +603,27 @@ static int arc_get_register_value(struct target *target, const char *reg_name,
        return ERROR_OK;
 }
 
+static int arc_set_register_value(struct target *target, const char *reg_name,
+               uint32_t value)
+{
+       LOG_DEBUG("reg_name=%s value=0x%08" PRIx32, reg_name, value);
+
+       if (!(target && reg_name)) {
+               LOG_ERROR("Arguments cannot be NULL.");
+               return ERROR_FAIL;
+       }
+
+       struct reg *reg = arc_reg_get_by_name(target->reg_cache, reg_name, true);
+
+       if (!reg)
+               return ERROR_ARC_REGISTER_NOT_FOUND;
+
+       uint8_t value_buf[4];
+       buf_set_u32(value_buf, 0, 32, value);
+       CHECK_RETVAL(reg->type->set(reg, value_buf));
+
+       return ERROR_OK;
+}
 
 /* Configure DCCM's */
 static int arc_configure_dccm(struct target  *target)
@@ -897,6 +918,44 @@ exit:
        return retval;
 }
 
+/**
+ * Finds an actionpoint that triggered last actionpoint event, as specified by
+ * DEBUG.ASR.
+ *
+ * @param actionpoint Pointer to be set to last active actionpoint. Pointer
+ *                    will be set to NULL if DEBUG.AH is 0.
+ */
+static int get_current_actionpoint(struct target *target,
+               struct arc_actionpoint **actionpoint)
+{
+       assert(target != NULL);
+       assert(actionpoint != NULL);
+
+       uint32_t debug_ah;
+       /* Check if actionpoint caused halt */
+       CHECK_RETVAL(arc_reg_get_field(target, "debug", "ah",
+                               &debug_ah));
+
+       if (debug_ah) {
+               struct arc_common *arc = target_to_arc(target);
+               unsigned int ap;
+               uint32_t debug_asr;
+               CHECK_RETVAL(arc_reg_get_field(target, "debug",
+                                       "asr", &debug_asr));
+
+               for (ap = 0; debug_asr > 1; debug_asr >>= 1)
+                       ap += 1;
+
+               assert(ap < arc->actionpoints_num);
+
+               *actionpoint = &(arc->actionpoints_list[ap]);
+       } else {
+               *actionpoint = NULL;
+       }
+
+       return ERROR_OK;
+}
+
 static int arc_examine_debug_reason(struct target *target)
 {
        uint32_t debug_bh;
@@ -916,8 +975,20 @@ static int arc_examine_debug_reason(struct target *target)
                /* DEBUG.BH is set if core halted due to BRK instruction.  */
                target->debug_reason = DBG_REASON_BREAKPOINT;
        } else {
-               /* TODO: Add Actionpoint check when AP support will be introduced*/
-               LOG_WARNING("Unknown debug reason");
+               struct arc_actionpoint *actionpoint = NULL;
+               CHECK_RETVAL(get_current_actionpoint(target, &actionpoint));
+
+               if (actionpoint != NULL) {
+                       if (!actionpoint->used)
+                               LOG_WARNING("Target halted by an unused actionpoint.");
+
+                       if (actionpoint->type == ARC_AP_BREAKPOINT)
+                               target->debug_reason = DBG_REASON_BREAKPOINT;
+                       else if (actionpoint->type == ARC_AP_WATCHPOINT)
+                               target->debug_reason = DBG_REASON_WATCHPOINT;
+                       else
+                               LOG_WARNING("Unknown type of actionpoint.");
+               }
        }
 
        return ERROR_OK;
@@ -1301,6 +1372,7 @@ static void arc_deinit_target(struct target *target)
        list_for_each_entry_safe(desc, k, &arc->bcr_reg_descriptions, list)
                free_reg_desc(desc);
 
+       free(arc->actionpoints_list);
        free(arc);
 }
 
@@ -1377,10 +1449,54 @@ int arc_read_instruction_u32(struct target *target, uint32_t address,
        return ERROR_OK;
 }
 
+/* Actionpoint mechanism allows to setup HW breakpoints
+ * and watchpoints. Each actionpoint is controlled by
+ * 3 aux registers: Actionpoint(AP) match mask(AP_AMM), AP match value(AP_AMV)
+ * and AP control(AC).
+ * This function is for setting/unsetting actionpoints:
+ * at - actionpoint target: trigger on mem/reg access
+ * tt - transaction type : trigger on r/w. */
+static int arc_configure_actionpoint(struct target *target, uint32_t ap_num,
+       uint32_t match_value, uint32_t control_tt, uint32_t control_at)
+{
+       struct arc_common *arc = target_to_arc(target);
+
+       if (control_tt != AP_AC_TT_DISABLE) {
+
+               if (arc->actionpoints_num_avail < 1) {
+                       LOG_ERROR("No free actionpoints, maximim amount is %" PRIu32,
+                                       arc->actionpoints_num);
+                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               }
+
+               /* Names of register to set - 24 chars should be enough. Looks a little
+                * bit out-of-place for C code, but makes it aligned to the bigger
+                * concept of "ARC registers are defined in TCL" as far as possible.
+                */
+               char ap_amv_reg_name[24], ap_amm_reg_name[24], ap_ac_reg_name[24];
+               snprintf(ap_amv_reg_name, 24, "ap_amv%" PRIu32, ap_num);
+               snprintf(ap_amm_reg_name, 24, "ap_amm%" PRIu32, ap_num);
+               snprintf(ap_ac_reg_name, 24, "ap_ac%" PRIu32, ap_num);
+               CHECK_RETVAL(arc_set_register_value(target, ap_amv_reg_name,
+                                        match_value));
+               CHECK_RETVAL(arc_set_register_value(target, ap_amm_reg_name, 0));
+               CHECK_RETVAL(arc_set_register_value(target, ap_ac_reg_name,
+                                        control_tt | control_at));
+               arc->actionpoints_num_avail--;
+       } else {
+               char ap_ac_reg_name[24];
+               snprintf(ap_ac_reg_name, 24, "ap_ac%" PRIu32, ap_num);
+               CHECK_RETVAL(arc_set_register_value(target, ap_ac_reg_name,
+                                        AP_AC_TT_DISABLE));
+               arc->actionpoints_num_avail++;
+       }
+
+       return ERROR_OK;
+}
+
 static int arc_set_breakpoint(struct target *target,
                struct breakpoint *breakpoint)
 {
-
        if (breakpoint->set) {
                LOG_WARNING("breakpoint already set");
                return ERROR_OK;
@@ -1425,8 +1541,34 @@ static int arc_set_breakpoint(struct target *target,
 
                breakpoint->set = 64; /* Any nice value but 0 */
        } else if (breakpoint->type == BKPT_HARD) {
-               LOG_DEBUG("Hardware breakpoints are not supported yet!");
-               return ERROR_FAIL;
+               struct arc_common *arc = target_to_arc(target);
+               struct arc_actionpoint *ap_list = arc->actionpoints_list;
+               unsigned int bp_num;
+
+               for (bp_num = 0; bp_num < arc->actionpoints_num; bp_num++) {
+                       if (!ap_list[bp_num].used)
+                               break;
+               }
+
+               if (bp_num >= arc->actionpoints_num) {
+                       LOG_ERROR("No free actionpoints, maximum amount is %" PRIu32,
+                                       arc->actionpoints_num);
+                       return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               }
+
+               int retval = arc_configure_actionpoint(target, bp_num,
+                               breakpoint->address, AP_AC_TT_READWRITE, AP_AC_AT_INST_ADDR);
+
+               if (retval == ERROR_OK) {
+                       breakpoint->set = bp_num + 1;
+                       ap_list[bp_num].used = 1;
+                       ap_list[bp_num].bp_value = breakpoint->address;
+                       ap_list[bp_num].type = ARC_AP_BREAKPOINT;
+
+                       LOG_DEBUG("bpid: %" PRIu32 ", bp_num %u bp_value 0x%" PRIx32,
+                                       breakpoint->unique_id, bp_num, ap_list[bp_num].bp_value);
+               }
+
        } else {
                LOG_DEBUG("ERROR: setting unknown breakpoint type");
                return ERROR_FAIL;
@@ -1491,8 +1633,27 @@ static int arc_unset_breakpoint(struct target *target,
                breakpoint->set = 0;
 
        }       else if (breakpoint->type == BKPT_HARD) {
-                       LOG_WARNING("Hardware breakpoints are not supported yet!");
-                       return ERROR_FAIL;
+               struct arc_common *arc = target_to_arc(target);
+               struct arc_actionpoint *ap_list = arc->actionpoints_list;
+               unsigned int bp_num = breakpoint->set - 1;
+
+               if ((breakpoint->set == 0) || (bp_num >= arc->actionpoints_num)) {
+                       LOG_DEBUG("Invalid actionpoint ID: %u in breakpoint: %" PRIu32,
+                                         bp_num, breakpoint->unique_id);
+                       return ERROR_OK;
+               }
+
+               retval = arc_configure_actionpoint(target, bp_num,
+                                               breakpoint->address, AP_AC_TT_DISABLE, AP_AC_AT_INST_ADDR);
+
+               if (retval == ERROR_OK) {
+                       breakpoint->set = 0;
+                       ap_list[bp_num].used = 0;
+                       ap_list[bp_num].bp_value = 0;
+
+                       LOG_DEBUG("bpid: %" PRIu32 " - released actionpoint ID: %i",
+                                       breakpoint->unique_id, bp_num);
+               }
        } else {
                        LOG_DEBUG("ERROR: unsetting unknown breakpoint type");
                        return ERROR_FAIL;
@@ -1530,6 +1691,115 @@ static int arc_remove_breakpoint(struct target *target,
        return ERROR_OK;
 }
 
+void arc_reset_actionpoints(struct target *target)
+{
+       struct arc_common *arc = target_to_arc(target);
+       struct arc_actionpoint *ap_list = arc->actionpoints_list;
+       struct breakpoint *next_b;
+
+       while (target->breakpoints) {
+               next_b = target->breakpoints->next;
+               arc_remove_breakpoint(target, target->breakpoints);
+               free(target->breakpoints->orig_instr);
+               free(target->breakpoints);
+               target->breakpoints = next_b;
+       }
+       for (unsigned int i = 0; i < arc->actionpoints_num; i++) {
+               if ((ap_list[i].used) && (ap_list[i].reg_address))
+                       arc_remove_auxreg_actionpoint(target, ap_list[i].reg_address);
+       }
+}
+
+int arc_set_actionpoints_num(struct target *target, uint32_t ap_num)
+{
+       LOG_DEBUG("target=%s actionpoints=%" PRIu32, target_name(target), ap_num);
+       struct arc_common *arc = target_to_arc(target);
+
+       /* Make sure that there are no enabled actionpoints in target. */
+       arc_reset_actionpoints(target);
+
+       /* Assume that all points have been removed from target.  */
+       free(arc->actionpoints_list);
+
+       arc->actionpoints_num_avail = ap_num;
+       arc->actionpoints_num = ap_num;
+       /* calloc can be safely called when ncount == 0.  */
+       arc->actionpoints_list = calloc(ap_num, sizeof(struct arc_actionpoint));
+
+       if (!arc->actionpoints_list) {
+               LOG_ERROR("Unable to allocate memory");
+               return ERROR_FAIL;
+       }
+       return ERROR_OK;
+}
+
+
+int arc_add_auxreg_actionpoint(struct target *target,
+       uint32_t auxreg_addr, uint32_t transaction)
+{
+       unsigned int ap_num = 0;
+       int retval = ERROR_OK;
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       struct arc_common *arc = target_to_arc(target);
+       struct arc_actionpoint *ap_list = arc->actionpoints_list;
+
+       while (ap_list[ap_num].used)
+               ap_num++;
+
+       if (ap_num >= arc->actionpoints_num) {
+               LOG_ERROR("No actionpoint free, maximum amount is %u",
+                               arc->actionpoints_num);
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       retval =  arc_configure_actionpoint(target, ap_num,
+                       auxreg_addr, transaction, AP_AC_AT_AUXREG_ADDR);
+
+       if (retval == ERROR_OK) {
+               ap_list[ap_num].used = 1;
+               ap_list[ap_num].reg_address = auxreg_addr;
+       }
+
+       return retval;
+}
+
+int arc_remove_auxreg_actionpoint(struct target *target, uint32_t auxreg_addr)
+{
+       int retval = ERROR_OK;
+       bool ap_found = false;
+       unsigned int ap_num = 0;
+
+       if (target->state != TARGET_HALTED)
+               return ERROR_TARGET_NOT_HALTED;
+
+       struct arc_common *arc = target_to_arc(target);
+       struct arc_actionpoint *ap_list = arc->actionpoints_list;
+
+       while ((ap_list[ap_num].used) && (ap_num < arc->actionpoints_num)) {
+               if (ap_list[ap_num].reg_address == auxreg_addr) {
+                       ap_found = true;
+                       break;
+               }
+               ap_num++;
+       }
+
+       if (ap_found) {
+               retval =  arc_configure_actionpoint(target, ap_num,
+                               auxreg_addr, AP_AC_TT_DISABLE, AP_AC_AT_AUXREG_ADDR);
+
+               if (retval == ERROR_OK) {
+                       ap_list[ap_num].used = 0;
+                       ap_list[ap_num].bp_value = 0;
+               }
+       } else {
+               LOG_ERROR("Register actionpoint not found");
+       }
+       return retval;
+}
+
 /* Helper function which swiches core to single_step mode by
  * doing aux r/w operations.  */
 int arc_config_step(struct target *target, int enable_step)
index 66414115976cb71dc48db6281cd98238a03d3bf7..f9ee5b45e1f447b473254c2c8cce98ca738cc3f9 100644 (file)
 #define SLC_AUX_CACHE_INV              0x905
 #define L2_INV_IV                      BIT(0)
 
+ /* Action Point */
+#define AP_AC_AT_INST_ADDR             0x0
+#define AP_AC_AT_MEMORY_ADDR   0x2
+#define AP_AC_AT_AUXREG_ADDR   0x4
+
+#define AP_AC_TT_DISABLE               0x00
+#define AP_AC_TT_WRITE                 0x10
+#define AP_AC_TT_READ                  0x20
+#define AP_AC_TT_READWRITE             0x30
+
 struct arc_reg_bitfield {
        struct reg_data_type_bitfield bitfield;
        char name[REG_TYPE_MAX_NAME_LENGTH];
@@ -96,8 +106,6 @@ struct arc_reg_data_type {
        };
 };
 
-
-
 /* Standard GDB register types */
 static const struct reg_data_type standard_gdb_types[] = {
        { .type = REG_TYPE_INT,         .id = "int" },
@@ -118,6 +126,18 @@ static const struct reg_data_type standard_gdb_types[] = {
        { .type = REG_TYPE_IEEE_DOUBLE, .id = "ieee_double" },
 };
 
+enum arc_actionpointype {
+       ARC_AP_BREAKPOINT,
+       ARC_AP_WATCHPOINT,
+};
+
+/* Actionpoint related fields  */
+struct arc_actionpoint {
+       int used;
+       uint32_t bp_value;
+       uint32_t reg_address;
+       enum arc_actionpointype type;
+};
 
 struct arc_common {
        uint32_t common_magic;
@@ -172,6 +192,11 @@ struct arc_common {
        unsigned long pc_index_in_cache;
        /* DEBUG register location in register cache. */
        unsigned long debug_index_in_cache;
+
+       /* Actionpoints */
+       unsigned int actionpoints_num;
+       unsigned int actionpoints_num_avail;
+       struct arc_actionpoint *actionpoints_list;
 };
 
 /* Borrowed from nds32.h */
@@ -284,4 +309,9 @@ int arc_reg_get_field(struct target *target, const char *reg_name,
 int arc_cache_flush(struct target *target);
 int arc_cache_invalidate(struct target *target);
 
+int arc_add_auxreg_actionpoint(struct target *target,
+       uint32_t auxreg_addr, uint32_t transaction);
+int arc_remove_auxreg_actionpoint(struct target *target, uint32_t auxreg_addr);
+int arc_set_actionpoints_num(struct target *target, uint32_t ap_num);
+
 #endif /* OPENOCD_TARGET_ARC_H */
index 59e1645d67cbfebf9c1db1caa8e8f2fc29fef834..a1d5a0936a238a7c41473b6749bea16fe4d246e1 100644 (file)
@@ -929,6 +929,50 @@ COMMAND_HANDLER(arc_l2_cache_disable_auto_cmd)
                &arc->has_l2cache, "target has l2 cache enabled");
 }
 
+static int jim_handle_actionpoints_num(Jim_Interp *interp, int argc,
+       Jim_Obj * const *argv)
+{
+       Jim_GetOptInfo goi;
+       Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
+
+       LOG_DEBUG("-");
+
+       if (goi.argc >= 2) {
+               Jim_WrongNumArgs(interp, goi.argc, goi.argv, "[<unsigned integer>]");
+               return JIM_ERR;
+       }
+
+       struct command_context *context = current_command_context(interp);
+       assert(context);
+
+       struct target *target = get_current_target(context);
+
+       if (!target) {
+               Jim_SetResultFormatted(goi.interp, "No current target");
+               return JIM_ERR;
+       }
+
+       struct arc_common *arc = target_to_arc(target);
+       /* It is not possible to pass &arc->actionpoints_num directly to
+        * handle_command_parse_uint, because this value should be valid during
+        * "actionpoint reset, initiated by arc_set_actionpoints_num.  */
+       uint32_t ap_num = arc->actionpoints_num;
+
+       if (goi.argc == 1) {
+               JIM_CHECK_RETVAL(arc_cmd_jim_get_uint32(&goi, &ap_num));
+               int e = arc_set_actionpoints_num(target, ap_num);
+               if (e != ERROR_OK) {
+                       Jim_SetResultFormatted(goi.interp,
+                               "Failed to set number of actionpoints");
+                       return JIM_ERR;
+               }
+       }
+
+       Jim_SetResultInt(interp, ap_num);
+
+       return JIM_OK;
+}
+
 /* ----- Exported target commands ------------------------------------------ */
 
 const struct command_registration arc_l2_cache_group_handlers[] = {
@@ -1024,6 +1068,13 @@ static const struct command_registration arc_core_command_handlers[] = {
                .usage = "",
                .chain = arc_cache_group_handlers,
        },
+       {
+               .name = "num-actionpoints",
+               .jim_handler = jim_handle_actionpoints_num,
+               .mode = COMMAND_ANY,
+               .usage = "[<unsigned integer>]",
+               .help = "Prints or sets amount of actionpoints in the processor.",
+       },
        COMMAND_REGISTRATION_DONE
 };
 
index ad55361a5482dcb65a0ae26da7ddf3ee607d5b76..a3172c220a1d918b20aae8286db775eaa9b98bfb 100644 (file)
@@ -30,6 +30,32 @@ proc arc_v2_examine_target { {target ""} } {
                r13 r14 r15 r16 r17 r18 r19 r20 r21 r22 r23 r24 r25 \
                gp fp sp ilink r30 blink lp_count pcl
 
+       # Actionpoints
+       if { [arc get-reg-field ap_build version] == 5 } {
+               set ap_build_type [arc get-reg-field ap_build type]
+               # AP_BUILD.TYPE > 0b0110 is reserved in current ISA.
+               # Current ISA supports up to 8 actionpoints.
+               if { $ap_build_type < 8 } {
+                       # Two LSB bits of AP_BUILD.TYPE define amount of actionpoints:
+                       # 0b00 - 2 actionpoints
+                       # 0b01 - 4 actionpoints
+                       # 0b10 - 8 actionpoints
+                       # 0b11 - reserved.
+                       set ap_num [expr 0x2 << ($ap_build_type & 3)]
+                       # Expression on top may produce 16 action points - which is a
+                       # reserved value for now.
+                       if { $ap_num < 16 } {
+                               # Enable actionpoint registers
+                               for {set i 0} {$i < $ap_num} {incr i} {
+                                       arc set-reg-exists ap_amv$i ap_amm$i ap_ac$i
+                               }
+
+                               # Set amount of actionpoints
+                               arc num-actionpoints $ap_num
+                       }
+               }
+       }
+
        # DCCM
        set dccm_version [arc get-reg-field dccm_build version]
        if { $dccm_version == 3 || $dccm_version == 4 } {
@@ -213,6 +239,30 @@ proc arc_v2_init_regs { } {
                0x018 aux_dccm  int
                0x208 aux_iccm  int
 
+               0x220 ap_amv0   uint32
+               0x221 ap_amm0   uint32
+               0x222 ap_ac0    ap_control_t
+               0x223 ap_amv1   uint32
+               0x224 ap_amm1   uint32
+               0x225 ap_ac1    ap_control_t
+               0x226 ap_amv2   uint32
+               0x227 ap_amm2   uint32
+               0x228 ap_ac2    ap_control_t
+               0x229 ap_amv3   uint32
+               0x22A ap_amm3   uint32
+               0x22B ap_ac3    ap_control_t
+               0x22C ap_amv4   uint32
+               0x22D ap_amm4   uint32
+               0x22E ap_ac4    ap_control_t
+               0x22F ap_amv5   uint32
+               0x230 ap_amm5   uint32
+               0x231 ap_ac5    ap_control_t
+               0x232 ap_amv6   uint32
+               0x233 ap_amm6   uint32
+               0x234 ap_ac6    ap_control_t
+               0x235 ap_amv7   uint32
+               0x236 ap_amm7   uint32
+               0x237 ap_ac7    ap_control_t
 
                0x400 eret              code_ptr
                0x401 erbta             code_ptr
@@ -285,4 +335,12 @@ proc arc_v2_init_regs { } {
 
 proc arc_v2_reset { {target ""} } {
        arc_common_reset $target
+
+       # Disable all actionpoints.  Cannot write via regcache yet, because it will
+       # not be flushed and all changes to registers will get lost.  Therefore has
+       # to write directly via JTAG layer...
+       set num_ap [arc num-actionpoints]
+       for {set i 0} {$i < $num_ap} {incr i} {
+               arc jtag set-aux-reg [expr 0x222 + $i * 3] 0
+       }
 }