Oleksandr Tymoshenko <gonzo@bluezbox.com> simple watchpoint support
[fw/openocd] / src / target / mips_m4k.c
index aac95bbcf4ef8868d091041989f39809365db6c9..07ecb5565d2afa5a56771d47bb62661d32da6930 100644 (file)
@@ -348,7 +348,7 @@ int mips_m4k_single_step_core(target_t *target)
 
        /* disable interrupts while stepping */
        mips32_enable_interrupts(target, 0);
-       
+
        /* exit debug mode */
        mips_ejtag_exit_debug(ejtag_info);
 
@@ -404,7 +404,7 @@ int mips_m4k_resume(struct target_s *target, int current, uint32_t address, int
 
        /* enable interrupts if we are running */
        mips32_enable_interrupts(target, !debug_execution);
-       
+
        /* exit debug mode */
        mips_ejtag_exit_debug(ejtag_info);
        target->debug_reason = DBG_REASON_NOTHALTED;
@@ -462,7 +462,7 @@ int mips_m4k_step(struct target_s *target, int current, uint32_t address, int ha
 
        /* disable interrupts while stepping */
        mips32_enable_interrupts(target, 0);
-               
+
        /* exit debug mode */
        mips_ejtag_exit_debug(ejtag_info);
 
@@ -498,7 +498,7 @@ int mips_m4k_set_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
        mips32_common_t *mips32 = target->arch_info;
        mips32_comparator_t * comparator_list = mips32->inst_break_list;
        int retval;
-       
+
        if (breakpoint->set)
        {
                LOG_WARNING("breakpoint already set");
@@ -530,7 +530,7 @@ int mips_m4k_set_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
                if (breakpoint->length == 4)
                {
                        uint32_t verify = 0xffffffff;
-                       
+
                        if ((retval = target_read_memory(target, breakpoint->address, breakpoint->length, 1, breakpoint->orig_instr)) != ERROR_OK)
                        {
                                return retval;
@@ -539,7 +539,7 @@ int mips_m4k_set_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
                        {
                                return retval;
                        }
-                       
+
                        if ((retval = target_read_u32(target, breakpoint->address, &verify)) != ERROR_OK)
                        {
                                return retval;
@@ -553,7 +553,7 @@ int mips_m4k_set_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
                else
                {
                        uint16_t verify = 0xffff;
-                       
+
                        if ((retval = target_read_memory(target, breakpoint->address, breakpoint->length, 1, breakpoint->orig_instr)) != ERROR_OK)
                        {
                                return retval;
@@ -562,7 +562,7 @@ int mips_m4k_set_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
                        {
                                return retval;
                        }
-                       
+
                        if ((retval = target_read_u16(target, breakpoint->address, &verify)) != ERROR_OK)
                        {
                                return retval;
@@ -573,7 +573,7 @@ int mips_m4k_set_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
                                return ERROR_OK;
                        }
                }
-               
+
                breakpoint->set = 20; /* Any nice value but 0 */
        }
 
@@ -586,7 +586,7 @@ int mips_m4k_unset_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
        mips32_common_t *mips32 = target->arch_info;
        mips32_comparator_t * comparator_list = mips32->inst_break_list;
        int retval;
-       
+
        if (!breakpoint->set)
        {
                LOG_WARNING("breakpoint not set");
@@ -611,7 +611,7 @@ int mips_m4k_unset_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
                if (breakpoint->length == 4)
                {
                        uint32_t current_instr;
-                       
+
                        /* check that user program has not modified breakpoint instruction */
                        if ((retval = target_read_memory(target, breakpoint->address, 4, 1, (uint8_t*)&current_instr)) != ERROR_OK)
                        {
@@ -628,13 +628,13 @@ int mips_m4k_unset_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
                else
                {
                        uint16_t current_instr;
-                       
+
                        /* check that user program has not modified breakpoint instruction */
                        if ((retval = target_read_memory(target, breakpoint->address, 2, 1, (uint8_t*)&current_instr)) != ERROR_OK)
                        {
                                return retval;
                        }
-                       
+
                        if (current_instr == MIPS16_SDBBP)
                        {
                                if ((retval = target_write_memory(target, breakpoint->address, 2, 1, breakpoint->orig_instr)) != ERROR_OK)
@@ -660,9 +660,9 @@ int mips_m4k_add_breakpoint(struct target_s *target, breakpoint_t *breakpoint)
                        LOG_INFO("no hardware breakpoint available");
                        return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
                }
-               
+
                mips32->num_inst_bpoints_avail--;
-       }       
+       }
 
        mips_m4k_set_breakpoint(target, breakpoint);
 
@@ -693,25 +693,132 @@ int mips_m4k_remove_breakpoint(struct target_s *target, breakpoint_t *breakpoint
 
 int mips_m4k_set_watchpoint(struct target_s *target, watchpoint_t *watchpoint)
 {
-       /* TODO */
+       mips32_common_t *mips32 = target->arch_info;
+       mips32_comparator_t * comparator_list = mips32->data_break_list;
+       int wp_num = 0;
+       /*
+        * watchpoint enabled, ignore all byte lanes in value register
+        * and exclude both load and store accesses from  watchpoint
+        * condition evaluation
+       */
+       int enable = EJTAG_DBCn_NOSB | EJTAG_DBCn_NOLB | EJTAG_DBCn_BE | 
+                (0xff << EJTAG_DBCn_BLM_SHIFT);
+       
+       if (watchpoint->set)
+       {
+               LOG_WARNING("watchpoint already set");
+               return ERROR_OK;
+       }
+
+       while(comparator_list[wp_num].used && (wp_num < mips32->num_data_bpoints))
+               wp_num++;
+       if (wp_num >= mips32->num_data_bpoints)
+       {
+               LOG_DEBUG("ERROR Can not find free FP Comparator");
+               LOG_WARNING("ERROR Can not find free FP Comparator");
+               exit(-1);
+       }
+
+       if (watchpoint->length != 4)
+       {
+               LOG_ERROR("Only watchpoints of length 4 are supported");
+               return ERROR_TARGET_UNALIGNED_ACCESS;
+       }
+
+       if (watchpoint->address % 4)
+       {
+               LOG_ERROR("Watchpoints address should be word aligned");
+               return ERROR_TARGET_UNALIGNED_ACCESS;
+       }
+
+       switch (watchpoint->rw)
+       {
+               case WPT_READ:
+                       enable &= ~EJTAG_DBCn_NOLB;
+                       break;
+               case WPT_WRITE:
+                       enable &= ~EJTAG_DBCn_NOSB;
+                       break;
+               case WPT_ACCESS:
+                       enable &= ~(EJTAG_DBCn_NOLB | EJTAG_DBCn_NOSB);
+                       break;
+               default:
+                       LOG_ERROR("BUG: watchpoint->rw neither read, write nor access");
+       }
+
+       watchpoint->set = wp_num + 1;
+       comparator_list[wp_num].used = 1;
+       comparator_list[wp_num].bp_value = watchpoint->address;
+       target_write_u32(target, comparator_list[wp_num].reg_address, comparator_list[wp_num].bp_value);
+       target_write_u32(target, comparator_list[wp_num].reg_address + 0x08, 0x00000000);
+       target_write_u32(target, comparator_list[wp_num].reg_address + 0x10, 0x00000000);
+       target_write_u32(target, comparator_list[wp_num].reg_address + 0x18, enable);
+       target_write_u32(target, comparator_list[wp_num].reg_address + 0x20, 0);
+       LOG_DEBUG("wp_num %i bp_value 0x%" PRIx32 "", wp_num, comparator_list[wp_num].bp_value);
+       
        return ERROR_OK;
 }
 
 int mips_m4k_unset_watchpoint(struct target_s *target, watchpoint_t *watchpoint)
 {
-       /* TODO */
+       /* get pointers to arch-specific information */
+       mips32_common_t *mips32 = target->arch_info;
+       mips32_comparator_t * comparator_list = mips32->data_break_list;
+       
+       if (!watchpoint->set)
+       {
+               LOG_WARNING("watchpoint not set");
+               return ERROR_OK;
+       }
+
+       int wp_num = watchpoint->set - 1;
+       if ((wp_num < 0) || (wp_num >= mips32->num_data_bpoints))
+       {
+               LOG_DEBUG("Invalid FP Comparator number in watchpoint");
+               return ERROR_OK;
+       }
+       comparator_list[wp_num].used = 0;
+       comparator_list[wp_num].bp_value = 0;
+       target_write_u32(target, comparator_list[wp_num].reg_address + 0x18, 0);
+       watchpoint->set = 0;
+
        return ERROR_OK;
 }
 
 int mips_m4k_add_watchpoint(struct target_s *target, watchpoint_t *watchpoint)
 {
-       /* TODO */
+       mips32_common_t *mips32 = target->arch_info;
+
+       if (mips32->num_data_bpoints_avail < 1)
+       {
+               LOG_INFO("no hardware watchpoints available");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+               
+       mips32->num_data_bpoints_avail--;
+
+       mips_m4k_set_watchpoint(target, watchpoint);
        return ERROR_OK;
 }
 
 int mips_m4k_remove_watchpoint(struct target_s *target, watchpoint_t *watchpoint)
 {
-       /* TODO */
+       /* get pointers to arch-specific information */
+       mips32_common_t *mips32 = target->arch_info;
+
+       if (target->state != TARGET_HALTED)
+       {
+               LOG_WARNING("target not halted");
+               return ERROR_TARGET_NOT_HALTED;
+       }
+
+       if (watchpoint->set)
+       {
+               mips_m4k_unset_watchpoint(target, watchpoint);
+       }
+
+       mips32->num_data_bpoints_avail++;
+
        return ERROR_OK;
 }
 
@@ -758,7 +865,7 @@ int mips_m4k_read_memory(struct target_s *target, uint32_t address, uint32_t siz
                return retval;
 
        /* TAP data register is loaded LSB first (little endian) */
-       if (target->endianness == TARGET_BIG_ENDIAN) 
+       if (target->endianness == TARGET_BIG_ENDIAN)
        {
                uint32_t i, t32;
                uint16_t t16;
@@ -810,7 +917,7 @@ int mips_m4k_write_memory(struct target_s *target, uint32_t address, uint32_t si
 
                for (i = 0; i < (count*size); i += size)
                {
-                       switch (size) 
+                       switch (size)
                        {
                                case 4:
                                        t32 = be_to_h_u32(&buffer[i]);
@@ -822,7 +929,7 @@ int mips_m4k_write_memory(struct target_s *target, uint32_t address, uint32_t si
                                        break;
                        }
                }
-       }          
+       }
 
        /* if noDMA off, use DMAACC mode for memory write */
        if (ejtag_info->impcode & EJTAG_IMP_NODMA)
@@ -884,7 +991,7 @@ int mips_m4k_examine(struct target_s *target)
        {
                mips_ejtag_get_idcode(ejtag_info, &idcode);
                ejtag_info->idcode = idcode;
-               
+
                if (((idcode >> 1) & 0x7FF) == 0x29)
                {
                        /* we are using a pic32mx so select ejtag port