-/* SPDX-License-Identifier: GPL-2.0-or-later */
+// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
- * Generic Xtensa debug module API for OpenOCD *
+ * Xtensa Debug Module (XDM) Support for OpenOCD *
+ * Copyright (C) 2020-2022 Cadence Design Systems, Inc. *
* Copyright (C) 2019 Espressif Systems Ltd. *
***************************************************************************/
#include <config.h>
#endif
+#include <helper/align.h>
#include "xtensa_debug_module.h"
#define TAPINS_PWRCTL 0x08
#define TAPINS_IDCODE_LEN 32
#define TAPINS_BYPASS_LEN 1
+/* Table of power register offsets for APB space */
+static const struct xtensa_dm_pwr_reg_offsets xdm_pwr_regs[XDMREG_PWRNUM] =
+ XTENSA_DM_PWR_REG_OFFSETS;
+
+/* Table of debug register offsets for Nexus and APB space */
+static const struct xtensa_dm_reg_offsets xdm_regs[XDMREG_NUM] =
+ XTENSA_DM_REG_OFFSETS;
static void xtensa_dm_add_set_ir(struct xtensa_debug_module *dm, uint8_t value)
{
struct scan_field field;
- uint8_t t[4] = { 0 };
+ uint8_t t[4] = { 0, 0, 0, 0 };
memset(&field, 0, sizeof(field));
field.num_bits = dm->tap->ir_length;
{
if (!dm || !cfg)
return ERROR_FAIL;
+ if (!IS_ALIGNED(cfg->ap_offset, XTENSA_DM_APB_ALIGN)) {
+ LOG_ERROR("Xtensa DM APB offset must be aligned to a %dKB multiple",
+ XTENSA_DM_APB_ALIGN / 1024);
+ return ERROR_FAIL;
+ }
dm->pwr_ops = cfg->pwr_ops;
dm->dbg_ops = cfg->dbg_ops;
dm->tap = cfg->tap;
dm->queue_tdi_idle = cfg->queue_tdi_idle;
dm->queue_tdi_idle_arg = cfg->queue_tdi_idle_arg;
+ dm->dap = cfg->dap;
+ dm->debug_ap = cfg->debug_ap;
+ dm->debug_apsel = cfg->debug_apsel;
+ dm->ap_offset = cfg->ap_offset;
return ERROR_OK;
}
-int xtensa_dm_queue_enable(struct xtensa_debug_module *dm)
+void xtensa_dm_deinit(struct xtensa_debug_module *dm)
{
- return dm->dbg_ops->queue_reg_write(dm, NARADR_DCRSET, OCDDCR_ENABLEOCD);
+ if (dm->debug_ap) {
+ dap_put_ap(dm->debug_ap);
+ dm->debug_ap = NULL;
+ }
}
-int xtensa_dm_queue_reg_read(struct xtensa_debug_module *dm, unsigned int reg, uint8_t *value)
+int xtensa_dm_poll(struct xtensa_debug_module *dm)
{
- uint8_t regdata = (reg << 1) | 0;
- uint8_t dummy[4] = { 0, 0, 0, 0 };
+ /* Check if debug_ap is available to prevent segmentation fault.
+ * If the re-examination after an error does not find a MEM-AP
+ * (e.g. the target stopped communicating), debug_ap pointer
+ * can suddenly become NULL.
+ */
+ return (!dm || (dm->dap && !dm->debug_ap)) ? ERROR_FAIL : ERROR_OK;
+}
+
+int xtensa_dm_examine(struct xtensa_debug_module *dm)
+{
+ struct adiv5_dap *swjdp = dm->dap;
+ int retval = ERROR_OK;
+
+ if (swjdp) {
+ LOG_DEBUG("DM examine: DAP AP select %d", dm->debug_apsel);
+ if (dm->debug_ap) {
+ dap_put_ap(dm->debug_ap);
+ dm->debug_ap = NULL;
+ }
+ if (dm->debug_apsel == DP_APSEL_INVALID) {
+ LOG_DEBUG("DM examine: search for APB-type MEM-AP...");
+ /* TODO: Determine whether AP_TYPE_AXI_AP APs can be supported... */
+ retval = dap_find_get_ap(swjdp, AP_TYPE_APB_AP, &dm->debug_ap);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Could not find MEM-AP to control the core");
+ return retval;
+ }
+ } else {
+ dm->debug_ap = dap_get_ap(swjdp, dm->debug_apsel);
+ }
+
+ /* TODO: Allow a user-specified AP instead of relying on AP_TYPE_APB_AP */
+ dm->debug_apsel = dm->debug_ap->ap_num;
+ LOG_DEBUG("DM examine: Setting apsel to %d", dm->debug_apsel);
+
+ /* Leave (only) generic DAP stuff for debugport_init(); */
+ dm->debug_ap->memaccess_tck = 8;
+
+ retval = mem_ap_init(dm->debug_ap);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("MEM-AP init failed: %d", retval);
+ return retval;
+ }
+
+ /* TODO: how to set autoincrement range? Hard-code it to 1024 bytes for now */
+ dm->debug_ap->tar_autoincr_block = (1 << 10);
+ }
- if (reg > NARADR_MAX) {
+ return retval;
+}
+
+int xtensa_dm_queue_enable(struct xtensa_debug_module *dm)
+{
+ return dm->dbg_ops->queue_reg_write(dm, XDMREG_DCRSET, OCDDCR_ENABLEOCD);
+}
+
+int xtensa_dm_queue_reg_read(struct xtensa_debug_module *dm, enum xtensa_dm_reg reg, uint8_t *value)
+{
+ if (reg >= XDMREG_NUM) {
LOG_ERROR("Invalid DBG reg ID %d!", reg);
return ERROR_FAIL;
}
+ if (dm->dap)
+ /* NOTE: Future optimization: mem_ap_read_u32() offers higher performance with
+ * queued reads, but requires an API change to pass value as a 32-bit pointer.
+ */
+ return mem_ap_read_buf(dm->debug_ap, value, 4, 1, xdm_regs[reg].apb + dm->ap_offset);
+ uint8_t regdata = (xdm_regs[reg].nar << 1) | 0;
+ uint8_t dummy[4] = { 0, 0, 0, 0 };
xtensa_dm_add_set_ir(dm, TAPINS_NARSEL);
xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_ADRLEN, ®data, NULL, TAP_IDLE);
xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_DATALEN, dummy, value, TAP_IDLE);
return ERROR_OK;
}
-int xtensa_dm_queue_reg_write(struct xtensa_debug_module *dm, unsigned int reg, uint32_t value)
+int xtensa_dm_queue_reg_write(struct xtensa_debug_module *dm, enum xtensa_dm_reg reg, uint32_t value)
{
- uint8_t regdata = (reg << 1) | 1;
- uint8_t valdata[] = { value, value >> 8, value >> 16, value >> 24 };
-
- if (reg > NARADR_MAX) {
+ if (reg >= XDMREG_NUM) {
LOG_ERROR("Invalid DBG reg ID %d!", reg);
return ERROR_FAIL;
}
+ if (dm->dap)
+ return mem_ap_write_u32(dm->debug_ap, xdm_regs[reg].apb + dm->ap_offset, value);
+ uint8_t regdata = (xdm_regs[reg].nar << 1) | 1;
+ uint8_t valdata[] = { value, value >> 8, value >> 16, value >> 24 };
xtensa_dm_add_set_ir(dm, TAPINS_NARSEL);
xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_ADRLEN, ®data, NULL, TAP_IDLE);
xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_DATALEN, valdata, NULL, TAP_IDLE);
return ERROR_OK;
}
-int xtensa_dm_queue_pwr_reg_read(struct xtensa_debug_module *dm, unsigned int reg, uint8_t *data, uint8_t clear)
+int xtensa_dm_queue_pwr_reg_read(struct xtensa_debug_module *dm,
+ enum xtensa_dm_pwr_reg reg,
+ uint8_t *data,
+ uint32_t clear)
{
- uint8_t value_clr = clear;
- uint8_t tap_insn;
- int tap_insn_sz;
-
- if (reg == DMREG_PWRCTL) {
- tap_insn = TAPINS_PWRCTL;
- tap_insn_sz = TAPINS_PWRCTL_LEN;
- } else if (reg == DMREG_PWRSTAT) {
- tap_insn = TAPINS_PWRSTAT;
- tap_insn_sz = TAPINS_PWRSTAT_LEN;
- } else {
+ if (reg >= XDMREG_PWRNUM) {
LOG_ERROR("Invalid PWR reg ID %d!", reg);
return ERROR_FAIL;
}
+ if (dm->dap) {
+ /* NOTE: Future optimization: mem_ap_read_u32() offers higher performance with
+ * queued reads, but requires an API change to pass value as a 32-bit pointer.
+ */
+ uint32_t apbreg = xdm_pwr_regs[reg].apb + dm->ap_offset;
+ int retval = mem_ap_read_buf(dm->debug_ap, data, 4, 1, apbreg);
+ if (retval == ERROR_OK)
+ retval = mem_ap_write_u32(dm->debug_ap, apbreg, clear);
+ return retval;
+ }
+ uint8_t value_clr = (uint8_t)clear;
+ uint8_t tap_insn = (reg == XDMREG_PWRCTL) ? TAPINS_PWRCTL : TAPINS_PWRSTAT;
+ int tap_insn_sz = (reg == XDMREG_PWRCTL) ? TAPINS_PWRCTL_LEN : TAPINS_PWRSTAT_LEN;
xtensa_dm_add_set_ir(dm, tap_insn);
xtensa_dm_add_dr_scan(dm, tap_insn_sz, &value_clr, data, TAP_IDLE);
return ERROR_OK;
}
-int xtensa_dm_queue_pwr_reg_write(struct xtensa_debug_module *dm, unsigned int reg, uint8_t data)
+int xtensa_dm_queue_pwr_reg_write(struct xtensa_debug_module *dm,
+ enum xtensa_dm_pwr_reg reg,
+ uint32_t data)
{
- uint8_t value = data;
- uint8_t tap_insn;
- int tap_insn_sz;
-
- if (reg == DMREG_PWRCTL) {
- tap_insn = TAPINS_PWRCTL;
- tap_insn_sz = TAPINS_PWRCTL_LEN;
- } else if (reg == DMREG_PWRSTAT) {
- tap_insn = TAPINS_PWRSTAT;
- tap_insn_sz = TAPINS_PWRSTAT_LEN;
- } else {
+ if (reg >= XDMREG_PWRNUM) {
LOG_ERROR("Invalid PWR reg ID %d!", reg);
return ERROR_FAIL;
}
+ if (dm->dap) {
+ uint32_t apbreg = xdm_pwr_regs[reg].apb + dm->ap_offset;
+ return mem_ap_write_u32(dm->debug_ap, apbreg, data);
+ }
+ uint8_t tap_insn = (reg == XDMREG_PWRCTL) ? TAPINS_PWRCTL : TAPINS_PWRSTAT;
+ int tap_insn_sz = (reg == XDMREG_PWRCTL) ? TAPINS_PWRCTL_LEN : TAPINS_PWRSTAT_LEN;
+ uint8_t value = (uint8_t)data;
xtensa_dm_add_set_ir(dm, tap_insn);
xtensa_dm_add_dr_scan(dm, tap_insn_sz, &value, NULL, TAP_IDLE);
return ERROR_OK;
{
uint8_t id_buf[sizeof(uint32_t)];
- dm->dbg_ops->queue_reg_read(dm, NARADR_OCDID, id_buf);
+ dm->dbg_ops->queue_reg_read(dm, XDMREG_OCDID, id_buf);
xtensa_dm_queue_tdi_idle(dm);
- int res = jtag_execute_queue();
+ int res = xtensa_dm_queue_execute(dm);
if (res != ERROR_OK)
return res;
dm->device_id = buf_get_u32(id_buf, 0, 32);
int xtensa_dm_power_status_read(struct xtensa_debug_module *dm, uint32_t clear)
{
- /* uint8_t id_buf[sizeof(uint32_t)]; */
+ uint8_t stat_buf[sizeof(uint32_t)] = { 0, 0, 0, 0 };
+ uint8_t stath_buf[sizeof(uint32_t)] = { 0, 0, 0, 0 };
/* TODO: JTAG does not work when PWRCTL_JTAGDEBUGUSE is not set.
- * It is set in xtensa_examine(), need to move reading of NARADR_OCDID out of this function */
- /* dm->dbg_ops->queue_reg_read(dm, NARADR_OCDID, id_buf);
+ * It is set in xtensa_examine(), need to move reading of XDMREG_OCDID out of this function */
+ /* dm->dbg_ops->queue_reg_read(dm, XDMREG_OCDID, id_buf);
*Read reset state */
- dm->pwr_ops->queue_reg_read(dm, DMREG_PWRSTAT, &dm->power_status.stat, clear);
- dm->pwr_ops->queue_reg_read(dm, DMREG_PWRSTAT, &dm->power_status.stath, clear);
+ dm->pwr_ops->queue_reg_read(dm, XDMREG_PWRSTAT, stat_buf, clear);
+ dm->pwr_ops->queue_reg_read(dm, XDMREG_PWRSTAT, stath_buf, clear);
xtensa_dm_queue_tdi_idle(dm);
- return jtag_execute_queue();
+ int res = xtensa_dm_queue_execute(dm);
+ if (res != ERROR_OK)
+ return res;
+ dm->power_status.stat = buf_get_u32(stat_buf, 0, 32);
+ dm->power_status.stath = buf_get_u32(stath_buf, 0, 32);
+ return res;
}
int xtensa_dm_core_status_read(struct xtensa_debug_module *dm)
uint8_t dsr_buf[sizeof(uint32_t)];
xtensa_dm_queue_enable(dm);
- dm->dbg_ops->queue_reg_read(dm, NARADR_DSR, dsr_buf);
+ dm->dbg_ops->queue_reg_read(dm, XDMREG_DSR, dsr_buf);
xtensa_dm_queue_tdi_idle(dm);
- int res = jtag_execute_queue();
+ int res = xtensa_dm_queue_execute(dm);
if (res != ERROR_OK)
return res;
dm->core_status.dsr = buf_get_u32(dsr_buf, 0, 32);
int xtensa_dm_core_status_clear(struct xtensa_debug_module *dm, xtensa_dsr_t bits)
{
- dm->dbg_ops->queue_reg_write(dm, NARADR_DSR, bits);
+ dm->dbg_ops->queue_reg_write(dm, XDMREG_DSR, bits);
xtensa_dm_queue_tdi_idle(dm);
- return jtag_execute_queue();
+ return xtensa_dm_queue_execute(dm);
}
int xtensa_dm_trace_start(struct xtensa_debug_module *dm, struct xtensa_trace_start_config *cfg)
{
/*Turn off trace unit so we can start a new trace. */
- dm->dbg_ops->queue_reg_write(dm, NARADR_TRAXCTRL, 0);
+ dm->dbg_ops->queue_reg_write(dm, XDMREG_TRAXCTRL, 0);
xtensa_dm_queue_tdi_idle(dm);
- int res = jtag_execute_queue();
+ int res = xtensa_dm_queue_execute(dm);
if (res != ERROR_OK)
return res;
/*Set up parameters */
- dm->dbg_ops->queue_reg_write(dm, NARADR_TRAXADDR, 0);
+ dm->dbg_ops->queue_reg_write(dm, XDMREG_TRAXADDR, 0);
if (cfg->stopmask != XTENSA_STOPMASK_DISABLED) {
- dm->dbg_ops->queue_reg_write(dm, NARADR_PCMATCHCTRL,
+ dm->dbg_ops->queue_reg_write(dm, XDMREG_PCMATCHCTRL,
(cfg->stopmask << PCMATCHCTRL_PCML_SHIFT));
- dm->dbg_ops->queue_reg_write(dm, NARADR_TRIGGERPC, cfg->stoppc);
+ dm->dbg_ops->queue_reg_write(dm, XDMREG_TRIGGERPC, cfg->stoppc);
}
- dm->dbg_ops->queue_reg_write(dm, NARADR_DELAYCNT, cfg->after);
+ dm->dbg_ops->queue_reg_write(dm, XDMREG_DELAYCNT, cfg->after);
/*Options are mostly hardcoded for now. ToDo: make this more configurable. */
dm->dbg_ops->queue_reg_write(
dm,
- NARADR_TRAXCTRL,
+ XDMREG_TRAXCTRL,
TRAXCTRL_TREN |
((cfg->stopmask != XTENSA_STOPMASK_DISABLED) ? TRAXCTRL_PCMEN : 0) | TRAXCTRL_TMEN |
(cfg->after_is_words ? 0 : TRAXCTRL_CNTU) | (0 << TRAXCTRL_SMPER_SHIFT) | TRAXCTRL_PTOWS);
xtensa_dm_queue_tdi_idle(dm);
- return jtag_execute_queue();
+ return xtensa_dm_queue_execute(dm);
}
int xtensa_dm_trace_stop(struct xtensa_debug_module *dm, bool pto_enable)
uint32_t traxctl;
struct xtensa_trace_status trace_status;
- dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXCTRL, traxctl_buf);
+ dm->dbg_ops->queue_reg_read(dm, XDMREG_TRAXCTRL, traxctl_buf);
xtensa_dm_queue_tdi_idle(dm);
- int res = jtag_execute_queue();
+ int res = xtensa_dm_queue_execute(dm);
if (res != ERROR_OK)
return res;
traxctl = buf_get_u32(traxctl_buf, 0, 32);
if (!pto_enable)
traxctl &= ~(TRAXCTRL_PTOWS | TRAXCTRL_PTOWT);
- dm->dbg_ops->queue_reg_write(dm, NARADR_TRAXCTRL, traxctl | TRAXCTRL_TRSTP);
+ dm->dbg_ops->queue_reg_write(dm, XDMREG_TRAXCTRL, traxctl | TRAXCTRL_TRSTP);
xtensa_dm_queue_tdi_idle(dm);
- res = jtag_execute_queue();
+ res = xtensa_dm_queue_execute(dm);
if (res != ERROR_OK)
return res;
{
uint8_t traxstat_buf[sizeof(uint32_t)];
- dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXSTAT, traxstat_buf);
+ dm->dbg_ops->queue_reg_read(dm, XDMREG_TRAXSTAT, traxstat_buf);
xtensa_dm_queue_tdi_idle(dm);
- int res = jtag_execute_queue();
+ int res = xtensa_dm_queue_execute(dm);
if (res == ERROR_OK && status)
status->stat = buf_get_u32(traxstat_buf, 0, 32);
return res;
if (!config)
return ERROR_FAIL;
- dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXCTRL, traxctl_buf);
- dm->dbg_ops->queue_reg_read(dm, NARADR_MEMADDRSTART, memadrstart_buf);
- dm->dbg_ops->queue_reg_read(dm, NARADR_MEMADDREND, memadrend_buf);
- dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXADDR, adr_buf);
+ dm->dbg_ops->queue_reg_read(dm, XDMREG_TRAXCTRL, traxctl_buf);
+ dm->dbg_ops->queue_reg_read(dm, XDMREG_MEMADDRSTART, memadrstart_buf);
+ dm->dbg_ops->queue_reg_read(dm, XDMREG_MEMADDREND, memadrend_buf);
+ dm->dbg_ops->queue_reg_read(dm, XDMREG_TRAXADDR, adr_buf);
xtensa_dm_queue_tdi_idle(dm);
- int res = jtag_execute_queue();
+ int res = xtensa_dm_queue_execute(dm);
if (res == ERROR_OK) {
config->ctrl = buf_get_u32(traxctl_buf, 0, 32);
config->memaddr_start = buf_get_u32(memadrstart_buf, 0, 32);
return ERROR_FAIL;
for (unsigned int i = 0; i < size / 4; i++)
- dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXDATA, &dest[i * 4]);
+ dm->dbg_ops->queue_reg_read(dm, XDMREG_TRAXDATA, &dest[i * 4]);
xtensa_dm_queue_tdi_idle(dm);
- return jtag_execute_queue();
+ return xtensa_dm_queue_execute(dm);
}
int xtensa_dm_perfmon_enable(struct xtensa_debug_module *dm, int counter_id,
(config->kernelcnt << 3);
/* enable performance monitor */
- dm->dbg_ops->queue_reg_write(dm, NARADR_PMG, 0x1);
+ dm->dbg_ops->queue_reg_write(dm, XDMREG_PMG, 0x1);
/* reset counter */
- dm->dbg_ops->queue_reg_write(dm, NARADR_PM0 + counter_id, 0);
- dm->dbg_ops->queue_reg_write(dm, NARADR_PMCTRL0 + counter_id, pmctrl);
- dm->dbg_ops->queue_reg_read(dm, NARADR_PMSTAT0 + counter_id, pmstat_buf);
+ dm->dbg_ops->queue_reg_write(dm, XDMREG_PM0 + counter_id, 0);
+ dm->dbg_ops->queue_reg_write(dm, XDMREG_PMCTRL0 + counter_id, pmctrl);
+ dm->dbg_ops->queue_reg_read(dm, XDMREG_PMSTAT0 + counter_id, pmstat_buf);
xtensa_dm_queue_tdi_idle(dm);
- return jtag_execute_queue();
+ return xtensa_dm_queue_execute(dm);
}
int xtensa_dm_perfmon_dump(struct xtensa_debug_module *dm, int counter_id,
uint8_t pmstat_buf[4];
uint8_t pmcount_buf[4];
- dm->dbg_ops->queue_reg_read(dm, NARADR_PMSTAT0 + counter_id, pmstat_buf);
- dm->dbg_ops->queue_reg_read(dm, NARADR_PM0 + counter_id, pmcount_buf);
+ dm->dbg_ops->queue_reg_read(dm, XDMREG_PMSTAT0 + counter_id, pmstat_buf);
+ dm->dbg_ops->queue_reg_read(dm, XDMREG_PM0 + counter_id, pmcount_buf);
xtensa_dm_queue_tdi_idle(dm);
- int res = jtag_execute_queue();
+ int res = xtensa_dm_queue_execute(dm);
if (res == ERROR_OK) {
uint32_t stat = buf_get_u32(pmstat_buf, 0, 32);
uint64_t result = buf_get_u32(pmcount_buf, 0, 32);