From: Evgeniy Didin Date: Fri, 10 Jul 2020 11:52:35 +0000 (+0300) Subject: target/arc: Introduce Actionpoints support X-Git-Tag: v0.11.0-rc1~191 X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=commitdiff_plain;h=8fea8460dbc6ca23e34a16898e86231daab0594d target/arc: Introduce Actionpoints support 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 Reviewed-on: http://openocd.zylin.com/5763 Tested-by: jenkins Reviewed-by: Antonio Borneo --- diff --git a/src/target/arc.c b/src/target/arc.c index 1ac4a43355..db338031f7 100644 --- a/src/target/arc.c +++ b/src/target/arc.c @@ -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) diff --git a/src/target/arc.h b/src/target/arc.h index 6641411597..f9ee5b45e1 100644 --- a/src/target/arc.h +++ b/src/target/arc.h @@ -78,6 +78,16 @@ #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 */ diff --git a/src/target/arc_cmd.c b/src/target/arc_cmd.c index 59e1645d67..a1d5a0936a 100644 --- a/src/target/arc_cmd.c +++ b/src/target/arc_cmd.c @@ -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, "[]"); + 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 = "[]", + .help = "Prints or sets amount of actionpoints in the processor.", + }, COMMAND_REGISTRATION_DONE }; diff --git a/tcl/cpu/arc/v2.tcl b/tcl/cpu/arc/v2.tcl index ad55361a54..a3172c220a 100644 --- a/tcl/cpu/arc/v2.tcl +++ b/tcl/cpu/arc/v2.tcl @@ -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 + } }