diff options
author | Hsiangkai Wang <hsiangkai@gmail.com> | 2013-02-05 11:55:37 +0800 |
---|---|---|
committer | Spencer Oliver <spen@spen-soft.co.uk> | 2013-06-05 19:27:35 +0000 |
commit | cf8a3c3d7075abad3c88cd604f8add4d06898abc (patch) | |
tree | 56f8b5794fd385ba7ba4a6617c214a9516a443b5 /src/target/nds32_v2.c | |
parent | ceb402dc9e903d2f3f6bc8125dfed9d82b83d2d1 (diff) | |
download | riscv-openocd-cf8a3c3d7075abad3c88cd604f8add4d06898abc.zip riscv-openocd-cf8a3c3d7075abad3c88cd604f8add4d06898abc.tar.gz riscv-openocd-cf8a3c3d7075abad3c88cd604f8add4d06898abc.tar.bz2 |
nds32: add new target type nds32_v2, nds32_v3, nds32_v3m
Add target code for Andes targets.
Change-Id: Ibf0e1b61b06127ca7d9ed502d98d7e2aeebbbe82
Signed-off-by: Hsiangkai Wang <hsiangkai@gmail.com>
Reviewed-on: http://openocd.zylin.com/1259
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
Diffstat (limited to 'src/target/nds32_v2.c')
-rw-r--r-- | src/target/nds32_v2.c | 763 |
1 files changed, 763 insertions, 0 deletions
diff --git a/src/target/nds32_v2.c b/src/target/nds32_v2.c new file mode 100644 index 0000000..90961d7 --- /dev/null +++ b/src/target/nds32_v2.c @@ -0,0 +1,763 @@ +/*************************************************************************** + * Copyright (C) 2013 Andes Technology * + * Hsiangkai Wang <hkwang@andestech.com> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <helper/time_support.h> +#include <helper/binarybuffer.h> +#include "breakpoints.h" +#include "nds32_insn.h" +#include "nds32_reg.h" +#include "nds32_edm.h" +#include "nds32_cmd.h" +#include "nds32_v2.h" +#include "nds32_aice.h" +#include "target_type.h" + +static int nds32_v2_register_mapping(struct nds32 *nds32, int reg_no) +{ + uint32_t max_level = nds32->max_interrupt_level; + uint32_t cur_level = nds32->current_interrupt_level; + + if ((1 <= cur_level) && (cur_level < max_level)) { + if (IR0 == reg_no) { + LOG_DEBUG("Map PSW to IPSW"); + return IR1; + } else if (PC == reg_no) { + LOG_DEBUG("Map PC to IPC"); + return IR9; + } + } else if ((2 <= cur_level) && (cur_level < max_level)) { + if (R26 == reg_no) { + LOG_DEBUG("Mapping P0 to P_P0"); + return IR12; + } else if (R27 == reg_no) { + LOG_DEBUG("Mapping P1 to P_P1"); + return IR13; + } else if (IR1 == reg_no) { + LOG_DEBUG("Mapping IPSW to P_IPSW"); + return IR2; + } else if (IR4 == reg_no) { + LOG_DEBUG("Mapping EVA to P_EVA"); + return IR5; + } else if (IR6 == reg_no) { + LOG_DEBUG("Mapping ITYPE to P_ITYPE"); + return IR7; + } else if (IR9 == reg_no) { + LOG_DEBUG("Mapping IPC to P_IPC"); + return IR10; + } + } else if (cur_level == max_level) { + if (PC == reg_no) { + LOG_DEBUG("Mapping PC to O_IPC"); + return IR11; + } + } + + return reg_no; +} + +static int nds32_v2_get_debug_reason(struct nds32 *nds32, uint32_t *reason) +{ + uint32_t val_itype; + struct aice_port_s *aice = target_to_aice(nds32->target); + + aice_read_register(aice, IR6, &val_itype); + + *reason = val_itype & 0x0F; + + return ERROR_OK; +} + +static int nds32_v2_activate_hardware_breakpoint(struct target *target) +{ + struct nds32_v2_common *nds32_v2 = target_to_nds32_v2(target); + struct aice_port_s *aice = target_to_aice(target); + struct breakpoint *bp; + int32_t hbr_index = 0; + + for (bp = target->breakpoints; bp; bp = bp->next) { + if (bp->type == BKPT_SOFT) { + /* already set at nds32_v2_add_breakpoint() */ + continue; + } else if (bp->type == BKPT_HARD) { + /* set address */ + aice_write_debug_reg(aice, NDS_EDM_SR_BPA0 + hbr_index, bp->address); + /* set mask */ + aice_write_debug_reg(aice, NDS_EDM_SR_BPAM0 + hbr_index, 0); + /* set value */ + aice_write_debug_reg(aice, NDS_EDM_SR_BPV0 + hbr_index, 0); + + if (nds32_v2->nds32.memory.address_translation) + /* enable breakpoint (virtual address) */ + aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + hbr_index, 0x2); + else + /* enable breakpoint (physical address) */ + aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + hbr_index, 0xA); + + LOG_DEBUG("Add hardware BP %d at %08" PRIx32, hbr_index, + bp->address); + + hbr_index++; + } else { + return ERROR_FAIL; + } + } + + return ERROR_OK; +} + +static int nds32_v2_deactivate_hardware_breakpoint(struct target *target) +{ + struct aice_port_s *aice = target_to_aice(target); + struct breakpoint *bp; + int32_t hbr_index = 0; + + for (bp = target->breakpoints; bp; bp = bp->next) { + if (bp->type == BKPT_SOFT) + continue; + else if (bp->type == BKPT_HARD) + /* disable breakpoint */ + aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + hbr_index, 0x0); + else + return ERROR_FAIL; + + LOG_DEBUG("Remove hardware BP %d at %08" PRIx32, hbr_index, + bp->address); + + hbr_index++; + } + + return ERROR_OK; +} + +static int nds32_v2_activate_hardware_watchpoint(struct target *target) +{ + struct aice_port_s *aice = target_to_aice(target); + struct nds32_v2_common *nds32_v2 = target_to_nds32_v2(target); + struct watchpoint *wp; + int32_t wp_num = nds32_v2->next_hbr_index; + uint32_t wp_config = 0; + + for (wp = target->watchpoints; wp; wp = wp->next) { + + wp_num--; + wp->mask = wp->length - 1; + if ((wp->address % wp->length) != 0) + wp->mask = (wp->mask << 1) + 1; + + if (wp->rw == WPT_READ) + wp_config = 0x3; + else if (wp->rw == WPT_WRITE) + wp_config = 0x5; + else if (wp->rw == WPT_ACCESS) + wp_config = 0x7; + + /* set/unset physical address bit of BPCn according to PSW.DT */ + if (nds32_v2->nds32.memory.address_translation == false) + wp_config |= 0x8; + + /* set address */ + aice_write_debug_reg(aice, NDS_EDM_SR_BPA0 + wp_num, + wp->address - (wp->address % wp->length)); + /* set mask */ + aice_write_debug_reg(aice, NDS_EDM_SR_BPAM0 + wp_num, wp->mask); + /* enable watchpoint */ + aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + wp_num, wp_config); + /* set value */ + aice_write_debug_reg(aice, NDS_EDM_SR_BPV0 + wp_num, 0); + + LOG_DEBUG("Add hardware wathcpoint %d at %08" PRIx32 " mask %08" PRIx32, wp_num, + wp->address, wp->mask); + + } + + return ERROR_OK; +} + +static int nds32_v2_deactivate_hardware_watchpoint(struct target *target) +{ + struct aice_port_s *aice = target_to_aice(target); + struct nds32_v2_common *nds32_v2 = target_to_nds32_v2(target); + int32_t wp_num = nds32_v2->next_hbr_index; + struct watchpoint *wp; + + for (wp = target->watchpoints; wp; wp = wp->next) { + wp_num--; + /* disable watchpoint */ + aice_write_debug_reg(aice, NDS_EDM_SR_BPC0 + wp_num, 0x0); + + LOG_DEBUG("Remove hardware wathcpoint %d at %08" PRIx32 " mask %08" PRIx32, + wp_num, wp->address, wp->mask); + } + + return ERROR_OK; +} + +static int nds32_v2_check_interrupt_stack(struct nds32_v2_common *nds32_v2) +{ + struct nds32 *nds32 = &(nds32_v2->nds32); + struct aice_port_s *aice = target_to_aice(nds32->target); + uint32_t val_ir0; + uint32_t val_ir1; + uint32_t val_ir2; + uint32_t modified_psw; + + /* Save interrupt level */ + aice_read_register(aice, IR0, &val_ir0); /* get $IR0 directly */ + + /* backup $IR0 */ + nds32_v2->backup_ir0 = val_ir0; + + nds32->current_interrupt_level = (val_ir0 >> 1) & 0x3; + + if (nds32_reach_max_interrupt_level(nds32)) { + LOG_ERROR("<-- TARGET ERROR! Reaching the max interrupt stack level %d. -->", + nds32->current_interrupt_level); + + /* decrease interrupt level */ + modified_psw = val_ir0 - 0x2; + + /* disable GIE, IT, DT, HSS */ + modified_psw &= (~0x8C1); + + aice_write_register(aice, IR0, modified_psw); + + return ERROR_OK; + } + + + /* There is a case that single step also trigger another interrupt, + then HSS bit in psw(ir0) will push to ipsw(ir1). + Then hit debug interrupt HSS bit in ipsw(ir1) will push to (p_ipsw)ir2 + Therefore, HSS bit in p_ipsw(ir2) also need clear. + + Only update $ir2 as current interrupt level is 2, because $ir2 will be random + value if the target never reaches interrupt level 2. */ + if ((nds32->max_interrupt_level == 3) && (nds32->current_interrupt_level == 2)) { + aice_read_register(aice, IR2, &val_ir2); /* get $IR2 directly */ + val_ir2 &= ~(0x01 << 11); + aice_write_register(aice, IR2, val_ir2); + } + + /* get origianl DT bit and set to current state let debugger has same memory view + PSW.IT MUST be turned off. Otherwise, DIM could not operate normally. */ + aice_read_register(aice, IR1, &val_ir1); + modified_psw = val_ir0 | (val_ir1 & 0x80); + aice_write_register(aice, IR0, modified_psw); + + return ERROR_OK; +} + +static int nds32_v2_restore_interrupt_stack(struct nds32_v2_common *nds32_v2) +{ + struct nds32 *nds32 = &(nds32_v2->nds32); + struct aice_port_s *aice = target_to_aice(nds32->target); + + /* restore origin $IR0 */ + aice_write_register(aice, IR0, nds32_v2->backup_ir0); + + return ERROR_OK; +} + +/** + * Save processor state. This is called after a HALT instruction + * succeeds, and on other occasions the processor enters debug mode + * (breakpoint, watchpoint, etc). + */ +static int nds32_v2_debug_entry(struct nds32 *nds32, bool enable_watchpoint) +{ + LOG_DEBUG("nds32_v2_debug_entry"); + + jtag_poll_set_enabled(false); + + if (nds32->virtual_hosting) + LOG_WARNING("<-- TARGET WARNING! Virtual hosting is not supported " + "under V1/V2 architecture. -->"); + + struct nds32_v2_common *nds32_v2 = target_to_nds32_v2(nds32->target); + + CHECK_RETVAL(nds32_v2_deactivate_hardware_breakpoint(nds32->target)); + + if (enable_watchpoint) + CHECK_RETVAL(nds32_v2_deactivate_hardware_watchpoint(nds32->target)); + + nds32->target->state = TARGET_HALTED; + nds32_examine_debug_reason(nds32); + + if (nds32->init_arch_info_after_halted == false) { + /* init architecture info according to config registers */ + CHECK_RETVAL(nds32_config(nds32)); + + nds32->init_arch_info_after_halted = true; + } + + /* REVISIT entire cache should already be invalid !!! */ + register_cache_invalidate(nds32->core_cache); + + /* check interrupt level before .full_context(), because + * get_mapped_reg needs current_interrupt_level information */ + nds32_v2_check_interrupt_stack(nds32_v2); + + /* Save registers. */ + nds32_full_context(nds32); + + return ERROR_OK; +} + +/* target request support */ +static int nds32_v2_target_request_data(struct target *target, + uint32_t size, uint8_t *buffer) +{ + /* AndesCore could use DTR register to communicate with OpenOCD + * to output messages + * Target data will be put in buffer + * The format of DTR is as follow + * DTR[31:16] => length, DTR[15:8] => size, DTR[7:0] => target_req_cmd + * target_req_cmd has three possible values: + * TARGET_REQ_TRACEMSG + * TARGET_REQ_DEBUGMSG + * TARGET_REQ_DEBUGCHAR + * if size == 0, target will call target_asciimsg(), + * else call target_hexmsg() + */ + LOG_WARNING("Not implemented: %s", __func__); + + return ERROR_OK; +} + +/** + * Restore processor state. + */ +static int nds32_v2_leave_debug_state(struct nds32 *nds32, bool enable_watchpoint) +{ + struct nds32_v2_common *nds32_v2 = target_to_nds32_v2(nds32->target); + + /* activate all hardware breakpoints */ + CHECK_RETVAL(nds32_v2_activate_hardware_breakpoint(nds32->target)); + + if (enable_watchpoint) { + /* activate all watchpoints */ + CHECK_RETVAL(nds32_v2_activate_hardware_watchpoint(nds32->target)); + } + + /* restore interrupt stack */ + nds32_v2_restore_interrupt_stack(nds32_v2); + + /* restore PSW, PC, and R0 ... after flushing any modified + * registers. + */ + CHECK_RETVAL(nds32_restore_context(nds32->target)); + + register_cache_invalidate(nds32->core_cache); + + jtag_poll_set_enabled(true); + + return ERROR_OK; +} + +static int nds32_v2_soft_reset_halt(struct target *target) +{ + /* TODO: test it */ + struct nds32 *nds32 = target_to_nds32(target); + struct aice_port_s *aice = target_to_aice(target); + + aice_assert_srst(aice, AICE_SRST); + + /* halt core and set pc to 0x0 */ + int retval = target_halt(target); + if (retval != ERROR_OK) + return retval; + + /* start fetching from IVB */ + uint32_t value_ir3; + nds32_get_mapped_reg(nds32, IR3, &value_ir3); + nds32_set_mapped_reg(nds32, PC, value_ir3 & 0xFFFF0000); + + return ERROR_OK; +} + +static int nds32_v2_deassert_reset(struct target *target) +{ + int retval; + + CHECK_RETVAL(nds32_poll(target)); + + if (target->state != TARGET_HALTED) { + /* reset only */ + LOG_WARNING("%s: ran after reset and before halt ...", + target_name(target)); + retval = target_halt(target); + if (retval != ERROR_OK) + return retval; + /* call target_poll() to avoid "Halt timed out" */ + CHECK_RETVAL(target_poll(target)); + } else { + jtag_poll_set_enabled(false); + } + + return ERROR_OK; +} + +static int nds32_v2_checksum_memory(struct target *target, + uint32_t address, uint32_t count, uint32_t *checksum) +{ + LOG_WARNING("Not implemented: %s", __func__); + + return ERROR_FAIL; +} + +static int nds32_v2_add_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + struct nds32_v2_common *nds32_v2 = target_to_nds32_v2(target); + struct nds32 *nds32 = &(nds32_v2->nds32); + int result; + + if (breakpoint->type == BKPT_HARD) { + /* check hardware resource */ + if (nds32_v2->n_hbr <= nds32_v2->next_hbr_index) { + LOG_WARNING("<-- TARGET WARNING! Insert too many hardware " + "breakpoints/watchpoints! The limit of " + "combined hardware breakpoints/watchpoints " + "is %d. -->", nds32_v2->n_hbr); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* update next place to put hardware breakpoint */ + nds32_v2->next_hbr_index++; + + /* hardware breakpoint insertion occurs before 'continue' actually */ + return ERROR_OK; + } else if (breakpoint->type == BKPT_SOFT) { + result = nds32_add_software_breakpoint(target, breakpoint); + if (ERROR_OK != result) { + /* auto convert to hardware breakpoint if failed */ + if (nds32->auto_convert_hw_bp) { + /* convert to hardware breakpoint */ + breakpoint->type = BKPT_HARD; + + return nds32_v2_add_breakpoint(target, breakpoint); + } + } + + return result; + } else /* unrecognized breakpoint type */ + return ERROR_FAIL; + + return ERROR_OK; +} + +static int nds32_v2_remove_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + struct nds32_v2_common *nds32_v2 = target_to_nds32_v2(target); + + if (breakpoint->type == BKPT_HARD) { + if (nds32_v2->next_hbr_index <= 0) + return ERROR_FAIL; + + /* update next place to put hardware breakpoint */ + nds32_v2->next_hbr_index--; + + /* hardware breakpoint removal occurs after 'halted' actually */ + return ERROR_OK; + } else if (breakpoint->type == BKPT_SOFT) { + return nds32_remove_software_breakpoint(target, breakpoint); + } else /* unrecognized breakpoint type */ + return ERROR_FAIL; + + return ERROR_OK; +} + +static int nds32_v2_add_watchpoint(struct target *target, + struct watchpoint *watchpoint) +{ + struct nds32_v2_common *nds32_v2 = target_to_nds32_v2(target); + + /* check hardware resource */ + if (nds32_v2->n_hbr <= nds32_v2->next_hbr_index) { + LOG_WARNING("<-- TARGET WARNING! Insert too many hardware " + "breakpoints/watchpoints! The limit of " + "combined hardware breakpoints/watchpoints is %d. -->", nds32_v2->n_hbr); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* update next place to put hardware watchpoint */ + nds32_v2->next_hbr_index++; + + return ERROR_OK; +} + +static int nds32_v2_remove_watchpoint(struct target *target, + struct watchpoint *watchpoint) +{ + struct nds32_v2_common *nds32_v2 = target_to_nds32_v2(target); + + if (nds32_v2->next_hbr_index <= 0) + return ERROR_FAIL; + + /* update next place to put hardware breakpoint */ + nds32_v2->next_hbr_index--; + + return ERROR_OK; +} + +static int nds32_v2_get_exception_address(struct nds32 *nds32, + uint32_t *address, uint32_t reason) +{ + struct aice_port_s *aice = target_to_aice(nds32->target); + + aice_read_register(aice, IR4, address); /* read $EVA directly */ + + /* TODO: hit multiple watchpoints */ + + return ERROR_OK; +} + +static int nds32_v2_run_algorithm(struct target *target, + int num_mem_params, + struct mem_param *mem_params, + int num_reg_params, + struct reg_param *reg_params, + uint32_t entry_point, + uint32_t exit_point, + int timeout_ms, + void *arch_info) +{ + LOG_WARNING("Not implemented: %s", __func__); + + return ERROR_FAIL; +} + +static int nds32_v2_target_create(struct target *target, Jim_Interp *interp) +{ + struct nds32_v2_common *nds32_v2; + + nds32_v2 = calloc(1, sizeof(*nds32_v2)); + if (!nds32_v2) + return ERROR_FAIL; + + nds32_v2->nds32.register_map = nds32_v2_register_mapping; + nds32_v2->nds32.get_debug_reason = nds32_v2_get_debug_reason; + nds32_v2->nds32.enter_debug_state = nds32_v2_debug_entry; + nds32_v2->nds32.leave_debug_state = nds32_v2_leave_debug_state; + nds32_v2->nds32.get_watched_address = nds32_v2_get_exception_address; + + nds32_init_arch_info(target, &(nds32_v2->nds32)); + + return ERROR_OK; +} + +static int nds32_v2_init_target(struct command_context *cmd_ctx, + struct target *target) +{ + /* Initialize anything we can set up without talking to the target */ + + struct nds32 *nds32 = target_to_nds32(target); + + nds32_init(nds32); + + return ERROR_OK; +} + +/* talk to the target and set things up */ +static int nds32_v2_examine(struct target *target) +{ + struct nds32_v2_common *nds32_v2 = target_to_nds32_v2(target); + struct nds32 *nds32 = &(nds32_v2->nds32); + struct aice_port_s *aice = target_to_aice(target); + + if (!target_was_examined(target)) { + CHECK_RETVAL(nds32_edm_config(nds32)); + + if (nds32->reset_halt_as_examine) + CHECK_RETVAL(nds32_reset_halt(nds32)); + } + + uint32_t edm_cfg; + aice_read_debug_reg(aice, NDS_EDM_SR_EDM_CFG, &edm_cfg); + + /* get the number of hardware breakpoints */ + nds32_v2->n_hbr = (edm_cfg & 0x7) + 1; + + nds32_v2->next_hbr_index = 0; + + LOG_INFO("%s: total hardware breakpoint %d", target_name(target), + nds32_v2->n_hbr); + + nds32->target->state = TARGET_RUNNING; + nds32->target->debug_reason = DBG_REASON_NOTHALTED; + + target_set_examined(target); + + return ERROR_OK; +} + +static int nds32_v2_translate_address(struct target *target, uint32_t *address) +{ + struct nds32 *nds32 = target_to_nds32(target); + struct nds32_memory *memory = &(nds32->memory); + uint32_t physical_address; + + /* Following conditions need to do address translation + * 1. BUS mode + * 2. CPU mode under maximum interrupt level */ + if ((NDS_MEMORY_ACC_BUS == memory->access_channel) || + ((NDS_MEMORY_ACC_CPU == memory->access_channel) && + nds32_reach_max_interrupt_level(nds32))) { + if (ERROR_OK == target->type->virt2phys(target, *address, &physical_address)) + *address = physical_address; + else + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int nds32_v2_read_buffer(struct target *target, uint32_t address, + uint32_t size, uint8_t *buffer) +{ + struct nds32 *nds32 = target_to_nds32(target); + struct nds32_memory *memory = &(nds32->memory); + + if ((NDS_MEMORY_ACC_CPU == memory->access_channel) && + (target->state != TARGET_HALTED)) { + LOG_WARNING("target was not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* BUG: If access range crosses multiple pages, the translation will not correct + * for second page or so. */ + + nds32_v2_translate_address(target, &address); + + return nds32_read_buffer(target, address, size, buffer); +} + +static int nds32_v2_write_buffer(struct target *target, uint32_t address, + uint32_t size, const uint8_t *buffer) +{ + struct nds32 *nds32 = target_to_nds32(target); + struct nds32_memory *memory = &(nds32->memory); + + if ((NDS_MEMORY_ACC_CPU == memory->access_channel) && + (target->state != TARGET_HALTED)) { + LOG_WARNING("target was not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* BUG: If access range crosses multiple pages, the translation will not correct + * for second page or so. */ + + nds32_v2_translate_address(target, &address); + + return nds32_write_buffer(target, address, size, buffer); +} + +static int nds32_v2_read_memory(struct target *target, uint32_t address, + uint32_t size, uint32_t count, uint8_t *buffer) +{ + struct nds32 *nds32 = target_to_nds32(target); + struct nds32_memory *memory = &(nds32->memory); + + if ((NDS_MEMORY_ACC_CPU == memory->access_channel) && + (target->state != TARGET_HALTED)) { + LOG_WARNING("target was not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* BUG: If access range crosses multiple pages, the translation will not correct + * for second page or so. */ + + nds32_v2_translate_address(target, &address); + + return nds32_read_memory(target, address, size, count, buffer); +} + +static int nds32_v2_write_memory(struct target *target, uint32_t address, + uint32_t size, uint32_t count, const uint8_t *buffer) +{ + struct nds32 *nds32 = target_to_nds32(target); + struct nds32_memory *memory = &(nds32->memory); + + if ((NDS_MEMORY_ACC_CPU == memory->access_channel) && + (target->state != TARGET_HALTED)) { + LOG_WARNING("target was not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* BUG: If access range crosses multiple pages, the translation will not correct + * for second page or so. */ + + nds32_v2_translate_address(target, &address); + + return nds32_write_memory(target, address, size, count, buffer); +} + +/** Holds methods for V2 targets. */ +struct target_type nds32_v2_target = { + .name = "nds32_v2", + + .poll = nds32_poll, + .arch_state = nds32_arch_state, + + .target_request_data = nds32_v2_target_request_data, + + .halt = nds32_halt, + .resume = nds32_resume, + .step = nds32_step, + + .assert_reset = nds32_assert_reset, + .deassert_reset = nds32_v2_deassert_reset, + .soft_reset_halt = nds32_v2_soft_reset_halt, + + /* register access */ + .get_gdb_reg_list = nds32_get_gdb_reg_list, + + /* memory access */ + .read_buffer = nds32_v2_read_buffer, + .write_buffer = nds32_v2_write_buffer, + .read_memory = nds32_v2_read_memory, + .write_memory = nds32_v2_write_memory, + + .checksum_memory = nds32_v2_checksum_memory, + + /* breakpoint/watchpoint */ + .add_breakpoint = nds32_v2_add_breakpoint, + .remove_breakpoint = nds32_v2_remove_breakpoint, + .add_watchpoint = nds32_v2_add_watchpoint, + .remove_watchpoint = nds32_v2_remove_watchpoint, + + /* MMU */ + .mmu = nds32_mmu, + .virt2phys = nds32_virtual_to_physical, + .read_phys_memory = nds32_read_phys_memory, + .write_phys_memory = nds32_write_phys_memory, + + .run_algorithm = nds32_v2_run_algorithm, + + .commands = nds32_command_handlers, + .target_create = nds32_v2_target_create, + .init_target = nds32_v2_init_target, + .examine = nds32_v2_examine, +}; |