Improve cortex_m3 command argument parsing.
[openocd.git] / src / target / cortex_m3.c
index e854f6b6560c174dff1aa227e5f7c9f0e3bc3ff2..a1d1cbc6fc29631961f901a390952c224ca85f26 100644 (file)
@@ -52,6 +52,12 @@ extern uint8_t armv7m_gdb_dummy_cpsr_value[];
 extern reg_t armv7m_gdb_dummy_cpsr_reg;
 #endif
 
+static int cortex_m3_has_mmu(struct target_s *target, bool *has_mmu)
+{
+       *has_mmu = false;
+       return ERROR_OK;
+}
+
 static int cortexm3_dap_read_coreregister_u32(swjdp_common_t *swjdp,
                uint32_t *value, int regnum)
 {
@@ -217,9 +223,12 @@ static int cortex_m3_endreset_event(target_t *target)
        /* Restore DWT registers */
        for (i = 0; i < cortex_m3->dwt_num_comp; i++)
        {
-               target_write_u32(target, dwt_list[i].dwt_comparator_address, dwt_list[i].comp);
-               target_write_u32(target, dwt_list[i].dwt_comparator_address | 0x4, dwt_list[i].mask);
-               target_write_u32(target, dwt_list[i].dwt_comparator_address | 0x8, dwt_list[i].function);
+               target_write_u32(target, dwt_list[i].dwt_comparator_address + 0,
+                               dwt_list[i].comp);
+               target_write_u32(target, dwt_list[i].dwt_comparator_address + 4,
+                               dwt_list[i].mask);
+               target_write_u32(target, dwt_list[i].dwt_comparator_address + 8,
+                               dwt_list[i].function);
        }
        swjdp_transaction_endcheck(swjdp);
 
@@ -253,7 +262,7 @@ static int cortex_m3_examine_debug_reason(target_t *target)
                        target->debug_reason = DBG_REASON_WATCHPOINT;
                else if (cortex_m3->nvic_dfsr & DFSR_VCATCH)
                        target->debug_reason = DBG_REASON_BREAKPOINT;
-               else /* EXTERNAL, HALTED, DWTTRAP w/o BKPT */
+               else /* EXTERNAL, HALTED */
                        target->debug_reason = DBG_REASON_UNDEFINED;
        }
 
@@ -615,7 +624,7 @@ static int cortex_m3_resume(struct target_s *target, int current,
                {
                        LOG_DEBUG("unset breakpoint at 0x%8.8" PRIx32 " (ID: %d)",
                                          breakpoint->address,
-                                         breakpoint->unique_id );
+                                         breakpoint->unique_id);
                        cortex_m3_unset_breakpoint(target, breakpoint);
                        cortex_m3_single_step_core(target);
                        cortex_m3_set_breakpoint(target, breakpoint);
@@ -1030,6 +1039,7 @@ cortex_m3_remove_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
        armv7m_common_t *armv7m = target->arch_info;
        cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
 
+       /* REVISIT why check? FBP can be updated with core running ... */
        if (target->state != TARGET_HALTED)
        {
                LOG_WARNING("target not halted");
@@ -1061,52 +1071,63 @@ cortex_m3_set_watchpoint(struct target_s *target, watchpoint_t *watchpoint)
        /* get pointers to arch-specific information */
        armv7m_common_t *armv7m = target->arch_info;
        cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
-       cortex_m3_dwt_comparator_t * comparator_list = cortex_m3->dwt_comparator_list;
 
-       if (watchpoint->set)
-       {
-               LOG_WARNING("watchpoint (%d) already set", watchpoint->unique_id );
-               return ERROR_OK;
+       /* watchpoint params were validated earlier */
+       mask = 0;
+       temp = watchpoint->length;
+       while (temp) {
+               temp >>= 1;
+               mask++;
        }
+       mask--;
 
-       if (watchpoint->mask == 0xffffffffu)
+       /* REVISIT Don't fully trust these "not used" records ... users
+        * may set up breakpoints by hand, e.g. dual-address data value
+        * watchpoint using comparator #1; comparator #0 matching cycle
+        * count; send data trace info through ITM and TPIU; etc
+        */
+       cortex_m3_dwt_comparator_t *comparator;
+
+       for (comparator = cortex_m3->dwt_comparator_list;
+                       comparator->used && dwt_num < cortex_m3->dwt_num_comp;
+                       comparator++, dwt_num++)
+               continue;
+       if (dwt_num >= cortex_m3->dwt_num_comp)
        {
-               while (comparator_list[dwt_num].used && (dwt_num < cortex_m3->dwt_num_comp))
-                       dwt_num++;
-               if (dwt_num >= cortex_m3->dwt_num_comp)
-               {
-                       LOG_DEBUG("ERROR Can not find free DWT Comparator");
-                       LOG_WARNING("ERROR Can not find free DWT Comparator");
-                       return -1;
-               }
-               watchpoint->set = dwt_num + 1;
-               mask = 0;
-               temp = watchpoint->length;
-               while (temp > 1)
-               {
-                       temp = temp / 2;
-                       mask++;
-               }
-               comparator_list[dwt_num].used = 1;
-               comparator_list[dwt_num].comp = watchpoint->address;
-               comparator_list[dwt_num].mask = mask;
-               comparator_list[dwt_num].function = watchpoint->rw + 5;
-               target_write_u32(target, comparator_list[dwt_num].dwt_comparator_address, comparator_list[dwt_num].comp);
-               target_write_u32(target, comparator_list[dwt_num].dwt_comparator_address | 0x4, comparator_list[dwt_num].mask);
-               target_write_u32(target, comparator_list[dwt_num].dwt_comparator_address | 0x8, comparator_list[dwt_num].function);
-               LOG_DEBUG("dwt_num %i 0x%" PRIx32 " 0x%" PRIx32 " 0x%" PRIx32 "", dwt_num, comparator_list[dwt_num].comp, comparator_list[dwt_num].mask, comparator_list[dwt_num].function);
+               LOG_ERROR("Can not find free DWT Comparator");
+               return ERROR_FAIL;
        }
-       else
-       {
-               /* Move this test to add_watchpoint */
-               LOG_WARNING("Cannot watch data values (id: %d)",
-                                 watchpoint->unique_id );
-               return ERROR_OK;
+       comparator->used = 1;
+       watchpoint->set = dwt_num + 1;
+
+       comparator->comp = watchpoint->address;
+       target_write_u32(target, comparator->dwt_comparator_address + 0,
+                       comparator->comp);
+
+       comparator->mask = mask;
+       target_write_u32(target, comparator->dwt_comparator_address + 4,
+                       comparator->mask);
+
+       switch (watchpoint->rw) {
+       case WPT_READ:
+               comparator->function = 5;
+               break;
+       case WPT_WRITE:
+               comparator->function = 6;
+               break;
+       case WPT_ACCESS:
+               comparator->function = 7;
+               break;
        }
-       LOG_DEBUG("Watchpoint (ID: %d) address: 0x%08" PRIx32 " set=%d ",
-                         watchpoint->unique_id, watchpoint->address, watchpoint->set );
-       return ERROR_OK;
+       target_write_u32(target, comparator->dwt_comparator_address + 8,
+                       comparator->function);
 
+       LOG_DEBUG("Watchpoint (ID %d) DWT%d 0x%08x 0x%x 0x%05x",
+                       watchpoint->unique_id, dwt_num,
+                       (unsigned) comparator->comp,
+                       (unsigned) comparator->mask,
+                       (unsigned) comparator->function);
+       return ERROR_OK;
 }
 
 static int
@@ -1115,28 +1136,33 @@ cortex_m3_unset_watchpoint(struct target_s *target, watchpoint_t *watchpoint)
        /* get pointers to arch-specific information */
        armv7m_common_t *armv7m = target->arch_info;
        cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
-       cortex_m3_dwt_comparator_t * comparator_list = cortex_m3->dwt_comparator_list;
+       cortex_m3_dwt_comparator_t *comparator;
        int dwt_num;
 
        if (!watchpoint->set)
        {
-               LOG_WARNING("watchpoint (wpid: %d) not set", watchpoint->unique_id );
+               LOG_WARNING("watchpoint (wpid: %d) not set",
+                               watchpoint->unique_id);
                return ERROR_OK;
        }
 
-       LOG_DEBUG("Watchpoint (ID: %d) address: 0x%08" PRIx32 " set=%d ",
-                         watchpoint->unique_id, watchpoint->address,watchpoint->set );
-
        dwt_num = watchpoint->set - 1;
 
+       LOG_DEBUG("Watchpoint (ID %d) DWT%d address: 0x%08x clear",
+                       watchpoint->unique_id, dwt_num,
+                       (unsigned) watchpoint->address);
+
        if ((dwt_num < 0) || (dwt_num >= cortex_m3->dwt_num_comp))
        {
                LOG_DEBUG("Invalid DWT Comparator number in watchpoint");
                return ERROR_OK;
        }
-       comparator_list[dwt_num].used = 0;
-       comparator_list[dwt_num].function = 0;
-       target_write_u32(target, comparator_list[dwt_num].dwt_comparator_address | 0x8, comparator_list[dwt_num].function);
+
+       comparator = cortex_m3->dwt_comparator_list + dwt_num;
+       comparator->used = 0;
+       comparator->function = 0;
+       target_write_u32(target, comparator->dwt_comparator_address + 8,
+                       comparator->function);
 
        watchpoint->set = 0;
 
@@ -1150,6 +1176,7 @@ cortex_m3_add_watchpoint(struct target_s *target, watchpoint_t *watchpoint)
        armv7m_common_t *armv7m = target->arch_info;
        cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
 
+       /* REVISIT why check? DWT can be updated with core running ... */
        if (target->state != TARGET_HALTED)
        {
                LOG_WARNING("target not halted");
@@ -1158,11 +1185,41 @@ cortex_m3_add_watchpoint(struct target_s *target, watchpoint_t *watchpoint)
 
        if (cortex_m3->dwt_comp_available < 1)
        {
+               LOG_DEBUG("no comparators?");
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
 
-       if ((watchpoint->length != 1) && (watchpoint->length != 2) && (watchpoint->length != 4))
-       {
+       /* hardware doesn't support data value masking */
+       if (watchpoint->mask != ~(uint32_t)0) {
+               LOG_DEBUG("watchpoint value masks not supported");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       /* hardware allows address masks of up to 32K */
+       unsigned mask;
+
+       for (mask = 0; mask < 16; mask++) {
+               if ((1u << mask) == watchpoint->length)
+                       break;
+       }
+       if (mask == 16) {
+               LOG_DEBUG("unsupported watchpoint length");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+       if (watchpoint->address & ((1 << mask) - 1)) {
+               LOG_DEBUG("watchpoint address is unaligned");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       /* Caller doesn't seem to be able to describe watching for data
+        * values of zero; that flags "no value".
+        *
+        * REVISIT This DWT may well be able to watch for specific data
+        * values.  Requires comparator #1 to set DATAVMATCH and match
+        * the data, and another comparator (DATAVADDR0) matching addr.
+        */
+       if (watchpoint->value) {
+               LOG_DEBUG("data value watchpoint not YET supported");
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
 
@@ -1179,6 +1236,7 @@ cortex_m3_remove_watchpoint(struct target_s *target, watchpoint_t *watchpoint)
        armv7m_common_t *armv7m = target->arch_info;
        cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
 
+       /* REVISIT why check? DWT can be updated with core running ... */
        if (target->state != TARGET_HALTED)
        {
                LOG_WARNING("target not halted");
@@ -1422,22 +1480,157 @@ static int cortex_m3_bulk_write_memory(target_t *target, uint32_t address,
        return cortex_m3_write_memory(target, address, 4, count, buffer);
 }
 
-static void cortex_m3_build_reg_cache(target_t *target)
+static int cortex_m3_init_target(struct command_context_s *cmd_ctx,
+               struct target_s *target)
 {
        armv7m_build_reg_cache(target);
+       return ERROR_OK;
 }
 
-static int cortex_m3_init_target(struct command_context_s *cmd_ctx,
-               struct target_s *target)
+/* REVISIT cache valid/dirty bits are unmaintained.  We could set "valid"
+ * on r/w if the core is not running, and clear on resume or reset ... or
+ * at least, in a post_restore_context() method.
+ */
+
+struct dwt_reg_state {
+       struct target_s *target;
+       uint32_t        addr;
+       uint32_t        value;  /* scratch/cache */
+};
+
+static int cortex_m3_dwt_get_reg(struct reg_s *reg)
 {
-       cortex_m3_build_reg_cache(target);
-       return ERROR_OK;
+       struct dwt_reg_state *state = reg->arch_info;
+
+       return target_read_u32(state->target, state->addr, &state->value);
+}
+
+static int cortex_m3_dwt_set_reg(struct reg_s *reg, uint8_t *buf)
+{
+       struct dwt_reg_state *state = reg->arch_info;
+
+       return target_write_u32(state->target, state->addr,
+                       buf_get_u32(buf, 0, reg->size));
+}
+
+struct dwt_reg {
+       uint32_t        addr;
+       char            *name;
+       unsigned        size;
+};
+
+static struct dwt_reg dwt_base_regs[] = {
+       { DWT_CTRL, "dwt_ctrl", 32, },
+       { DWT_CYCCNT, "dwt_cyccnt", 32, },
+       /* plus some 8 bit counters, useful for profiling with TPIU */
+};
+
+static struct dwt_reg dwt_comp[] = {
+#define DWT_COMPARATOR(i) \
+               { DWT_COMP0 + 0x10 * (i), "dwt_" #i "_comp", 32, }, \
+               { DWT_MASK0 + 0x10 * (i), "dwt_" #i "_mask", 4, }, \
+               { DWT_FUNCTION0 + 0x10 * (i), "dwt_" #i "_function", 32, }
+       DWT_COMPARATOR(0),
+       DWT_COMPARATOR(1),
+       DWT_COMPARATOR(2),
+       DWT_COMPARATOR(3),
+#undef DWT_COMPARATOR
+};
+
+static int dwt_reg_type = -1;
+
+static void
+cortex_m3_dwt_addreg(struct target_s *t, struct reg_s *r, struct dwt_reg *d)
+{
+       struct dwt_reg_state *state;
+
+       state = calloc(1, sizeof *state);
+       if (!state)
+               return;
+       state->addr = d->addr;
+       state->target = t;
+
+       r->name = d->name;
+       r->size = d->size;
+       r->value = &state->value;
+       r->arch_info = state;
+       r->arch_type = dwt_reg_type;
+}
+
+static void
+cortex_m3_dwt_setup(cortex_m3_common_t *cm3, struct target_s *target)
+{
+       uint32_t dwtcr;
+       struct reg_cache_s *cache;
+       cortex_m3_dwt_comparator_t *comparator;
+       int reg, i;
+
+       target_read_u32(target, DWT_CTRL, &dwtcr);
+       if (!dwtcr) {
+               LOG_DEBUG("no DWT");
+               return;
+       }
+
+       if (dwt_reg_type < 0)
+               dwt_reg_type = register_reg_arch_type(cortex_m3_dwt_get_reg,
+                               cortex_m3_dwt_set_reg);
+
+       cm3->dwt_num_comp = (dwtcr >> 28) & 0xF;
+       cm3->dwt_comp_available = cm3->dwt_num_comp;
+       cm3->dwt_comparator_list = calloc(cm3->dwt_num_comp,
+                       sizeof(cortex_m3_dwt_comparator_t));
+       if (!cm3->dwt_comparator_list) {
+fail0:
+               cm3->dwt_num_comp = 0;
+               LOG_ERROR("out of mem");
+               return;
+       }
+
+       cache = calloc(1, sizeof *cache);
+       if (!cache) {
+fail1:
+               free(cm3->dwt_comparator_list);
+               goto fail0;
+       }
+       cache->name = "cortex-m3 dwt registers";
+       cache->num_regs = 2 + cm3->dwt_num_comp * 3;
+       cache->reg_list = calloc(cache->num_regs, sizeof *cache->reg_list);
+       if (!cache->reg_list) {
+               free(cache);
+               goto fail1;
+       }
+
+       for (reg = 0; reg < 2; reg++)
+               cortex_m3_dwt_addreg(target, cache->reg_list + reg,
+                               dwt_base_regs + reg);
+
+       comparator = cm3->dwt_comparator_list;
+       for (i = 0; i < cm3->dwt_num_comp; i++, comparator++) {
+               int j;
+
+               comparator->dwt_comparator_address = DWT_COMP0 + 0x10 * i;
+               for (j = 0; j < 3; j++, reg++)
+                       cortex_m3_dwt_addreg(target, cache->reg_list + reg,
+                                       dwt_comp + 3 * i + j);
+       }
+
+       *register_get_last_cache_p(&target->reg_cache) = cache;
+       cm3->dwt_cache = cache;
+
+       LOG_INFO("DWT dwtcr 0x%" PRIx32 ", comp %d, watch%s",
+                       dwtcr, cm3->dwt_num_comp,
+                       (dwtcr & (0xf << 24)) ? " only" : "/trigger");
+
+       /* REVISIT:  if num_comp > 1, check whether comparator #1 can
+        * implement single-address data value watchpoints ... so we
+        * won't need to check it later, when asked to set one up.
+        */
 }
 
 static int cortex_m3_examine(struct target_s *target)
 {
        int retval;
-       uint32_t cpuid, fpcr, dwtcr, ictr;
+       uint32_t cpuid, fpcr;
        int i;
 
        /* get pointers to arch-specific information */
@@ -1453,21 +1646,15 @@ static int cortex_m3_examine(struct target_s *target)
                target_set_examined(target);
 
                /* Read from Device Identification Registers */
-               if ((retval = target_read_u32(target, CPUID, &cpuid)) != ERROR_OK)
+               retval = target_read_u32(target, CPUID, &cpuid);
+               if (retval != ERROR_OK)
                        return retval;
 
                if (((cpuid >> 4) & 0xc3f) == 0xc23)
                        LOG_DEBUG("CORTEX-M3 processor detected");
                LOG_DEBUG("cpuid: 0x%8.8" PRIx32 "", cpuid);
 
-               target_read_u32(target, NVIC_ICTR, &ictr);
-               cortex_m3->intlinesnum = (ictr & 0x1F) + 1;
-               cortex_m3->intsetenable = calloc(cortex_m3->intlinesnum, 4);
-               for (i = 0; i < cortex_m3->intlinesnum; i++)
-               {
-                       target_read_u32(target, NVIC_ISE0 + 4 * i, cortex_m3->intsetenable + i);
-                       LOG_DEBUG("interrupt enable[%i] = 0x%8.8" PRIx32 "", i, cortex_m3->intsetenable[i]);
-               }
+               /* NOTE: FPB and DWT are both optional. */
 
                /* Setup FPB */
                target_read_u32(target, FP_CTRL, &fpcr);
@@ -1485,24 +1672,12 @@ static int cortex_m3_examine(struct target_s *target)
                LOG_DEBUG("FPB fpcr 0x%" PRIx32 ", numcode %i, numlit %i", fpcr, cortex_m3->fp_num_code, cortex_m3->fp_num_lit);
 
                /* Setup DWT */
-               target_read_u32(target, DWT_CTRL, &dwtcr);
-               cortex_m3->dwt_num_comp = (dwtcr >> 28) & 0xF;
-               cortex_m3->dwt_comp_available = cortex_m3->dwt_num_comp;
-               cortex_m3->dwt_comparator_list = calloc(cortex_m3->dwt_num_comp, sizeof(cortex_m3_dwt_comparator_t));
-               for (i = 0; i < cortex_m3->dwt_num_comp; i++)
-               {
-                       cortex_m3->dwt_comparator_list[i].dwt_comparator_address = DWT_COMP0 + 0x10 * i;
-               }
+               cortex_m3_dwt_setup(cortex_m3, target);
        }
 
        return ERROR_OK;
 }
 
-static int cortex_m3_quit(void)
-{
-       return ERROR_OK;
-}
-
 static int cortex_m3_dcc_read(swjdp_common_t *swjdp, uint8_t *value, uint8_t *ctrl)
 {
        uint16_t dcrdr;
@@ -1655,14 +1830,10 @@ handle_cortex_m3_disassemble_command(struct command_context_s *cmd_ctx,
        errno = 0;
        switch (argc) {
        case 2:
-               count = strtoul(args[1], NULL, 0);
-               if (errno)
-                       return ERROR_FAIL;
+               COMMAND_PARSE_NUMBER(ulong, args[1], count);
                /* FALL THROUGH */
        case 1:
-               address = strtoul(args[0], NULL, 0);
-               if (errno)
-                       return ERROR_FAIL;
+               COMMAND_PARSE_NUMBER(u32, args[0], address);
                break;
        default:
                command_print(cmd_ctx,
@@ -1842,7 +2013,7 @@ target_type_t cortexm3_target =
        .register_commands = cortex_m3_register_commands,
        .target_create = cortex_m3_target_create,
        .init_target = cortex_m3_init_target,
+       .has_mmu = cortex_m3_has_mmu,
        .examine = cortex_m3_examine,
-       .quit = cortex_m3_quit
 };
 

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)