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