jtag_tap_t -> struct jtag_tap
[fw/openocd] / src / target / cortex_m3.c
index e854f6b6560c174dff1aa227e5f7c9f0e3bc3ff2..d617817d14c8c17c8d85a39fb4d80a23bfd9bcdc 100644 (file)
 #include "arm_disassembler.h"
 
 
+/* NOTE:  most of this should work fine for the Cortex-M1 and
+ * Cortex-M0 cores too, although they're ARMv6-M not ARMv7-M.
+ */
+
 #define ARRAY_SIZE(x)  ((int)(sizeof(x)/sizeof((x)[0])))
 
 
@@ -117,10 +121,8 @@ static int cortexm3_dap_write_coreregister_u32(swjdp_common_t *swjdp,
 static int cortex_m3_write_debug_halt_mask(target_t *target,
                uint32_t mask_on, uint32_t mask_off)
 {
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
-       swjdp_common_t *swjdp = &armv7m->swjdp_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
+       struct swjdp_common_s *swjdp = &cortex_m3->armv7m.swjdp_info;
 
        /* mask off status bits */
        cortex_m3->dcb_dhcsr &= ~((0xFFFF << 16) | mask_off);
@@ -132,10 +134,8 @@ static int cortex_m3_write_debug_halt_mask(target_t *target,
 
 static int cortex_m3_clear_halt(target_t *target)
 {
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
-       swjdp_common_t *swjdp = &armv7m->swjdp_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
+       struct swjdp_common_s *swjdp = &cortex_m3->armv7m.swjdp_info;
 
        /* clear step if any */
        cortex_m3_write_debug_halt_mask(target, C_HALT, C_STEP);
@@ -151,10 +151,8 @@ static int cortex_m3_clear_halt(target_t *target)
 
 static int cortex_m3_single_step_core(target_t *target)
 {
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
-       swjdp_common_t *swjdp = &armv7m->swjdp_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
+       struct swjdp_common_s *swjdp = &cortex_m3->armv7m.swjdp_info;
        uint32_t dhcsr_save;
 
        /* backup dhcsr reg */
@@ -177,11 +175,8 @@ static int cortex_m3_endreset_event(target_t *target)
 {
        int i;
        uint32_t dcb_demcr;
-
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
-       swjdp_common_t *swjdp = &armv7m->swjdp_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
+       struct swjdp_common_s *swjdp = &cortex_m3->armv7m.swjdp_info;
        cortex_m3_fp_comparator_t *fp_list = cortex_m3->fp_comparator_list;
        cortex_m3_dwt_comparator_t *dwt_list = cortex_m3->dwt_comparator_list;
 
@@ -217,9 +212,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);
 
@@ -233,9 +231,7 @@ static int cortex_m3_endreset_event(target_t *target)
 
 static int cortex_m3_examine_debug_reason(target_t *target)
 {
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
 
        /* THIS IS NOT GOOD, TODO - better logic for detection of debug state reason */
        /* only check the debug reason if we don't know it already */
@@ -253,7 +249,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;
        }
 
@@ -263,9 +259,7 @@ static int cortex_m3_examine_debug_reason(target_t *target)
 static int cortex_m3_examine_exception_reason(target_t *target)
 {
        uint32_t shcsr, except_sr, cfsr = -1, except_ar = -1;
-
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
+       struct armv7m_common_s *armv7m = target_to_armv7m(target);
        swjdp_common_t *swjdp = &armv7m->swjdp_info;
 
        mem_ap_read_u32(swjdp, NVIC_SHCSR, &shcsr);
@@ -315,10 +309,8 @@ static int cortex_m3_debug_entry(target_t *target)
        int i;
        uint32_t xPSR;
        int retval;
-
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
+       struct armv7m_common_s *armv7m = &cortex_m3->armv7m;
        swjdp_common_t *swjdp = &armv7m->swjdp_info;
 
        LOG_DEBUG(" ");
@@ -388,11 +380,8 @@ static int cortex_m3_poll(target_t *target)
 {
        int retval;
        enum target_state prev_target_state = target->state;
-
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
-       swjdp_common_t *swjdp = &armv7m->swjdp_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
+       struct swjdp_common_s *swjdp = &cortex_m3->armv7m.swjdp_info;
 
        /* Read from Debug Halting Control and Status Register */
        retval = mem_ap_read_atomic_u32(swjdp, DCB_DHCSR, &cortex_m3->dcb_dhcsr);
@@ -505,10 +494,8 @@ static int cortex_m3_halt(target_t *target)
 
 static int cortex_m3_soft_reset_halt(struct target_s *target)
 {
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
-       swjdp_common_t *swjdp = &armv7m->swjdp_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
+       struct swjdp_common_s *swjdp = &cortex_m3->armv7m.swjdp_info;
        uint32_t dcb_dhcsr = 0;
        int retval, timeout = 0;
 
@@ -560,8 +547,7 @@ static void cortex_m3_enable_breakpoints(struct target_s *target)
 static int cortex_m3_resume(struct target_s *target, int current,
                uint32_t address, int handle_breakpoints, int debug_execution)
 {
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
+       struct armv7m_common_s *armv7m = target_to_armv7m(target);
        breakpoint_t *breakpoint = NULL;
        uint32_t resume_pc;
 
@@ -615,7 +601,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);
@@ -649,9 +635,8 @@ static int cortex_m3_resume(struct target_s *target, int current,
 static int cortex_m3_step(struct target_s *target, int current,
                uint32_t address, int handle_breakpoints)
 {
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
+       struct armv7m_common_s *armv7m = &cortex_m3->armv7m;
        swjdp_common_t *swjdp = &armv7m->swjdp_info;
        breakpoint_t *breakpoint = NULL;
 
@@ -663,12 +648,16 @@ static int cortex_m3_step(struct target_s *target, int current,
 
        /* current = 1: continue on current pc, otherwise continue at <address> */
        if (!current)
-               buf_set_u32(armv7m->core_cache->reg_list[15].value, 0, 32, address);
+               buf_set_u32(cortex_m3->armv7m.core_cache->reg_list[15].value,
+                               0, 32, address);
 
        /* the front-end may request us not to handle breakpoints */
-       if (handle_breakpoints)
-               if ((breakpoint = breakpoint_find(target, buf_get_u32(armv7m->core_cache->reg_list[15].value, 0, 32))))
+       if (handle_breakpoints) {
+               breakpoint = breakpoint_find(target, buf_get_u32(armv7m
+                               ->core_cache->reg_list[15].value, 0, 32));
+               if (breakpoint)
                        cortex_m3_unset_breakpoint(target, breakpoint);
+       }
 
        target->debug_reason = DBG_REASON_SINGLESTEP;
 
@@ -697,9 +686,8 @@ static int cortex_m3_step(struct target_s *target, int current,
 
 static int cortex_m3_assert_reset(target_t *target)
 {
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
-       swjdp_common_t *swjdp = &armv7m->swjdp_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
+       struct swjdp_common_s *swjdp = &cortex_m3->armv7m.swjdp_info;
        int assert_srst = 1;
 
        LOG_DEBUG("target->state: %s",
@@ -850,12 +838,8 @@ cortex_m3_set_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
        int retval;
        int fp_num = 0;
        uint32_t hilo;
-
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
-
-       cortex_m3_fp_comparator_t * comparator_list = cortex_m3->fp_comparator_list;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
+       cortex_m3_fp_comparator_t *comparator_list = cortex_m3->fp_comparator_list;
 
        if (breakpoint->set)
        {
@@ -919,9 +903,7 @@ static int
 cortex_m3_unset_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
 {
        int retval;
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
        cortex_m3_fp_comparator_t * comparator_list = cortex_m3->fp_comparator_list;
 
        if (!breakpoint->set)
@@ -975,9 +957,7 @@ cortex_m3_unset_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
 static int
 cortex_m3_add_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
 {
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
 
        if (cortex_m3->auto_bp_type)
        {
@@ -1026,10 +1006,9 @@ cortex_m3_add_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
 static int
 cortex_m3_remove_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
 {
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
 
+       /* REVISIT why check? FBP can be updated with core running ... */
        if (target->state != TARGET_HALTED)
        {
                LOG_WARNING("target not halted");
@@ -1057,86 +1036,97 @@ cortex_m3_set_watchpoint(struct target_s *target, watchpoint_t *watchpoint)
 {
        int dwt_num = 0;
        uint32_t mask, temp;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
 
-       /* 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--;
+
+       /* 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;
 
-       if (watchpoint->mask == 0xffffffffu)
+       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
 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;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
+       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;
 
@@ -1146,10 +1136,9 @@ cortex_m3_unset_watchpoint(struct target_s *target, watchpoint_t *watchpoint)
 static int
 cortex_m3_add_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;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
 
+       /* REVISIT why check? DWT can be updated with core running ... */
        if (target->state != TARGET_HALTED)
        {
                LOG_WARNING("target not halted");
@@ -1158,11 +1147,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;
        }
 
@@ -1175,10 +1194,9 @@ cortex_m3_add_watchpoint(struct target_s *target, watchpoint_t *watchpoint)
 static int
 cortex_m3_remove_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;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
 
+       /* REVISIT why check? DWT can be updated with core running ... */
        if (target->state != TARGET_HALTED)
        {
                LOG_WARNING("target not halted");
@@ -1213,8 +1231,7 @@ static int cortex_m3_load_core_reg_u32(struct target_s *target,
                enum armv7m_regtype type, uint32_t num, uint32_t * value)
 {
        int retval;
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
+       struct armv7m_common_s *armv7m = target_to_armv7m(target);
        swjdp_common_t *swjdp = &armv7m->swjdp_info;
 
        /* NOTE:  we "know" here that the register identifiers used
@@ -1278,9 +1295,7 @@ static int cortex_m3_store_core_reg_u32(struct target_s *target,
 {
        int retval;
        uint32_t reg;
-
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
+       struct armv7m_common_s *armv7m = target_to_armv7m(target);
        swjdp_common_t *swjdp = &armv7m->swjdp_info;
 
 #ifdef ARMV7_GDB_HACKS
@@ -1355,8 +1370,7 @@ static int cortex_m3_store_core_reg_u32(struct target_s *target,
 static int cortex_m3_read_memory(struct target_s *target, uint32_t address,
                uint32_t size, uint32_t count, uint8_t *buffer)
 {
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
+       struct armv7m_common_s *armv7m = target_to_armv7m(target);
        swjdp_common_t *swjdp = &armv7m->swjdp_info;
        int retval;
 
@@ -1388,8 +1402,7 @@ static int cortex_m3_read_memory(struct target_s *target, uint32_t address,
 static int cortex_m3_write_memory(struct target_s *target, uint32_t address,
                uint32_t size, uint32_t count, uint8_t *buffer)
 {
-       /* get pointers to arch-specific information */
-       armv7m_common_t *armv7m = target->arch_info;
+       struct armv7m_common_s *armv7m = target_to_armv7m(target);
        swjdp_common_t *swjdp = &armv7m->swjdp_info;
        int retval;
 
@@ -1422,28 +1435,160 @@ 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_DEBUG("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 */
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
-       swjdp_common_t *swjdp = &armv7m->swjdp_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
+       struct swjdp_common_s *swjdp = &cortex_m3->armv7m.swjdp_info;
 
        if ((retval = ahbap_debugport_init(swjdp)) != ERROR_OK)
                return retval;
@@ -1453,21 +1598,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 +1624,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;
@@ -1527,7 +1654,7 @@ static int cortex_m3_dcc_read(swjdp_common_t *swjdp, uint8_t *value, uint8_t *ct
 static int cortex_m3_target_request_data(target_t *target,
                uint32_t size, uint8_t *buffer)
 {
-       armv7m_common_t *armv7m = target->arch_info;
+       struct armv7m_common_s *armv7m = target_to_armv7m(target);
        swjdp_common_t *swjdp = &armv7m->swjdp_info;
        uint8_t data;
        uint8_t ctrl;
@@ -1547,7 +1674,7 @@ static int cortex_m3_handle_target_request(void *priv)
        target_t *target = priv;
        if (!target_was_examined(target))
                return ERROR_OK;
-       armv7m_common_t *armv7m = target->arch_info;
+       struct armv7m_common_s *armv7m = target_to_armv7m(target);
        swjdp_common_t *swjdp = &armv7m->swjdp_info;
 
        if (!target->dbg_msg_enabled)
@@ -1581,11 +1708,10 @@ static int cortex_m3_handle_target_request(void *priv)
 }
 
 static int cortex_m3_init_arch_info(target_t *target,
-               cortex_m3_common_t *cortex_m3, jtag_tap_t *tap)
+               cortex_m3_common_t *cortex_m3, struct jtag_tap *tap)
 {
        int retval;
-       armv7m_common_t *armv7m;
-       armv7m = &cortex_m3->armv7m;
+       struct armv7m_common_s *armv7m = &cortex_m3->armv7m;
 
        armv7m_init_arch_info(target, armv7m);
 
@@ -1600,11 +1726,6 @@ static int cortex_m3_init_arch_info(target_t *target,
        armv7m->swjdp_info.memaccess_tck = 8;
        armv7m->swjdp_info.tar_autoincr_block = (1 << 12);      /* Cortex-M3 has 4096 bytes autoincrement range */
 
-       /* initialize arch-specific breakpoint handling */
-
-       cortex_m3->common_magic = CORTEX_M3_COMMON_MAGIC;
-       cortex_m3->arch_info = NULL;
-
        /* register arch-specific functions */
        armv7m->examine_debug_reason = cortex_m3_examine_debug_reason;
 
@@ -1613,7 +1734,6 @@ static int cortex_m3_init_arch_info(target_t *target,
        armv7m->pre_restore_context = NULL;
        armv7m->post_restore_context = NULL;
 
-       armv7m->arch_info = cortex_m3;
        armv7m->load_core_reg_u32 = cortex_m3_load_core_reg_u32;
        armv7m->store_core_reg_u32 = cortex_m3_store_core_reg_u32;
 
@@ -1631,38 +1751,56 @@ static int cortex_m3_target_create(struct target_s *target, Jim_Interp *interp)
 {
        cortex_m3_common_t *cortex_m3 = calloc(1,sizeof(cortex_m3_common_t));
 
+       cortex_m3->common_magic = CORTEX_M3_COMMON_MAGIC;
        cortex_m3_init_arch_info(target, cortex_m3, target->tap);
 
        return ERROR_OK;
 }
 
+/*--------------------------------------------------------------------------*/
+
+static int cortex_m3_verify_pointer(struct command_context_s *cmd_ctx,
+               struct cortex_m3_common_s *cm3)
+{
+       if (cm3->common_magic != CORTEX_M3_COMMON_MAGIC) {
+               command_print(cmd_ctx, "target is not a Cortex-M3");
+               return ERROR_TARGET_INVALID;
+       }
+       return ERROR_OK;
+}
+
+/*
+ * Only stuff below this line should need to verify that its target
+ * is a Cortex-M3.  Everything else should have indirected through the
+ * cortexm3_target structure, which is only used with CM3 targets.
+ */
+
 /*
  * REVISIT Thumb2 disassembly should work for all ARMv7 cores, as well
  * as at least ARM-1156T2.  The interesting thing about Cortex-M is
  * that *only* Thumb2 disassembly matters.  There are also some small
  * additions to Thumb2 that are specific to ARMv7-M.
  */
-static int
-handle_cortex_m3_disassemble_command(struct command_context_s *cmd_ctx,
-               char *cmd, char **args, int argc)
+COMMAND_HANDLER(handle_cortex_m3_disassemble_command)
 {
-       int retval = ERROR_OK;
+       int retval;
        target_t *target = get_current_target(cmd_ctx);
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
        uint32_t address;
        unsigned long count = 1;
        arm_instruction_t cur_instruction;
 
+       retval = cortex_m3_verify_pointer(cmd_ctx, cortex_m3);
+       if (retval != ERROR_OK)
+               return retval;
+
        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,
@@ -1695,40 +1833,44 @@ static const struct {
        { "reset",      VC_CORERESET, },
 };
 
-static int
-handle_cortex_m3_vector_catch_command(struct command_context_s *cmd_ctx,
-               char *cmd, char **argv, int argc)
+COMMAND_HANDLER(handle_cortex_m3_vector_catch_command)
 {
        target_t *target = get_current_target(cmd_ctx);
-       armv7m_common_t *armv7m = target->arch_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
+       struct armv7m_common_s *armv7m = &cortex_m3->armv7m;
        swjdp_common_t *swjdp = &armv7m->swjdp_info;
        uint32_t demcr = 0;
+       int retval;
        int i;
 
+       retval = cortex_m3_verify_pointer(cmd_ctx, cortex_m3);
+       if (retval != ERROR_OK)
+               return retval;
+
        mem_ap_read_atomic_u32(swjdp, DCB_DEMCR, &demcr);
 
        if (argc > 0) {
                unsigned catch = 0;
 
                if (argc == 1) {
-                       if (strcmp(argv[0], "all") == 0) {
+                       if (strcmp(args[0], "all") == 0) {
                                catch = VC_HARDERR | VC_INTERR | VC_BUSERR
                                        | VC_STATERR | VC_CHKERR | VC_NOCPERR
                                        | VC_MMERR | VC_CORERESET;
                                goto write;
-                       } else if (strcmp(argv[0], "none") == 0) {
+                       } else if (strcmp(args[0], "none") == 0) {
                                goto write;
                        }
                }
                while (argc-- > 0) {
                        for (i = 0; i < ARRAY_SIZE(vec_ids); i++) {
-                               if (strcmp(argv[argc], vec_ids[i].name) != 0)
+                               if (strcmp(args[argc], vec_ids[i].name) != 0)
                                        continue;
                                catch |= vec_ids[i].mask;
                                break;
                        }
                        if (i == ARRAY_SIZE(vec_ids)) {
-                               LOG_ERROR("No CM3 vector '%s'", argv[argc]);
+                               LOG_ERROR("No CM3 vector '%s'", args[argc]);
                                return ERROR_INVALID_ARGUMENTS;
                        }
                }
@@ -1748,17 +1890,19 @@ write:
        return ERROR_OK;
 }
 
-static int
-handle_cortex_m3_mask_interrupts_command(struct command_context_s *cmd_ctx,
-               char *cmd, char **args, int argc)
+COMMAND_HANDLER(handle_cortex_m3_mask_interrupts_command)
 {
        target_t *target = get_current_target(cmd_ctx);
-       armv7m_common_t *armv7m = target->arch_info;
-       cortex_m3_common_t *cortex_m3 = armv7m->arch_info;
+       struct cortex_m3_common_s *cortex_m3 = target_to_cm3(target);
+       int retval;
+
+       retval = cortex_m3_verify_pointer(cmd_ctx, cortex_m3);
+       if (retval != ERROR_OK)
+               return retval;
 
        if (target->state != TARGET_HALTED)
        {
-               command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd);
+               command_print(cmd_ctx, "target must be stopped for \"%s\" command", CMD_NAME);
                return ERROR_OK;
        }
 
@@ -1843,6 +1987,4 @@ target_type_t cortexm3_target =
        .target_create = cortex_m3_target_create,
        .init_target = cortex_m3_init_target,
        .examine = cortex_m3_examine,
-       .quit = cortex_m3_quit
 };
-