nds32: remove .soft_reset_halt dependency
[openocd.git] / src / target / nds32_v3m.c
1 /***************************************************************************
2 * Copyright (C) 2013 Andes Technology *
3 * Hsiangkai Wang <hkwang@andestech.com> *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
19 ***************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "breakpoints.h"
26 #include "nds32_cmd.h"
27 #include "nds32_aice.h"
28 #include "nds32_v3m.h"
29 #include "nds32_v3_common.h"
30
31 static int nds32_v3m_activate_hardware_breakpoint(struct target *target)
32 {
33 struct nds32_v3m_common *nds32_v3m = target_to_nds32_v3m(target);
34 struct aice_port_s *aice = target_to_aice(target);
35 struct breakpoint *bp;
36 unsigned brp_num = nds32_v3m->n_hbr - 1;
37
38 for (bp = target->breakpoints; bp; bp = bp->next) {
39 if (bp->type == BKPT_SOFT) {
40 /* already set at nds32_v3m_add_breakpoint() */
41 continue;
42 } else if (bp->type == BKPT_HARD) {
43 /* set address */
44 aice_write_debug_reg(aice, NDS_EDM_SR_BPA0 + brp_num, bp->address);
45 /* set mask */
46 aice_write_debug_reg(aice, NDS_EDM_SR_BPAM0 + brp_num, 0);
47
48 if (nds32_v3m->nds32.memory.address_translation)
49 /* enable breakpoint (virtual address) */
50 aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + brp_num, 0x2);
51 else
52 /* enable breakpoint (physical address) */
53 aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + brp_num, 0xA);
54
55 LOG_DEBUG("Add hardware BP %d at %08" PRIx32, brp_num,
56 bp->address);
57
58 brp_num--;
59 } else {
60 return ERROR_FAIL;
61 }
62 }
63
64 return ERROR_OK;
65 }
66
67 static int nds32_v3m_deactivate_hardware_breakpoint(struct target *target)
68 {
69 struct nds32_v3m_common *nds32_v3m = target_to_nds32_v3m(target);
70 struct aice_port_s *aice = target_to_aice(target);
71 struct breakpoint *bp;
72 unsigned brp_num = nds32_v3m->n_hbr - 1;
73
74 for (bp = target->breakpoints; bp; bp = bp->next) {
75 if (bp->type == BKPT_SOFT)
76 continue;
77 else if (bp->type == BKPT_HARD)
78 /* disable breakpoint */
79 aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + brp_num, 0x0);
80 else
81 return ERROR_FAIL;
82
83 LOG_DEBUG("Remove hardware BP %d at %08" PRIx32, brp_num,
84 bp->address);
85
86 brp_num--;
87 }
88
89 return ERROR_OK;
90 }
91
92 static int nds32_v3m_activate_hardware_watchpoint(struct target *target)
93 {
94 struct aice_port_s *aice = target_to_aice(target);
95 struct nds32_v3m_common *nds32_v3m = target_to_nds32_v3m(target);
96 struct watchpoint *wp;
97 int32_t wp_num = 0;
98 uint32_t wp_config = 0;
99 bool ld_stop, st_stop;
100
101 if (nds32_v3m->nds32.global_stop)
102 ld_stop = st_stop = false;
103
104 for (wp = target->watchpoints; wp; wp = wp->next) {
105
106 if (wp_num < nds32_v3m->used_n_wp) {
107 wp->mask = wp->length - 1;
108 if ((wp->address % wp->length) != 0)
109 wp->mask = (wp->mask << 1) + 1;
110
111 if (wp->rw == WPT_READ)
112 wp_config = 0x3;
113 else if (wp->rw == WPT_WRITE)
114 wp_config = 0x5;
115 else if (wp->rw == WPT_ACCESS)
116 wp_config = 0x7;
117
118 /* set/unset physical address bit of BPCn according to PSW.DT */
119 if (nds32_v3m->nds32.memory.address_translation == false)
120 wp_config |= 0x8;
121
122 /* set address */
123 aice_write_debug_reg(aice, NDS_EDM_SR_BPA0 + wp_num,
124 wp->address - (wp->address % wp->length));
125 /* set mask */
126 aice_write_debug_reg(aice, NDS_EDM_SR_BPAM0 + wp_num, wp->mask);
127 /* enable watchpoint */
128 aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + wp_num, wp_config);
129
130 LOG_DEBUG("Add hardware wathcpoint %d at %08" PRIx32
131 " mask %08" PRIx32, wp_num,
132 wp->address, wp->mask);
133
134 wp_num++;
135 } else if (nds32_v3m->nds32.global_stop) {
136 if (wp->rw == WPT_READ)
137 ld_stop = true;
138 else if (wp->rw == WPT_WRITE)
139 st_stop = true;
140 else if (wp->rw == WPT_ACCESS)
141 ld_stop = st_stop = true;
142 }
143 }
144
145 if (nds32_v3m->nds32.global_stop) {
146 uint32_t edm_ctl;
147 aice_read_debug_reg(aice, NDS_EDM_SR_EDM_CTL, &edm_ctl);
148 if (ld_stop)
149 edm_ctl |= 0x10;
150 if (st_stop)
151 edm_ctl |= 0x20;
152 aice_write_debug_reg(aice, NDS_EDM_SR_EDM_CTL, edm_ctl);
153 }
154
155 return ERROR_OK;
156 }
157
158 static int nds32_v3m_deactivate_hardware_watchpoint(struct target *target)
159 {
160 struct nds32_v3m_common *nds32_v3m = target_to_nds32_v3m(target);
161 struct aice_port_s *aice = target_to_aice(target);
162 struct watchpoint *wp;
163 int32_t wp_num = 0;
164 bool clean_global_stop = false;
165
166 for (wp = target->watchpoints; wp; wp = wp->next) {
167
168 if (wp_num < nds32_v3m->used_n_wp) {
169 /* disable watchpoint */
170 aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + wp_num, 0x0);
171
172 LOG_DEBUG("Remove hardware wathcpoint %d at %08" PRIx32
173 " mask %08" PRIx32, wp_num,
174 wp->address, wp->mask);
175 wp_num++;
176 } else if (nds32_v3m->nds32.global_stop) {
177 clean_global_stop = true;
178 }
179 }
180
181 if (clean_global_stop) {
182 uint32_t edm_ctl;
183 aice_read_debug_reg(aice, NDS_EDM_SR_EDM_CTL, &edm_ctl);
184 edm_ctl = edm_ctl & (~0x30);
185 aice_write_debug_reg(aice, NDS_EDM_SR_EDM_CTL, edm_ctl);
186 }
187
188 return ERROR_OK;
189 }
190
191 static int nds32_v3m_check_interrupt_stack(struct nds32 *nds32)
192 {
193 uint32_t val_ir0;
194 uint32_t value;
195
196 /* Save interrupt level */
197 nds32_get_mapped_reg(nds32, IR0, &val_ir0);
198 nds32->current_interrupt_level = (val_ir0 >> 1) & 0x3;
199
200 if (nds32_reach_max_interrupt_level(nds32))
201 LOG_ERROR("<-- TARGET ERROR! Reaching the max interrupt stack level %d. -->",
202 nds32->current_interrupt_level);
203
204 /* backup $ir6 to avoid suppressed exception overwrite */
205 nds32_get_mapped_reg(nds32, IR6, &value);
206
207 return ERROR_OK;
208 }
209
210 static int nds32_v3m_restore_interrupt_stack(struct nds32 *nds32)
211 {
212 uint32_t value;
213
214 /* get backup value from cache */
215 /* then set back to make the register dirty */
216 nds32_get_mapped_reg(nds32, IR0, &value);
217 nds32_set_mapped_reg(nds32, IR0, value);
218
219 nds32_get_mapped_reg(nds32, IR6, &value);
220 nds32_set_mapped_reg(nds32, IR6, value);
221
222 return ERROR_OK;
223 }
224
225 static int nds32_v3m_deassert_reset(struct target *target)
226 {
227 int retval;
228
229 CHECK_RETVAL(nds32_poll(target));
230
231 if (target->state != TARGET_HALTED) {
232 /* reset only */
233 LOG_WARNING("%s: ran after reset and before halt ...",
234 target_name(target));
235 retval = target_halt(target);
236 if (retval != ERROR_OK)
237 return retval;
238
239 }
240
241 return ERROR_OK;
242 }
243
244 static int nds32_v3m_add_breakpoint(struct target *target,
245 struct breakpoint *breakpoint)
246 {
247 struct nds32_v3m_common *nds32_v3m = target_to_nds32_v3m(target);
248 struct nds32 *nds32 = &(nds32_v3m->nds32);
249 int result;
250
251 if (breakpoint->type == BKPT_HARD) {
252 /* check hardware resource */
253 if (nds32_v3m->next_hbr_index < nds32_v3m->next_hwp_index) {
254 LOG_WARNING("<-- TARGET WARNING! Insert too many "
255 "hardware breakpoints/watchpoints! "
256 "The limit of combined hardware "
257 "breakpoints/watchpoints is %d. -->",
258 nds32_v3m->n_hbr);
259 LOG_WARNING("<-- TARGET STATUS: Inserted number of "
260 "hardware breakpoint: %d, hardware "
261 "watchpoints: %d. -->",
262 nds32_v3m->n_hbr - nds32_v3m->next_hbr_index - 1,
263 nds32_v3m->used_n_wp);
264 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
265 }
266
267 /* update next place to put hardware breakpoint */
268 nds32_v3m->next_hbr_index--;
269
270 /* hardware breakpoint insertion occurs before 'continue' actually */
271 return ERROR_OK;
272 } else if (breakpoint->type == BKPT_SOFT) {
273 result = nds32_add_software_breakpoint(target, breakpoint);
274 if (ERROR_OK != result) {
275 /* auto convert to hardware breakpoint if failed */
276 if (nds32->auto_convert_hw_bp) {
277 /* convert to hardware breakpoint */
278 breakpoint->type = BKPT_HARD;
279
280 return nds32_v3m_add_breakpoint(target, breakpoint);
281 }
282 }
283
284 return result;
285 } else /* unrecognized breakpoint type */
286 return ERROR_FAIL;
287
288 return ERROR_OK;
289 }
290
291 static int nds32_v3m_remove_breakpoint(struct target *target,
292 struct breakpoint *breakpoint)
293 {
294 struct nds32_v3m_common *nds32_v3m = target_to_nds32_v3m(target);
295
296 if (breakpoint->type == BKPT_HARD) {
297 if (nds32_v3m->next_hbr_index >= nds32_v3m->n_hbr - 1)
298 return ERROR_FAIL;
299
300 /* update next place to put hardware breakpoint */
301 nds32_v3m->next_hbr_index++;
302
303 /* hardware breakpoint removal occurs after 'halted' actually */
304 return ERROR_OK;
305 } else if (breakpoint->type == BKPT_SOFT) {
306 return nds32_remove_software_breakpoint(target, breakpoint);
307 } else /* unrecognized breakpoint type */
308 return ERROR_FAIL;
309
310 return ERROR_OK;
311 }
312
313 static int nds32_v3m_add_watchpoint(struct target *target,
314 struct watchpoint *watchpoint)
315 {
316 struct nds32_v3m_common *nds32_v3m = target_to_nds32_v3m(target);
317
318 /* check hardware resource */
319 if (nds32_v3m->next_hwp_index >= nds32_v3m->n_hwp) {
320 /* No hardware resource */
321 if (nds32_v3m->nds32.global_stop) {
322 LOG_WARNING("<-- TARGET WARNING! The number of "
323 "watchpoints exceeds the hardware "
324 "resources. Stop at every load/store "
325 "instruction to check for watchpoint matches. -->");
326 return ERROR_OK;
327 }
328
329 LOG_WARNING("<-- TARGET WARNING! Insert too many hardware "
330 "watchpoints! The limit of hardware watchpoints "
331 "is %d. -->", nds32_v3m->n_hwp);
332 LOG_WARNING("<-- TARGET STATUS: Inserted number of "
333 "hardware watchpoint: %d. -->",
334 nds32_v3m->used_n_wp);
335 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
336 }
337
338 if (nds32_v3m->next_hwp_index > nds32_v3m->next_hbr_index) {
339 /* No hardware resource */
340 if (nds32_v3m->nds32.global_stop) {
341 LOG_WARNING("<-- TARGET WARNING! The number of "
342 "watchpoints exceeds the hardware "
343 "resources. Stop at every load/store "
344 "instruction to check for watchpoint matches. -->");
345 return ERROR_OK;
346 }
347
348 LOG_WARNING("<-- TARGET WARNING! Insert too many hardware "
349 "breakpoints/watchpoints! The limit of combined "
350 "hardware breakpoints/watchpoints is %d. -->",
351 nds32_v3m->n_hbr);
352 LOG_WARNING("<-- TARGET STATUS: Inserted number of "
353 "hardware breakpoint: %d, hardware "
354 "watchpoints: %d. -->",
355 nds32_v3m->n_hbr - nds32_v3m->next_hbr_index - 1,
356 nds32_v3m->used_n_wp);
357 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
358 }
359
360 /* update next place to put hardware watchpoint */
361 nds32_v3m->next_hwp_index++;
362 nds32_v3m->used_n_wp++;
363
364 return ERROR_OK;
365 }
366
367 static int nds32_v3m_remove_watchpoint(struct target *target,
368 struct watchpoint *watchpoint)
369 {
370 struct nds32_v3m_common *nds32_v3m = target_to_nds32_v3m(target);
371
372 if (nds32_v3m->next_hwp_index <= 0) {
373 if (nds32_v3m->nds32.global_stop)
374 return ERROR_OK;
375
376 return ERROR_FAIL;
377 }
378
379 /* update next place to put hardware watchpoint */
380 nds32_v3m->next_hwp_index--;
381 nds32_v3m->used_n_wp--;
382
383 return ERROR_OK;
384 }
385
386 struct nds32_v3_common_callback nds32_v3m_common_callback = {
387 .check_interrupt_stack = nds32_v3m_check_interrupt_stack,
388 .restore_interrupt_stack = nds32_v3m_restore_interrupt_stack,
389 .activate_hardware_breakpoint = nds32_v3m_activate_hardware_breakpoint,
390 .activate_hardware_watchpoint = nds32_v3m_activate_hardware_watchpoint,
391 .deactivate_hardware_breakpoint = nds32_v3m_deactivate_hardware_breakpoint,
392 .deactivate_hardware_watchpoint = nds32_v3m_deactivate_hardware_watchpoint,
393 };
394
395 static int nds32_v3m_target_create(struct target *target, Jim_Interp *interp)
396 {
397 struct nds32_v3m_common *nds32_v3m;
398
399 nds32_v3m = calloc(1, sizeof(*nds32_v3m));
400 if (!nds32_v3m)
401 return ERROR_FAIL;
402
403 nds32_v3_common_register_callback(&nds32_v3m_common_callback);
404 nds32_v3_target_create_common(target, &(nds32_v3m->nds32));
405
406 return ERROR_OK;
407 }
408
409 /* talk to the target and set things up */
410 static int nds32_v3m_examine(struct target *target)
411 {
412 struct nds32_v3m_common *nds32_v3m = target_to_nds32_v3m(target);
413 struct nds32 *nds32 = &(nds32_v3m->nds32);
414 struct aice_port_s *aice = target_to_aice(target);
415
416 if (!target_was_examined(target)) {
417 CHECK_RETVAL(nds32_edm_config(nds32));
418
419 if (nds32->reset_halt_as_examine)
420 CHECK_RETVAL(nds32_reset_halt(nds32));
421 }
422
423 uint32_t edm_cfg;
424 aice_read_debug_reg(aice, NDS_EDM_SR_EDM_CFG, &edm_cfg);
425
426 /* get the number of hardware breakpoints */
427 nds32_v3m->n_hbr = (edm_cfg & 0x7) + 1;
428 nds32_v3m->used_n_wp = 0;
429
430 /* get the number of hardware watchpoints */
431 /* If the WP field is hardwired to zero, it means this is a
432 * simple breakpoint. Otherwise, if the WP field is writable
433 * then it means this is a regular watchpoints. */
434 nds32_v3m->n_hwp = 0;
435 for (int32_t i = 0 ; i < nds32_v3m->n_hbr ; i++) {
436 /** check the hardware breakpoint is simple or not */
437 uint32_t tmp_value;
438 aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + i, 0x1);
439 aice_read_debug_reg(aice, NDS_EDM_SR_BPC0 + i, &tmp_value);
440
441 if (tmp_value)
442 nds32_v3m->n_hwp++;
443 }
444 /* hardware breakpoint is inserted from high index to low index */
445 nds32_v3m->next_hbr_index = nds32_v3m->n_hbr - 1;
446 /* hardware watchpoint is inserted from low index to high index */
447 nds32_v3m->next_hwp_index = 0;
448
449 LOG_INFO("%s: total hardware breakpoint %d (simple breakpoint %d)",
450 target_name(target), nds32_v3m->n_hbr, nds32_v3m->n_hbr - nds32_v3m->n_hwp);
451 LOG_INFO("%s: total hardware watchpoint %d", target_name(target), nds32_v3m->n_hwp);
452
453 nds32->target->state = TARGET_RUNNING;
454 nds32->target->debug_reason = DBG_REASON_NOTHALTED;
455
456 target_set_examined(target);
457
458 return ERROR_OK;
459 }
460
461 /** Holds methods for NDS32 V3m targets. */
462 struct target_type nds32_v3m_target = {
463 .name = "nds32_v3m",
464
465 .poll = nds32_poll,
466 .arch_state = nds32_arch_state,
467
468 .target_request_data = nds32_v3_target_request_data,
469
470 .halt = nds32_halt,
471 .resume = nds32_resume,
472 .step = nds32_step,
473
474 .assert_reset = nds32_assert_reset,
475 .deassert_reset = nds32_v3m_deassert_reset,
476
477 /* register access */
478 .get_gdb_reg_list = nds32_get_gdb_reg_list,
479
480 /* memory access */
481 .read_buffer = nds32_v3_read_buffer,
482 .write_buffer = nds32_v3_write_buffer,
483 .read_memory = nds32_v3_read_memory,
484 .write_memory = nds32_v3_write_memory,
485
486 .checksum_memory = nds32_v3_checksum_memory,
487
488 /* breakpoint/watchpoint */
489 .add_breakpoint = nds32_v3m_add_breakpoint,
490 .remove_breakpoint = nds32_v3m_remove_breakpoint,
491 .add_watchpoint = nds32_v3m_add_watchpoint,
492 .remove_watchpoint = nds32_v3m_remove_watchpoint,
493 .hit_watchpoint = nds32_v3_hit_watchpoint,
494
495 /* MMU */
496 .mmu = nds32_mmu,
497 .virt2phys = nds32_virtual_to_physical,
498 .read_phys_memory = nds32_read_phys_memory,
499 .write_phys_memory = nds32_write_phys_memory,
500
501 .run_algorithm = nds32_v3_run_algorithm,
502
503 .commands = nds32_command_handlers,
504 .target_create = nds32_v3m_target_create,
505 .init_target = nds32_v3_init_target,
506 .examine = nds32_v3m_examine,
507
508 .get_gdb_fileio_info = nds32_get_gdb_fileio_info,
509 .gdb_fileio_end = nds32_gdb_fileio_end,
510 };