target/xtensa: add NX support
- Manual integration of NX support from xt0.2 release - No new clang static analysis warnings Signed-off-by: Ian Thompson <ianst@cadence.com> Change-Id: I95b51ccc83e56c0d4dbf09e01969ed6a4a93d497 Reviewed-on: https://review.openocd.org/c/openocd/+/7356 Tested-by: jenkins Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
This commit is contained in:
parent
047b1a8fc2
commit
904d58c208
|
@ -165,6 +165,7 @@
|
||||||
#define XT_SR_DDR (xtensa_regs[XT_REG_IDX_DDR].reg_num)
|
#define XT_SR_DDR (xtensa_regs[XT_REG_IDX_DDR].reg_num)
|
||||||
#define XT_SR_PS (xtensa_regs[XT_REG_IDX_PS].reg_num)
|
#define XT_SR_PS (xtensa_regs[XT_REG_IDX_PS].reg_num)
|
||||||
#define XT_SR_WB (xtensa_regs[XT_REG_IDX_WINDOWBASE].reg_num)
|
#define XT_SR_WB (xtensa_regs[XT_REG_IDX_WINDOWBASE].reg_num)
|
||||||
|
#define XT_REG_A0 (xtensa_regs[XT_REG_IDX_AR0].reg_num)
|
||||||
#define XT_REG_A3 (xtensa_regs[XT_REG_IDX_AR3].reg_num)
|
#define XT_REG_A3 (xtensa_regs[XT_REG_IDX_AR3].reg_num)
|
||||||
#define XT_REG_A4 (xtensa_regs[XT_REG_IDX_AR4].reg_num)
|
#define XT_REG_A4 (xtensa_regs[XT_REG_IDX_AR4].reg_num)
|
||||||
|
|
||||||
|
@ -173,6 +174,7 @@
|
||||||
#define XT_EPC_REG_NUM_BASE (0xb0U) /* (EPC1 - 1), for adding DBGLEVEL */
|
#define XT_EPC_REG_NUM_BASE (0xb0U) /* (EPC1 - 1), for adding DBGLEVEL */
|
||||||
#define XT_PC_REG_NUM_VIRTUAL (0xffU) /* Marker for computing PC (EPC[DBGLEVEL) */
|
#define XT_PC_REG_NUM_VIRTUAL (0xffU) /* Marker for computing PC (EPC[DBGLEVEL) */
|
||||||
#define XT_PC_DBREG_NUM_BASE (0x20U) /* External (i.e., GDB) access */
|
#define XT_PC_DBREG_NUM_BASE (0x20U) /* External (i.e., GDB) access */
|
||||||
|
#define XT_NX_IBREAKC_BASE (0xc0U) /* (IBREAKC0..IBREAKC1) for NX */
|
||||||
|
|
||||||
#define XT_SW_BREAKPOINTS_MAX_NUM 32
|
#define XT_SW_BREAKPOINTS_MAX_NUM 32
|
||||||
#define XT_HW_IBREAK_MAX_NUM 2
|
#define XT_HW_IBREAK_MAX_NUM 2
|
||||||
|
@ -476,7 +478,9 @@ static enum xtensa_reg_id xtensa_windowbase_offset_to_canonical(struct xtensa *x
|
||||||
LOG_ERROR("Error: can't convert register %d to non-windowbased register!", reg_idx);
|
LOG_ERROR("Error: can't convert register %d to non-windowbased register!", reg_idx);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return ((idx + windowbase * 4) & (xtensa->core_config->aregs_num - 1)) + XT_REG_IDX_AR0;
|
/* Each windowbase value represents 4 registers on LX and 8 on NX */
|
||||||
|
int base_inc = (xtensa->core_config->core_type == XT_LX) ? 4 : 8;
|
||||||
|
return ((idx + windowbase * base_inc) & (xtensa->core_config->aregs_num - 1)) + XT_REG_IDX_AR0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum xtensa_reg_id xtensa_canonical_to_windowbase_offset(struct xtensa *xtensa,
|
static enum xtensa_reg_id xtensa_canonical_to_windowbase_offset(struct xtensa *xtensa,
|
||||||
|
@ -526,26 +530,29 @@ static int xtensa_queue_pwr_reg_write(struct xtensa *xtensa, unsigned int reg, u
|
||||||
static int xtensa_window_state_save(struct target *target, uint32_t *woe)
|
static int xtensa_window_state_save(struct target *target, uint32_t *woe)
|
||||||
{
|
{
|
||||||
struct xtensa *xtensa = target_to_xtensa(target);
|
struct xtensa *xtensa = target_to_xtensa(target);
|
||||||
int woe_dis;
|
unsigned int woe_sr = (xtensa->core_config->core_type == XT_LX) ? XT_SR_PS : XT_SR_WB;
|
||||||
|
uint32_t woe_dis;
|
||||||
uint8_t woe_buf[4];
|
uint8_t woe_buf[4];
|
||||||
|
|
||||||
if (xtensa->core_config->windowed) {
|
if (xtensa->core_config->windowed) {
|
||||||
/* Save PS (LX) and disable window overflow exceptions prior to AR save */
|
/* Save PS (LX) or WB (NX) and disable window overflow exceptions prior to AR save */
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, XT_SR_PS, XT_REG_A3));
|
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, woe_sr, XT_REG_A3));
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
||||||
xtensa_queue_dbg_reg_read(xtensa, XDMREG_DDR, woe_buf);
|
xtensa_queue_dbg_reg_read(xtensa, XDMREG_DDR, woe_buf);
|
||||||
int res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
|
int res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
|
||||||
if (res != ERROR_OK) {
|
if (res != ERROR_OK) {
|
||||||
LOG_ERROR("Failed to read PS (%d)!", res);
|
LOG_TARGET_ERROR(target, "Failed to read %s (%d)!",
|
||||||
|
(woe_sr == XT_SR_PS) ? "PS" : "WB", res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
xtensa_core_status_check(target);
|
xtensa_core_status_check(target);
|
||||||
*woe = buf_get_u32(woe_buf, 0, 32);
|
*woe = buf_get_u32(woe_buf, 0, 32);
|
||||||
woe_dis = *woe & ~XT_PS_WOE_MSK;
|
woe_dis = *woe & ~((woe_sr == XT_SR_PS) ? XT_PS_WOE_MSK : XT_WB_S_MSK);
|
||||||
LOG_DEBUG("Clearing PS.WOE (0x%08" PRIx32 " -> 0x%08" PRIx32 ")", *woe, woe_dis);
|
LOG_TARGET_DEBUG(target, "Clearing %s (0x%08" PRIx32 " -> 0x%08" PRIx32 ")",
|
||||||
|
(woe_sr == XT_SR_PS) ? "PS.WOE" : "WB.S", *woe, woe_dis);
|
||||||
xtensa_queue_dbg_reg_write(xtensa, XDMREG_DDR, woe_dis);
|
xtensa_queue_dbg_reg_write(xtensa, XDMREG_DDR, woe_dis);
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, XT_SR_PS, XT_REG_A3));
|
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, woe_sr, XT_REG_A3));
|
||||||
}
|
}
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
@ -554,12 +561,14 @@ static int xtensa_window_state_save(struct target *target, uint32_t *woe)
|
||||||
static void xtensa_window_state_restore(struct target *target, uint32_t woe)
|
static void xtensa_window_state_restore(struct target *target, uint32_t woe)
|
||||||
{
|
{
|
||||||
struct xtensa *xtensa = target_to_xtensa(target);
|
struct xtensa *xtensa = target_to_xtensa(target);
|
||||||
|
unsigned int woe_sr = (xtensa->core_config->core_type == XT_LX) ? XT_SR_PS : XT_SR_WB;
|
||||||
if (xtensa->core_config->windowed) {
|
if (xtensa->core_config->windowed) {
|
||||||
/* Restore window overflow exception state */
|
/* Restore window overflow exception state */
|
||||||
xtensa_queue_dbg_reg_write(xtensa, XDMREG_DDR, woe);
|
xtensa_queue_dbg_reg_write(xtensa, XDMREG_DDR, woe);
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, XT_SR_PS, XT_REG_A3));
|
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, woe_sr, XT_REG_A3));
|
||||||
LOG_DEBUG("Restored PS.WOE (0x%08" PRIx32 ")", woe);
|
LOG_TARGET_DEBUG(target, "Restored %s (0x%08" PRIx32 ")",
|
||||||
|
(woe_sr == XT_SR_PS) ? "PS.WOE" : "WB", woe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -596,6 +605,10 @@ static int xtensa_write_dirty_registers(struct target *target)
|
||||||
bool preserve_a3 = false;
|
bool preserve_a3 = false;
|
||||||
uint8_t a3_buf[4];
|
uint8_t a3_buf[4];
|
||||||
xtensa_reg_val_t a3 = 0, woe;
|
xtensa_reg_val_t a3 = 0, woe;
|
||||||
|
unsigned int ms_idx = (xtensa->core_config->core_type == XT_NX) ?
|
||||||
|
xtensa->nx_reg_idx[XT_NX_REG_IDX_MS] : reg_list_size;
|
||||||
|
xtensa_reg_val_t ms;
|
||||||
|
bool restore_ms = false;
|
||||||
|
|
||||||
LOG_TARGET_DEBUG(target, "start");
|
LOG_TARGET_DEBUG(target, "start");
|
||||||
|
|
||||||
|
@ -627,13 +640,25 @@ static int xtensa_write_dirty_registers(struct target *target)
|
||||||
} else if (rlist[ridx].type == XT_REG_FR) {
|
} else if (rlist[ridx].type == XT_REG_FR) {
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_WFR(xtensa, reg_num, XT_REG_A3));
|
xtensa_queue_exec_ins(xtensa, XT_INS_WFR(xtensa, reg_num, XT_REG_A3));
|
||||||
} else {/*SFR */
|
} else {/*SFR */
|
||||||
if (reg_num == XT_PC_REG_NUM_VIRTUAL)
|
if (reg_num == XT_PC_REG_NUM_VIRTUAL) {
|
||||||
/* reg number of PC for debug interrupt depends on NDEBUGLEVEL
|
if (xtensa->core_config->core_type == XT_LX) {
|
||||||
**/
|
/* reg number of PC for debug interrupt depends on NDEBUGLEVEL */
|
||||||
reg_num =
|
reg_num = (XT_EPC_REG_NUM_BASE + xtensa->core_config->debug.irq_level);
|
||||||
(XT_EPC_REG_NUM_BASE +
|
|
||||||
xtensa->core_config->debug.irq_level);
|
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, reg_num, XT_REG_A3));
|
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, reg_num, XT_REG_A3));
|
||||||
|
} else {
|
||||||
|
/* NX PC set through issuing a jump instruction */
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_JX(xtensa, XT_REG_A3));
|
||||||
|
}
|
||||||
|
} else if (i == ms_idx) {
|
||||||
|
/* MS must be restored after ARs. This ensures ARs remain in correct
|
||||||
|
* order even for reversed register groups (overflow/underflow).
|
||||||
|
*/
|
||||||
|
ms = regval;
|
||||||
|
restore_ms = true;
|
||||||
|
LOG_TARGET_DEBUG(target, "Delaying MS write: 0x%x", ms);
|
||||||
|
} else {
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, reg_num, XT_REG_A3));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reg_list[i].dirty = false;
|
reg_list[i].dirty = false;
|
||||||
|
@ -653,7 +678,7 @@ static int xtensa_write_dirty_registers(struct target *target)
|
||||||
reg_list[XT_REG_IDX_CPENABLE].dirty = false;
|
reg_list[XT_REG_IDX_CPENABLE].dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
preserve_a3 = (xtensa->core_config->windowed);
|
preserve_a3 = (xtensa->core_config->windowed) || (xtensa->core_config->core_type == XT_NX);
|
||||||
if (preserve_a3) {
|
if (preserve_a3) {
|
||||||
/* Save (windowed) A3 for scratch use */
|
/* Save (windowed) A3 for scratch use */
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
||||||
|
@ -670,7 +695,12 @@ static int xtensa_write_dirty_registers(struct target *target)
|
||||||
if (res != ERROR_OK)
|
if (res != ERROR_OK)
|
||||||
return res;
|
return res;
|
||||||
/* Grab the windowbase, we need it. */
|
/* Grab the windowbase, we need it. */
|
||||||
windowbase = xtensa_reg_get(target, XT_REG_IDX_WINDOWBASE);
|
uint32_t wb_idx = (xtensa->core_config->core_type == XT_LX) ?
|
||||||
|
XT_REG_IDX_WINDOWBASE : xtensa->nx_reg_idx[XT_NX_REG_IDX_WB];
|
||||||
|
windowbase = xtensa_reg_get(target, wb_idx);
|
||||||
|
if (xtensa->core_config->core_type == XT_NX)
|
||||||
|
windowbase = (windowbase & XT_WB_P_MSK) >> XT_WB_P_SHIFT;
|
||||||
|
|
||||||
/* Check if there are mismatches between the ARx and corresponding Ax registers.
|
/* Check if there are mismatches between the ARx and corresponding Ax registers.
|
||||||
* When the user sets a register on a windowed config, xt-gdb may set the ARx
|
* When the user sets a register on a windowed config, xt-gdb may set the ARx
|
||||||
* register directly. Thus we take ARx as priority over Ax if both are dirty
|
* register directly. Thus we take ARx as priority over Ax if both are dirty
|
||||||
|
@ -748,10 +778,12 @@ static int xtensa_write_dirty_registers(struct target *target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*Now rotate the window so we'll see the next 16 registers. The final rotate
|
|
||||||
* will wraparound, */
|
/* Now rotate the window so we'll see the next 16 registers. The final rotate
|
||||||
/*leaving us in the state we were. */
|
* will wraparound, leaving us in the state we were.
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_ROTW(xtensa, 4));
|
* Each ROTW rotates 4 registers on LX and 8 on NX */
|
||||||
|
int rotw_arg = (xtensa->core_config->core_type == XT_LX) ? 4 : 2;
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_ROTW(xtensa, rotw_arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
xtensa_window_state_restore(target, woe);
|
xtensa_window_state_restore(target, woe);
|
||||||
|
@ -760,6 +792,14 @@ static int xtensa_write_dirty_registers(struct target *target)
|
||||||
xtensa->scratch_ars[s].intval = false;
|
xtensa->scratch_ars[s].intval = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (restore_ms) {
|
||||||
|
uint32_t ms_regno = xtensa->optregs[ms_idx - XT_NUM_REGS].reg_num;
|
||||||
|
xtensa_queue_dbg_reg_write(xtensa, XDMREG_DDR, ms);
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, ms_regno, XT_REG_A3));
|
||||||
|
LOG_TARGET_DEBUG(target, "Delayed MS (0x%x) write complete: 0x%x", ms_regno, ms);
|
||||||
|
}
|
||||||
|
|
||||||
if (preserve_a3) {
|
if (preserve_a3) {
|
||||||
xtensa_queue_dbg_reg_write(xtensa, XDMREG_DDR, a3);
|
xtensa_queue_dbg_reg_write(xtensa, XDMREG_DDR, a3);
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
||||||
|
@ -877,10 +917,41 @@ static inline void xtensa_reg_set_value(struct reg *reg, xtensa_reg_val_t value)
|
||||||
reg->dirty = true;
|
reg->dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int xtensa_imprecise_exception_occurred(struct target *target)
|
||||||
|
{
|
||||||
|
struct xtensa *xtensa = target_to_xtensa(target);
|
||||||
|
for (enum xtensa_nx_reg_idx idx = XT_NX_REG_IDX_IEVEC; idx <= XT_NX_REG_IDX_MESR; idx++) {
|
||||||
|
enum xtensa_reg_id ridx = xtensa->nx_reg_idx[idx];
|
||||||
|
if (xtensa->nx_reg_idx[idx]) {
|
||||||
|
xtensa_reg_val_t reg = xtensa_reg_get(target, xtensa->nx_reg_idx[idx]);
|
||||||
|
if (reg & XT_IMPR_EXC_MSK) {
|
||||||
|
LOG_TARGET_DEBUG(target, "Imprecise exception: %s: 0x%x",
|
||||||
|
xtensa->core_cache->reg_list[ridx].name, reg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xtensa_imprecise_exception_clear(struct target *target)
|
||||||
|
{
|
||||||
|
struct xtensa *xtensa = target_to_xtensa(target);
|
||||||
|
for (enum xtensa_nx_reg_idx idx = XT_NX_REG_IDX_IEVEC; idx <= XT_NX_REG_IDX_MESRCLR; idx++) {
|
||||||
|
enum xtensa_reg_id ridx = xtensa->nx_reg_idx[idx];
|
||||||
|
if (ridx && idx != XT_NX_REG_IDX_MESR) {
|
||||||
|
xtensa_reg_val_t value = (idx == XT_NX_REG_IDX_MESRCLR) ? XT_MESRCLR_IMPR_EXC_MSK : 0;
|
||||||
|
xtensa_reg_set(target, ridx, value);
|
||||||
|
LOG_TARGET_DEBUG(target, "Imprecise exception: clearing %s (0x%x)",
|
||||||
|
xtensa->core_cache->reg_list[ridx].name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int xtensa_core_status_check(struct target *target)
|
int xtensa_core_status_check(struct target *target)
|
||||||
{
|
{
|
||||||
struct xtensa *xtensa = target_to_xtensa(target);
|
struct xtensa *xtensa = target_to_xtensa(target);
|
||||||
int res, needclear = 0;
|
int res, needclear = 0, needimprclear = 0;
|
||||||
|
|
||||||
xtensa_dm_core_status_read(&xtensa->dbg_mod);
|
xtensa_dm_core_status_read(&xtensa->dbg_mod);
|
||||||
xtensa_dsr_t dsr = xtensa_dm_core_status_get(&xtensa->dbg_mod);
|
xtensa_dsr_t dsr = xtensa_dm_core_status_get(&xtensa->dbg_mod);
|
||||||
|
@ -904,11 +975,20 @@ int xtensa_core_status_check(struct target *target)
|
||||||
dsr);
|
dsr);
|
||||||
needclear = 1;
|
needclear = 1;
|
||||||
}
|
}
|
||||||
|
if (xtensa->core_config->core_type == XT_NX && (xtensa_imprecise_exception_occurred(target))) {
|
||||||
|
if (!xtensa->suppress_dsr_errors)
|
||||||
|
LOG_TARGET_ERROR(target,
|
||||||
|
"%s: Imprecise exception occurred!", target_name(target));
|
||||||
|
needclear = 1;
|
||||||
|
needimprclear = 1;
|
||||||
|
}
|
||||||
if (needclear) {
|
if (needclear) {
|
||||||
res = xtensa_dm_core_status_clear(&xtensa->dbg_mod,
|
res = xtensa_dm_core_status_clear(&xtensa->dbg_mod,
|
||||||
OCDDSR_EXECEXCEPTION | OCDDSR_EXECOVERRUN);
|
OCDDSR_EXECEXCEPTION | OCDDSR_EXECOVERRUN);
|
||||||
if (res != ERROR_OK && !xtensa->suppress_dsr_errors)
|
if (res != ERROR_OK && !xtensa->suppress_dsr_errors)
|
||||||
LOG_TARGET_ERROR(target, "clearing DSR failed!");
|
LOG_TARGET_ERROR(target, "clearing DSR failed!");
|
||||||
|
if (xtensa->core_config->core_type == XT_NX && needimprclear)
|
||||||
|
xtensa_imprecise_exception_clear(target);
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
}
|
}
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
|
@ -934,8 +1014,12 @@ void xtensa_reg_set(struct target *target, enum xtensa_reg_id reg_id, xtensa_reg
|
||||||
void xtensa_reg_set_deep_relgen(struct target *target, enum xtensa_reg_id a_idx, xtensa_reg_val_t value)
|
void xtensa_reg_set_deep_relgen(struct target *target, enum xtensa_reg_id a_idx, xtensa_reg_val_t value)
|
||||||
{
|
{
|
||||||
struct xtensa *xtensa = target_to_xtensa(target);
|
struct xtensa *xtensa = target_to_xtensa(target);
|
||||||
|
uint32_t wb_idx = (xtensa->core_config->core_type == XT_LX) ?
|
||||||
|
XT_REG_IDX_WINDOWBASE : xtensa->nx_reg_idx[XT_NX_REG_IDX_WB];
|
||||||
uint32_t windowbase = (xtensa->core_config->windowed ?
|
uint32_t windowbase = (xtensa->core_config->windowed ?
|
||||||
xtensa_reg_get(target, XT_REG_IDX_WINDOWBASE) : 0);
|
xtensa_reg_get(target, wb_idx) : 0);
|
||||||
|
if (xtensa->core_config->core_type == XT_NX)
|
||||||
|
windowbase = (windowbase & XT_WB_P_MSK) >> XT_WB_P_SHIFT;
|
||||||
int ar_idx = xtensa_windowbase_offset_to_canonical(xtensa, a_idx, windowbase);
|
int ar_idx = xtensa_windowbase_offset_to_canonical(xtensa, a_idx, windowbase);
|
||||||
xtensa_reg_set(target, a_idx, value);
|
xtensa_reg_set(target, a_idx, value);
|
||||||
xtensa_reg_set(target, ar_idx, value);
|
xtensa_reg_set(target, ar_idx, value);
|
||||||
|
@ -944,14 +1028,68 @@ void xtensa_reg_set_deep_relgen(struct target *target, enum xtensa_reg_id a_idx,
|
||||||
/* Read cause for entering halted state; return bitmask in DEBUGCAUSE_* format */
|
/* Read cause for entering halted state; return bitmask in DEBUGCAUSE_* format */
|
||||||
uint32_t xtensa_cause_get(struct target *target)
|
uint32_t xtensa_cause_get(struct target *target)
|
||||||
{
|
{
|
||||||
|
struct xtensa *xtensa = target_to_xtensa(target);
|
||||||
|
if (xtensa->core_config->core_type == XT_LX) {
|
||||||
|
/* LX cause in DEBUGCAUSE */
|
||||||
return xtensa_reg_get(target, XT_REG_IDX_DEBUGCAUSE);
|
return xtensa_reg_get(target, XT_REG_IDX_DEBUGCAUSE);
|
||||||
|
}
|
||||||
|
if (xtensa->nx_stop_cause & DEBUGCAUSE_VALID)
|
||||||
|
return xtensa->nx_stop_cause;
|
||||||
|
|
||||||
|
/* NX cause determined from DSR.StopCause */
|
||||||
|
if (xtensa_dm_core_status_read(&xtensa->dbg_mod) != ERROR_OK) {
|
||||||
|
LOG_TARGET_ERROR(target, "Read DSR error");
|
||||||
|
} else {
|
||||||
|
uint32_t dsr = xtensa_dm_core_status_get(&xtensa->dbg_mod);
|
||||||
|
/* NX causes are prioritized; only 1 bit can be set */
|
||||||
|
switch ((dsr & OCDDSR_STOPCAUSE) >> OCDDSR_STOPCAUSE_SHIFT) {
|
||||||
|
case OCDDSR_STOPCAUSE_DI:
|
||||||
|
xtensa->nx_stop_cause = DEBUGCAUSE_DI;
|
||||||
|
break;
|
||||||
|
case OCDDSR_STOPCAUSE_SS:
|
||||||
|
xtensa->nx_stop_cause = DEBUGCAUSE_IC;
|
||||||
|
break;
|
||||||
|
case OCDDSR_STOPCAUSE_IB:
|
||||||
|
xtensa->nx_stop_cause = DEBUGCAUSE_IB;
|
||||||
|
break;
|
||||||
|
case OCDDSR_STOPCAUSE_B:
|
||||||
|
case OCDDSR_STOPCAUSE_B1:
|
||||||
|
xtensa->nx_stop_cause = DEBUGCAUSE_BI;
|
||||||
|
break;
|
||||||
|
case OCDDSR_STOPCAUSE_BN:
|
||||||
|
xtensa->nx_stop_cause = DEBUGCAUSE_BN;
|
||||||
|
break;
|
||||||
|
case OCDDSR_STOPCAUSE_DB0:
|
||||||
|
case OCDDSR_STOPCAUSE_DB1:
|
||||||
|
xtensa->nx_stop_cause = DEBUGCAUSE_DB;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_TARGET_ERROR(target, "Unknown stop cause (DSR: 0x%08x)", dsr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (xtensa->nx_stop_cause)
|
||||||
|
xtensa->nx_stop_cause |= DEBUGCAUSE_VALID;
|
||||||
|
}
|
||||||
|
return xtensa->nx_stop_cause;
|
||||||
}
|
}
|
||||||
|
|
||||||
void xtensa_cause_clear(struct target *target)
|
void xtensa_cause_clear(struct target *target)
|
||||||
{
|
{
|
||||||
struct xtensa *xtensa = target_to_xtensa(target);
|
struct xtensa *xtensa = target_to_xtensa(target);
|
||||||
|
if (xtensa->core_config->core_type == XT_LX) {
|
||||||
xtensa_reg_set(target, XT_REG_IDX_DEBUGCAUSE, 0);
|
xtensa_reg_set(target, XT_REG_IDX_DEBUGCAUSE, 0);
|
||||||
xtensa->core_cache->reg_list[XT_REG_IDX_DEBUGCAUSE].dirty = false;
|
xtensa->core_cache->reg_list[XT_REG_IDX_DEBUGCAUSE].dirty = false;
|
||||||
|
} else {
|
||||||
|
/* NX DSR.STOPCAUSE is not writeable; clear cached copy but leave it valid */
|
||||||
|
xtensa->nx_stop_cause = DEBUGCAUSE_VALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void xtensa_cause_reset(struct target *target)
|
||||||
|
{
|
||||||
|
/* Clear DEBUGCAUSE_VALID to trigger re-read (on NX) */
|
||||||
|
struct xtensa *xtensa = target_to_xtensa(target);
|
||||||
|
xtensa->nx_stop_cause = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int xtensa_assert_reset(struct target *target)
|
int xtensa_assert_reset(struct target *target)
|
||||||
|
@ -1008,9 +1146,11 @@ int xtensa_fetch_all_regs(struct target *target)
|
||||||
struct xtensa *xtensa = target_to_xtensa(target);
|
struct xtensa *xtensa = target_to_xtensa(target);
|
||||||
struct reg *reg_list = xtensa->core_cache->reg_list;
|
struct reg *reg_list = xtensa->core_cache->reg_list;
|
||||||
unsigned int reg_list_size = xtensa->core_cache->num_regs;
|
unsigned int reg_list_size = xtensa->core_cache->num_regs;
|
||||||
xtensa_reg_val_t cpenable = 0, windowbase = 0, a3;
|
xtensa_reg_val_t cpenable = 0, windowbase = 0, a0 = 0, a3;
|
||||||
|
unsigned int ms_idx = reg_list_size;
|
||||||
|
uint32_t ms = 0;
|
||||||
uint32_t woe;
|
uint32_t woe;
|
||||||
uint8_t a3_buf[4];
|
uint8_t a0_buf[4], a3_buf[4], ms_buf[4];
|
||||||
bool debug_dsrs = !xtensa->regs_fetched || LOG_LEVEL_IS(LOG_LVL_DEBUG);
|
bool debug_dsrs = !xtensa->regs_fetched || LOG_LEVEL_IS(LOG_LVL_DEBUG);
|
||||||
|
|
||||||
union xtensa_reg_val_u *regvals = calloc(reg_list_size, sizeof(*regvals));
|
union xtensa_reg_val_u *regvals = calloc(reg_list_size, sizeof(*regvals));
|
||||||
|
@ -1030,6 +1170,25 @@ int xtensa_fetch_all_regs(struct target *target)
|
||||||
/* Save (windowed) A3 so cache matches physical AR3; A3 usable as scratch */
|
/* Save (windowed) A3 so cache matches physical AR3; A3 usable as scratch */
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
||||||
xtensa_queue_dbg_reg_read(xtensa, XDMREG_DDR, a3_buf);
|
xtensa_queue_dbg_reg_read(xtensa, XDMREG_DDR, a3_buf);
|
||||||
|
if (xtensa->core_config->core_type == XT_NX) {
|
||||||
|
/* Save (windowed) A0 as well--it will be required for reading PC */
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, XT_SR_DDR, XT_REG_A0));
|
||||||
|
xtensa_queue_dbg_reg_read(xtensa, XDMREG_DDR, a0_buf);
|
||||||
|
|
||||||
|
/* Set MS.DispSt, clear MS.DE prior to accessing ARs. This ensures ARs remain
|
||||||
|
* in correct order even for reversed register groups (overflow/underflow).
|
||||||
|
*/
|
||||||
|
ms_idx = xtensa->nx_reg_idx[XT_NX_REG_IDX_MS];
|
||||||
|
uint32_t ms_regno = xtensa->optregs[ms_idx - XT_NUM_REGS].reg_num;
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, ms_regno, XT_REG_A3));
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
||||||
|
xtensa_queue_dbg_reg_read(xtensa, XDMREG_DDR, ms_buf);
|
||||||
|
LOG_TARGET_DEBUG(target, "Overriding MS (0x%x): 0x%x", ms_regno, XT_MS_DISPST_DBG);
|
||||||
|
xtensa_queue_dbg_reg_write(xtensa, XDMREG_DDR, XT_MS_DISPST_DBG);
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, ms_regno, XT_REG_A3));
|
||||||
|
}
|
||||||
|
|
||||||
int res = xtensa_window_state_save(target, &woe);
|
int res = xtensa_window_state_save(target, &woe);
|
||||||
if (res != ERROR_OK)
|
if (res != ERROR_OK)
|
||||||
goto xtensa_fetch_all_regs_done;
|
goto xtensa_fetch_all_regs_done;
|
||||||
|
@ -1052,11 +1211,13 @@ int xtensa_fetch_all_regs(struct target *target)
|
||||||
dsrs[XT_REG_IDX_AR0 + i + j].buf);
|
dsrs[XT_REG_IDX_AR0 + i + j].buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (xtensa->core_config->windowed)
|
if (xtensa->core_config->windowed) {
|
||||||
/* Now rotate the window so we'll see the next 16 registers. The final rotate
|
/* Now rotate the window so we'll see the next 16 registers. The final rotate
|
||||||
* will wraparound, */
|
* will wraparound, leaving us in the state we were.
|
||||||
/* leaving us in the state we were. */
|
* Each ROTW rotates 4 registers on LX and 8 on NX */
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_ROTW(xtensa, 4));
|
int rotw_arg = (xtensa->core_config->core_type == XT_LX) ? 4 : 2;
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_ROTW(xtensa, rotw_arg));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
xtensa_window_state_restore(target, woe);
|
xtensa_window_state_restore(target, woe);
|
||||||
|
|
||||||
|
@ -1074,6 +1235,10 @@ int xtensa_fetch_all_regs(struct target *target)
|
||||||
xtensa_core_status_check(target);
|
xtensa_core_status_check(target);
|
||||||
|
|
||||||
a3 = buf_get_u32(a3_buf, 0, 32);
|
a3 = buf_get_u32(a3_buf, 0, 32);
|
||||||
|
if (xtensa->core_config->core_type == XT_NX) {
|
||||||
|
a0 = buf_get_u32(a0_buf, 0, 32);
|
||||||
|
ms = buf_get_u32(ms_buf, 0, 32);
|
||||||
|
}
|
||||||
|
|
||||||
if (xtensa->core_config->coproc) {
|
if (xtensa->core_config->coproc) {
|
||||||
cpenable = buf_get_u32(regvals[XT_REG_IDX_CPENABLE].buf, 0, 32);
|
cpenable = buf_get_u32(regvals[XT_REG_IDX_CPENABLE].buf, 0, 32);
|
||||||
|
@ -1104,17 +1269,30 @@ int xtensa_fetch_all_regs(struct target *target)
|
||||||
break;
|
break;
|
||||||
case XT_REG_SPECIAL:
|
case XT_REG_SPECIAL:
|
||||||
if (reg_num == XT_PC_REG_NUM_VIRTUAL) {
|
if (reg_num == XT_PC_REG_NUM_VIRTUAL) {
|
||||||
|
if (xtensa->core_config->core_type == XT_LX) {
|
||||||
/* reg number of PC for debug interrupt depends on NDEBUGLEVEL */
|
/* reg number of PC for debug interrupt depends on NDEBUGLEVEL */
|
||||||
reg_num = XT_EPC_REG_NUM_BASE + xtensa->core_config->debug.irq_level;
|
reg_num = XT_EPC_REG_NUM_BASE + xtensa->core_config->debug.irq_level;
|
||||||
} else if (reg_num == xtensa_regs[XT_REG_IDX_PS].reg_num) {
|
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, reg_num, XT_REG_A3));
|
||||||
|
} else {
|
||||||
|
/* NX PC read through CALL0(0) and reading A0 */
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_CALL0(xtensa, 0));
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, XT_SR_DDR, XT_REG_A0));
|
||||||
|
xtensa_queue_dbg_reg_read(xtensa, XDMREG_DDR, regvals[i].buf);
|
||||||
|
xtensa_queue_dbg_reg_read(xtensa, XDMREG_DSR, dsrs[i].buf);
|
||||||
|
reg_fetched = false;
|
||||||
|
}
|
||||||
|
} else if ((xtensa->core_config->core_type == XT_LX)
|
||||||
|
&& (reg_num == xtensa_regs[XT_REG_IDX_PS].reg_num)) {
|
||||||
/* reg number of PS for debug interrupt depends on NDEBUGLEVEL */
|
/* reg number of PS for debug interrupt depends on NDEBUGLEVEL */
|
||||||
reg_num = XT_EPS_REG_NUM_BASE + xtensa->core_config->debug.irq_level;
|
reg_num = XT_EPS_REG_NUM_BASE + xtensa->core_config->debug.irq_level;
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, reg_num, XT_REG_A3));
|
||||||
} else if (reg_num == xtensa_regs[XT_REG_IDX_CPENABLE].reg_num) {
|
} else if (reg_num == xtensa_regs[XT_REG_IDX_CPENABLE].reg_num) {
|
||||||
/* CPENABLE already read/updated; don't re-read */
|
/* CPENABLE already read/updated; don't re-read */
|
||||||
reg_fetched = false;
|
reg_fetched = false;
|
||||||
break;
|
break;
|
||||||
}
|
} else {
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, reg_num, XT_REG_A3));
|
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, reg_num, XT_REG_A3));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
reg_fetched = false;
|
reg_fetched = false;
|
||||||
|
@ -1154,9 +1332,15 @@ int xtensa_fetch_all_regs(struct target *target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xtensa->core_config->windowed)
|
if (xtensa->core_config->windowed) {
|
||||||
/* We need the windowbase to decode the general addresses. */
|
/* We need the windowbase to decode the general addresses. */
|
||||||
windowbase = buf_get_u32(regvals[XT_REG_IDX_WINDOWBASE].buf, 0, 32);
|
uint32_t wb_idx = (xtensa->core_config->core_type == XT_LX) ?
|
||||||
|
XT_REG_IDX_WINDOWBASE : xtensa->nx_reg_idx[XT_NX_REG_IDX_WB];
|
||||||
|
windowbase = buf_get_u32(regvals[wb_idx].buf, 0, 32);
|
||||||
|
if (xtensa->core_config->core_type == XT_NX)
|
||||||
|
windowbase = (windowbase & XT_WB_P_MSK) >> XT_WB_P_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
/* Decode the result and update the cache. */
|
/* Decode the result and update the cache. */
|
||||||
for (unsigned int i = 0; i < reg_list_size; i++) {
|
for (unsigned int i = 0; i < reg_list_size; i++) {
|
||||||
struct xtensa_reg_desc *rlist = (i < XT_NUM_REGS) ? xtensa_regs : xtensa->optregs;
|
struct xtensa_reg_desc *rlist = (i < XT_NUM_REGS) ? xtensa_regs : xtensa->optregs;
|
||||||
|
@ -1180,6 +1364,16 @@ int xtensa_fetch_all_regs(struct target *target)
|
||||||
bool is_dirty = (i == XT_REG_IDX_CPENABLE);
|
bool is_dirty = (i == XT_REG_IDX_CPENABLE);
|
||||||
if (xtensa_extra_debug_log)
|
if (xtensa_extra_debug_log)
|
||||||
LOG_INFO("Register %s: 0x%X", reg_list[i].name, regval);
|
LOG_INFO("Register %s: 0x%X", reg_list[i].name, regval);
|
||||||
|
if (rlist[ridx].reg_num == XT_PC_REG_NUM_VIRTUAL &&
|
||||||
|
xtensa->core_config->core_type == XT_NX) {
|
||||||
|
/* A0 from prior CALL0 points to next instruction; decrement it */
|
||||||
|
regval -= 3;
|
||||||
|
is_dirty = 1;
|
||||||
|
} else if (i == ms_idx) {
|
||||||
|
LOG_TARGET_DEBUG(target, "Caching MS: 0x%x", ms);
|
||||||
|
regval = ms;
|
||||||
|
is_dirty = 1;
|
||||||
|
}
|
||||||
xtensa_reg_set(target, i, regval);
|
xtensa_reg_set(target, i, regval);
|
||||||
reg_list[i].dirty = is_dirty; /*always do this _after_ xtensa_reg_set! */
|
reg_list[i].dirty = is_dirty; /*always do this _after_ xtensa_reg_set! */
|
||||||
}
|
}
|
||||||
|
@ -1214,6 +1408,11 @@ int xtensa_fetch_all_regs(struct target *target)
|
||||||
/* We have used A3 (XT_REG_RELGEN) as a scratch register. Restore and flag for write-back. */
|
/* We have used A3 (XT_REG_RELGEN) as a scratch register. Restore and flag for write-back. */
|
||||||
xtensa_reg_set(target, XT_REG_IDX_A3, a3);
|
xtensa_reg_set(target, XT_REG_IDX_A3, a3);
|
||||||
xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A3);
|
xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A3);
|
||||||
|
if (xtensa->core_config->core_type == XT_NX) {
|
||||||
|
xtensa_reg_set(target, XT_REG_IDX_A0, a0);
|
||||||
|
xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A0);
|
||||||
|
}
|
||||||
|
|
||||||
xtensa->regs_fetched = true;
|
xtensa->regs_fetched = true;
|
||||||
xtensa_fetch_all_regs_done:
|
xtensa_fetch_all_regs_done:
|
||||||
free(regvals);
|
free(regvals);
|
||||||
|
@ -1262,7 +1461,7 @@ int xtensa_get_gdb_reg_list(struct target *target,
|
||||||
struct xtensa_reg_desc *rlist = (i < XT_NUM_REGS) ? xtensa_regs : xtensa->optregs;
|
struct xtensa_reg_desc *rlist = (i < XT_NUM_REGS) ? xtensa_regs : xtensa->optregs;
|
||||||
unsigned int ridx = (i < XT_NUM_REGS) ? i : i - XT_NUM_REGS;
|
unsigned int ridx = (i < XT_NUM_REGS) ? i : i - XT_NUM_REGS;
|
||||||
int sparse_idx = rlist[ridx].dbreg_num;
|
int sparse_idx = rlist[ridx].dbreg_num;
|
||||||
if (i == XT_REG_IDX_PS) {
|
if (i == XT_REG_IDX_PS && xtensa->core_config->core_type == XT_LX) {
|
||||||
if (xtensa->eps_dbglevel_idx == 0) {
|
if (xtensa->eps_dbglevel_idx == 0) {
|
||||||
LOG_ERROR("eps_dbglevel_idx not set\n");
|
LOG_ERROR("eps_dbglevel_idx not set\n");
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
@ -1372,9 +1571,12 @@ int xtensa_prepare_resume(struct target *target,
|
||||||
if (xtensa->hw_brps[slot]) {
|
if (xtensa->hw_brps[slot]) {
|
||||||
/* Write IBREAKA[slot] and set bit #slot in IBREAKENABLE */
|
/* Write IBREAKA[slot] and set bit #slot in IBREAKENABLE */
|
||||||
xtensa_reg_set(target, XT_REG_IDX_IBREAKA0 + slot, xtensa->hw_brps[slot]->address);
|
xtensa_reg_set(target, XT_REG_IDX_IBREAKA0 + slot, xtensa->hw_brps[slot]->address);
|
||||||
|
if (xtensa->core_config->core_type == XT_NX)
|
||||||
|
xtensa_reg_set(target, xtensa->nx_reg_idx[XT_NX_REG_IDX_IBREAKC0] + slot, XT_IBREAKC_FB);
|
||||||
bpena |= BIT(slot);
|
bpena |= BIT(slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (xtensa->core_config->core_type == XT_LX)
|
||||||
xtensa_reg_set(target, XT_REG_IDX_IBREAKENABLE, bpena);
|
xtensa_reg_set(target, XT_REG_IDX_IBREAKENABLE, bpena);
|
||||||
|
|
||||||
/* Here we write all registers to the targets */
|
/* Here we write all registers to the targets */
|
||||||
|
@ -1390,6 +1592,7 @@ int xtensa_do_resume(struct target *target)
|
||||||
|
|
||||||
LOG_TARGET_DEBUG(target, "start");
|
LOG_TARGET_DEBUG(target, "start");
|
||||||
|
|
||||||
|
xtensa_cause_reset(target);
|
||||||
xtensa_queue_exec_ins(xtensa, XT_INS_RFDO(xtensa));
|
xtensa_queue_exec_ins(xtensa, XT_INS_RFDO(xtensa));
|
||||||
int res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
|
int res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
|
||||||
if (res != ERROR_OK) {
|
if (res != ERROR_OK) {
|
||||||
|
@ -1467,13 +1670,14 @@ int xtensa_do_step(struct target *target, int current, target_addr_t address, in
|
||||||
return ERROR_TARGET_NOT_HALTED;
|
return ERROR_TARGET_NOT_HALTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xtensa->eps_dbglevel_idx == 0) {
|
if (xtensa->eps_dbglevel_idx == 0 && xtensa->core_config->core_type == XT_LX) {
|
||||||
LOG_ERROR("eps_dbglevel_idx not set\n");
|
LOG_TARGET_ERROR(target, "eps_dbglevel_idx not set\n");
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Save old ps (EPS[dbglvl] on LX), pc */
|
/* Save old ps (EPS[dbglvl] on LX), pc */
|
||||||
oldps = xtensa_reg_get(target, xtensa->eps_dbglevel_idx);
|
oldps = xtensa_reg_get(target, (xtensa->core_config->core_type == XT_LX) ?
|
||||||
|
xtensa->eps_dbglevel_idx : XT_REG_IDX_PS);
|
||||||
oldpc = xtensa_reg_get(target, XT_REG_IDX_PC);
|
oldpc = xtensa_reg_get(target, XT_REG_IDX_PC);
|
||||||
|
|
||||||
cause = xtensa_cause_get(target);
|
cause = xtensa_cause_get(target);
|
||||||
|
@ -1542,7 +1746,7 @@ int xtensa_do_step(struct target *target, int current, target_addr_t address, in
|
||||||
if (!handle_breakpoints && (cause & (DEBUGCAUSE_BI | DEBUGCAUSE_BN)))
|
if (!handle_breakpoints && (cause & (DEBUGCAUSE_BI | DEBUGCAUSE_BN)))
|
||||||
/* handle normal SW breakpoint */
|
/* handle normal SW breakpoint */
|
||||||
xtensa_cause_clear(target); /* so we don't recurse into the same routine */
|
xtensa_cause_clear(target); /* so we don't recurse into the same routine */
|
||||||
if ((oldps & 0xf) >= icountlvl) {
|
if (xtensa->core_config->core_type == XT_LX && ((oldps & 0xf) >= icountlvl)) {
|
||||||
/* Lower interrupt level to allow stepping, but flag eps[dbglvl] to be restored */
|
/* Lower interrupt level to allow stepping, but flag eps[dbglvl] to be restored */
|
||||||
ps_lowered = true;
|
ps_lowered = true;
|
||||||
uint32_t newps = (oldps & ~0xf) | (icountlvl - 1);
|
uint32_t newps = (oldps & ~0xf) | (icountlvl - 1);
|
||||||
|
@ -1554,10 +1758,16 @@ int xtensa_do_step(struct target *target, int current, target_addr_t address, in
|
||||||
oldps);
|
oldps);
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
if (xtensa->core_config->core_type == XT_LX) {
|
||||||
xtensa_reg_set(target, XT_REG_IDX_ICOUNTLEVEL, icountlvl);
|
xtensa_reg_set(target, XT_REG_IDX_ICOUNTLEVEL, icountlvl);
|
||||||
xtensa_reg_set(target, XT_REG_IDX_ICOUNT, icount_val);
|
xtensa_reg_set(target, XT_REG_IDX_ICOUNT, icount_val);
|
||||||
|
} else {
|
||||||
|
xtensa_queue_dbg_reg_write(xtensa, XDMREG_DCRSET, OCDDCR_STEPREQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
/* Now ICOUNT is set, we can resume as if we were going to run */
|
/* Now that ICOUNT (LX) or DCR.StepRequest (NX) is set,
|
||||||
|
* we can resume as if we were going to run
|
||||||
|
*/
|
||||||
res = xtensa_prepare_resume(target, current, address, 0, 0);
|
res = xtensa_prepare_resume(target, current, address, 0, 0);
|
||||||
if (res != ERROR_OK) {
|
if (res != ERROR_OK) {
|
||||||
LOG_TARGET_ERROR(target, "Failed to prepare resume for single step");
|
LOG_TARGET_ERROR(target, "Failed to prepare resume for single step");
|
||||||
|
@ -2108,6 +2318,22 @@ int xtensa_poll(struct target *target)
|
||||||
OCDDSR_DEBUGPENDBREAK | OCDDSR_DEBUGINTBREAK | OCDDSR_DEBUGPENDTRAX |
|
OCDDSR_DEBUGPENDBREAK | OCDDSR_DEBUGINTBREAK | OCDDSR_DEBUGPENDTRAX |
|
||||||
OCDDSR_DEBUGINTTRAX |
|
OCDDSR_DEBUGINTTRAX |
|
||||||
OCDDSR_DEBUGPENDHOST | OCDDSR_DEBUGINTHOST);
|
OCDDSR_DEBUGPENDHOST | OCDDSR_DEBUGINTHOST);
|
||||||
|
if (xtensa->core_config->core_type == XT_NX) {
|
||||||
|
/* Enable imprecise exceptions while in halted state */
|
||||||
|
xtensa_reg_val_t ps = xtensa_reg_get(target, XT_REG_IDX_PS);
|
||||||
|
xtensa_reg_val_t newps = ps & ~(XT_PS_DIEXC_MSK);
|
||||||
|
xtensa_mark_register_dirty(xtensa, XT_REG_IDX_PS);
|
||||||
|
LOG_TARGET_DEBUG(target, "Enabling PS.DIEXC: 0x%08x -> 0x%08x", ps, newps);
|
||||||
|
xtensa_queue_dbg_reg_write(xtensa, XDMREG_DDR, newps);
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, XT_SR_DDR, XT_REG_A3));
|
||||||
|
xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, XT_SR_PS, XT_REG_A3));
|
||||||
|
res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
|
||||||
|
if (res != ERROR_OK) {
|
||||||
|
LOG_TARGET_ERROR(target, "Failed to write PS.DIEXC (%d)!", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
xtensa_core_status_check(target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
target->debug_reason = DBG_REASON_NOTHALTED;
|
target->debug_reason = DBG_REASON_NOTHALTED;
|
||||||
|
@ -2326,6 +2552,8 @@ int xtensa_breakpoint_remove(struct target *target, struct breakpoint *breakpoin
|
||||||
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||||
}
|
}
|
||||||
xtensa->hw_brps[slot] = NULL;
|
xtensa->hw_brps[slot] = NULL;
|
||||||
|
if (xtensa->core_config->core_type == XT_NX)
|
||||||
|
xtensa_reg_set(target, xtensa->nx_reg_idx[XT_NX_REG_IDX_IBREAKC0] + slot, 0);
|
||||||
LOG_TARGET_DEBUG(target, "cleared HW breakpoint %u @ " TARGET_ADDR_FMT, slot, breakpoint->address);
|
LOG_TARGET_DEBUG(target, "cleared HW breakpoint %u @ " TARGET_ADDR_FMT, slot, breakpoint->address);
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
@ -3073,8 +3301,10 @@ COMMAND_HELPER(xtensa_cmd_xtdef_do, struct xtensa *xtensa)
|
||||||
const char *core_name = CMD_ARGV[0];
|
const char *core_name = CMD_ARGV[0];
|
||||||
if (strcasecmp(core_name, "LX") == 0) {
|
if (strcasecmp(core_name, "LX") == 0) {
|
||||||
xtensa->core_config->core_type = XT_LX;
|
xtensa->core_config->core_type = XT_LX;
|
||||||
|
} else if (strcasecmp(core_name, "NX") == 0) {
|
||||||
|
xtensa->core_config->core_type = XT_NX;
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR("xtdef [LX]\n");
|
LOG_ERROR("xtdef [LX|NX]\n");
|
||||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
}
|
}
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
|
@ -3456,6 +3686,33 @@ COMMAND_HELPER(xtensa_cmd_xtreg_do, struct xtensa *xtensa)
|
||||||
xtensa->eps_dbglevel_idx = XT_NUM_REGS + xtensa->num_optregs - 1;
|
xtensa->eps_dbglevel_idx = XT_NUM_REGS + xtensa->num_optregs - 1;
|
||||||
LOG_DEBUG("Setting PS (%s) index to %d", rptr->name, xtensa->eps_dbglevel_idx);
|
LOG_DEBUG("Setting PS (%s) index to %d", rptr->name, xtensa->eps_dbglevel_idx);
|
||||||
}
|
}
|
||||||
|
if (xtensa->core_config->core_type == XT_NX) {
|
||||||
|
enum xtensa_nx_reg_idx idx = XT_NX_REG_IDX_NUM;
|
||||||
|
if (strcmp(rptr->name, "ibreakc0") == 0)
|
||||||
|
idx = XT_NX_REG_IDX_IBREAKC0;
|
||||||
|
else if (strcmp(rptr->name, "wb") == 0)
|
||||||
|
idx = XT_NX_REG_IDX_WB;
|
||||||
|
else if (strcmp(rptr->name, "ms") == 0)
|
||||||
|
idx = XT_NX_REG_IDX_MS;
|
||||||
|
else if (strcmp(rptr->name, "ievec") == 0)
|
||||||
|
idx = XT_NX_REG_IDX_IEVEC;
|
||||||
|
else if (strcmp(rptr->name, "ieextern") == 0)
|
||||||
|
idx = XT_NX_REG_IDX_IEEXTERN;
|
||||||
|
else if (strcmp(rptr->name, "mesr") == 0)
|
||||||
|
idx = XT_NX_REG_IDX_MESR;
|
||||||
|
else if (strcmp(rptr->name, "mesrclr") == 0)
|
||||||
|
idx = XT_NX_REG_IDX_MESRCLR;
|
||||||
|
if (idx < XT_NX_REG_IDX_NUM) {
|
||||||
|
if (xtensa->nx_reg_idx[idx] != 0) {
|
||||||
|
LOG_ERROR("nx_reg_idx[%d] previously set to %d",
|
||||||
|
idx, xtensa->nx_reg_idx[idx]);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
xtensa->nx_reg_idx[idx] = XT_NUM_REGS + xtensa->num_optregs - 1;
|
||||||
|
LOG_DEBUG("NX reg %s: index %d (%d)",
|
||||||
|
rptr->name, xtensa->nx_reg_idx[idx], idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (strcmp(rptr->name, "cpenable") == 0) {
|
} else if (strcmp(rptr->name, "cpenable") == 0) {
|
||||||
xtensa->core_config->coproc = true;
|
xtensa->core_config->coproc = true;
|
||||||
}
|
}
|
||||||
|
@ -3640,6 +3897,12 @@ COMMAND_HELPER(xtensa_cmd_mask_interrupts_do, struct xtensa *xtensa)
|
||||||
command_print(CMD, "Current ISR step mode: %s", st);
|
command_print(CMD, "Current ISR step mode: %s", st);
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (xtensa->core_config->core_type == XT_NX) {
|
||||||
|
command_print(CMD, "ERROR: ISR step mode only supported on Xtensa LX");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Masking is ON -> interrupts during stepping are OFF, and vice versa */
|
/* Masking is ON -> interrupts during stepping are OFF, and vice versa */
|
||||||
if (!strcasecmp(CMD_ARGV[0], "off"))
|
if (!strcasecmp(CMD_ARGV[0], "off"))
|
||||||
state = XT_STEPPING_ISR_ON;
|
state = XT_STEPPING_ISR_ON;
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#define XT_ISNS_SZ_MAX 3
|
#define XT_ISNS_SZ_MAX 3
|
||||||
|
|
||||||
|
/* PS register bits (LX) */
|
||||||
#define XT_PS_RING(_v_) ((uint32_t)((_v_) & 0x3) << 6)
|
#define XT_PS_RING(_v_) ((uint32_t)((_v_) & 0x3) << 6)
|
||||||
#define XT_PS_RING_MSK (0x3 << 6)
|
#define XT_PS_RING_MSK (0x3 << 6)
|
||||||
#define XT_PS_RING_GET(_v_) (((_v_) >> 6) & 0x3)
|
#define XT_PS_RING_GET(_v_) (((_v_) >> 6) & 0x3)
|
||||||
|
@ -42,6 +43,31 @@
|
||||||
#define XT_PS_OWB_MSK (0xF << 8)
|
#define XT_PS_OWB_MSK (0xF << 8)
|
||||||
#define XT_PS_WOE_MSK BIT(18)
|
#define XT_PS_WOE_MSK BIT(18)
|
||||||
|
|
||||||
|
/* PS register bits (NX) */
|
||||||
|
#define XT_PS_DIEXC_MSK BIT(2)
|
||||||
|
|
||||||
|
/* MS register bits (NX) */
|
||||||
|
#define XT_MS_DE_MSK BIT(5)
|
||||||
|
#define XT_MS_DISPST_MSK (0x1f)
|
||||||
|
#define XT_MS_DISPST_DBG (0x10)
|
||||||
|
|
||||||
|
/* WB register bits (NX) */
|
||||||
|
#define XT_WB_P_SHIFT (0)
|
||||||
|
#define XT_WB_P_MSK (0x7U << XT_WB_P_SHIFT)
|
||||||
|
#define XT_WB_C_SHIFT (4)
|
||||||
|
#define XT_WB_C_MSK (0x7U << XT_WB_C_SHIFT)
|
||||||
|
#define XT_WB_N_SHIFT (8)
|
||||||
|
#define XT_WB_N_MSK (0x7U << XT_WB_N_SHIFT)
|
||||||
|
#define XT_WB_S_SHIFT (30)
|
||||||
|
#define XT_WB_S_MSK (0x3U << XT_WB_S_SHIFT)
|
||||||
|
|
||||||
|
/* IBREAKC register bits (NX) */
|
||||||
|
#define XT_IBREAKC_FB (0x80000000)
|
||||||
|
|
||||||
|
/* Definitions for imprecise exception registers (NX) */
|
||||||
|
#define XT_IMPR_EXC_MSK (0x00000013)
|
||||||
|
#define XT_MESRCLR_IMPR_EXC_MSK (0x00000090)
|
||||||
|
|
||||||
#define XT_LOCAL_MEM_REGIONS_NUM_MAX 8
|
#define XT_LOCAL_MEM_REGIONS_NUM_MAX 8
|
||||||
|
|
||||||
#define XT_AREGS_NUM_MAX 64
|
#define XT_AREGS_NUM_MAX 64
|
||||||
|
@ -79,6 +105,7 @@ struct xtensa_keyval_info_s {
|
||||||
enum xtensa_type {
|
enum xtensa_type {
|
||||||
XT_UNDEF = 0,
|
XT_UNDEF = 0,
|
||||||
XT_LX,
|
XT_LX,
|
||||||
|
XT_NX,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct xtensa_cache_config {
|
struct xtensa_cache_config {
|
||||||
|
@ -167,6 +194,17 @@ enum xtensa_stepping_isr_mode {
|
||||||
XT_STEPPING_ISR_ON, /* interrupts are enabled during stepping */
|
XT_STEPPING_ISR_ON, /* interrupts are enabled during stepping */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum xtensa_nx_reg_idx {
|
||||||
|
XT_NX_REG_IDX_IBREAKC0 = 0,
|
||||||
|
XT_NX_REG_IDX_WB,
|
||||||
|
XT_NX_REG_IDX_MS,
|
||||||
|
XT_NX_REG_IDX_IEVEC, /* IEVEC, IEEXTERN, and MESR must be contiguous */
|
||||||
|
XT_NX_REG_IDX_IEEXTERN,
|
||||||
|
XT_NX_REG_IDX_MESR,
|
||||||
|
XT_NX_REG_IDX_MESRCLR,
|
||||||
|
XT_NX_REG_IDX_NUM
|
||||||
|
};
|
||||||
|
|
||||||
/* Only supported in cores with in-CPU MMU. None of Espressif chips as of now. */
|
/* Only supported in cores with in-CPU MMU. None of Espressif chips as of now. */
|
||||||
enum xtensa_mode {
|
enum xtensa_mode {
|
||||||
XT_MODE_RING0,
|
XT_MODE_RING0,
|
||||||
|
@ -232,6 +270,8 @@ struct xtensa {
|
||||||
uint8_t come_online_probes_num;
|
uint8_t come_online_probes_num;
|
||||||
bool proc_syscall;
|
bool proc_syscall;
|
||||||
bool halt_request;
|
bool halt_request;
|
||||||
|
uint32_t nx_stop_cause;
|
||||||
|
uint32_t nx_reg_idx[XT_NX_REG_IDX_NUM];
|
||||||
struct xtensa_keyval_info_s scratch_ars[XT_AR_SCRATCH_NUM];
|
struct xtensa_keyval_info_s scratch_ars[XT_AR_SCRATCH_NUM];
|
||||||
bool regs_fetched; /* true after first register fetch completed successfully */
|
bool regs_fetched; /* true after first register fetch completed successfully */
|
||||||
};
|
};
|
||||||
|
|
|
@ -246,6 +246,7 @@ struct xtensa_dm_reg_offsets {
|
||||||
#define OCDDCR_ENABLEOCD BIT(0)
|
#define OCDDCR_ENABLEOCD BIT(0)
|
||||||
#define OCDDCR_DEBUGINTERRUPT BIT(1)
|
#define OCDDCR_DEBUGINTERRUPT BIT(1)
|
||||||
#define OCDDCR_INTERRUPTALLCONDS BIT(2)
|
#define OCDDCR_INTERRUPTALLCONDS BIT(2)
|
||||||
|
#define OCDDCR_STEPREQUEST BIT(3) /* NX only */
|
||||||
#define OCDDCR_BREAKINEN BIT(16)
|
#define OCDDCR_BREAKINEN BIT(16)
|
||||||
#define OCDDCR_BREAKOUTEN BIT(17)
|
#define OCDDCR_BREAKOUTEN BIT(17)
|
||||||
#define OCDDCR_DEBUGSWACTIVE BIT(20)
|
#define OCDDCR_DEBUGSWACTIVE BIT(20)
|
||||||
|
@ -259,6 +260,8 @@ struct xtensa_dm_reg_offsets {
|
||||||
#define OCDDSR_EXECBUSY BIT(2)
|
#define OCDDSR_EXECBUSY BIT(2)
|
||||||
#define OCDDSR_EXECOVERRUN BIT(3)
|
#define OCDDSR_EXECOVERRUN BIT(3)
|
||||||
#define OCDDSR_STOPPED BIT(4)
|
#define OCDDSR_STOPPED BIT(4)
|
||||||
|
#define OCDDSR_STOPCAUSE (0xF << 5) /* NX only */
|
||||||
|
#define OCDDSR_STOPCAUSE_SHIFT (5) /* NX only */
|
||||||
#define OCDDSR_COREWROTEDDR BIT(10)
|
#define OCDDSR_COREWROTEDDR BIT(10)
|
||||||
#define OCDDSR_COREREADDDR BIT(11)
|
#define OCDDSR_COREREADDDR BIT(11)
|
||||||
#define OCDDSR_HOSTWROTEDDR BIT(14)
|
#define OCDDSR_HOSTWROTEDDR BIT(14)
|
||||||
|
@ -275,12 +278,24 @@ struct xtensa_dm_reg_offsets {
|
||||||
#define OCDDSR_BREAKINITI BIT(26)
|
#define OCDDSR_BREAKINITI BIT(26)
|
||||||
#define OCDDSR_DBGMODPOWERON BIT(31)
|
#define OCDDSR_DBGMODPOWERON BIT(31)
|
||||||
|
|
||||||
|
/* NX stop cause */
|
||||||
|
#define OCDDSR_STOPCAUSE_DI (0) /* Debug Interrupt */
|
||||||
|
#define OCDDSR_STOPCAUSE_SS (1) /* Single-step completed */
|
||||||
|
#define OCDDSR_STOPCAUSE_IB (2) /* HW breakpoint (IBREAKn match) */
|
||||||
|
#define OCDDSR_STOPCAUSE_B1 (4) /* SW breakpoint (BREAK.1 instruction) */
|
||||||
|
#define OCDDSR_STOPCAUSE_BN (5) /* SW breakpoint (BREAK.N instruction) */
|
||||||
|
#define OCDDSR_STOPCAUSE_B (6) /* SW breakpoint (BREAK instruction) */
|
||||||
|
#define OCDDSR_STOPCAUSE_DB0 (8) /* HW watchpoint (DBREAK0 match) */
|
||||||
|
#define OCDDSR_STOPCAUSE_DB1 (9) /* HW watchpoint (DBREAK0 match) */
|
||||||
|
|
||||||
|
/* LX stop cause */
|
||||||
#define DEBUGCAUSE_IC BIT(0) /* ICOUNT exception */
|
#define DEBUGCAUSE_IC BIT(0) /* ICOUNT exception */
|
||||||
#define DEBUGCAUSE_IB BIT(1) /* IBREAK exception */
|
#define DEBUGCAUSE_IB BIT(1) /* IBREAK exception */
|
||||||
#define DEBUGCAUSE_DB BIT(2) /* DBREAK exception */
|
#define DEBUGCAUSE_DB BIT(2) /* DBREAK exception */
|
||||||
#define DEBUGCAUSE_BI BIT(3) /* BREAK instruction encountered */
|
#define DEBUGCAUSE_BI BIT(3) /* BREAK instruction encountered */
|
||||||
#define DEBUGCAUSE_BN BIT(4) /* BREAK.N instruction encountered */
|
#define DEBUGCAUSE_BN BIT(4) /* BREAK.N instruction encountered */
|
||||||
#define DEBUGCAUSE_DI BIT(5) /* Debug Interrupt */
|
#define DEBUGCAUSE_DI BIT(5) /* Debug Interrupt */
|
||||||
|
#define DEBUGCAUSE_VALID BIT(31) /* Pseudo-value to trigger reread (NX only) */
|
||||||
|
|
||||||
#define TRAXCTRL_TREN BIT(0) /* Trace enable. Tracing starts on 0->1 */
|
#define TRAXCTRL_TREN BIT(0) /* Trace enable. Tracing starts on 0->1 */
|
||||||
#define TRAXCTRL_TRSTP BIT(1) /* Trace Stop. Make 1 to stop trace. */
|
#define TRAXCTRL_TRSTP BIT(1) /* Trace Stop. Make 1 to stop trace. */
|
||||||
|
|
Loading…
Reference in New Issue